eda: Simpler approach to Id row caching by storing row directly inside the Table...
[sdk] / eda / libeda / src / idList.ec
1 #ifdef ECERE_STATIC
2 public import static "ecere"
3 #else
4 public import "ecere"
5 #endif
6
7 import "EDA.ec"
8 import "gui"
9
10 default:
11 extern int __ecereVMethodID_class_OnGetString;
12 extern int __ecereVMethodID_class_OnCompare;
13 extern int __ecereVMethodID_class_OnUnserialize;
14 extern int __ecereVMethodID_class_OnSerialize;
15 extern int __ecereVMethodID_class_OnFree;
16
17 static void UnusedFunction()
18 {
19    int a;
20    a.OnGetString(0,0,0);
21    a.OnFree();
22    a.OnCopy(null);
23    a.OnCompare(null);
24    a.OnSaveEdit(null,0);
25    a.OnEdit(null,null,0,0,0,0,0);
26    a.OnDisplay(null,0,0,0,0,0,0);
27    a.OnGetDataFromString(null);
28    a.OnUnserialize(null);
29    a.OnSerialize(null);
30 }
31 private:
32
33 public class Id : uint
34 {
35    class_data Table * table;     class_property Table * table     { set { class_data(table) = value; } get { return class_data(table); } };
36    //class_data Field * idField; class_property Field * idField { set { class_data(nameField) = value; } get { return class_data(idField); } };
37    //class_data Field * displayField; class_property Field * displayField { set { class_data(displayField) = value; } get { return class_data(displayField); } };
38    class_data Field * nameField; class_property Field * nameField { set { class_data(nameField) = value; } get { return class_data(nameField); } };
39    class_data char * addText;   class_property char * addText  { set { class_data(addText) = value; } };
40    class_data void * Refill;    class_property void * Refill   { set { class_data(Refill) = value; } };
41
42    Window OnEdit(DataBox dataBox, void * obsolete, int x, int y, int w, int h, void * userData)
43    {
44       if(this || !this) {     // FIXME
45          TableDropBox dropBox = dataBox.keepEditor ? (TableDropBox)dataBox.editor /*obsolete*/ : null;
46          if(!dropBox)
47          {
48 /*
49             if(eClass_IsDerived(dataBox._class, class(FieldDropBox)))
50             {
51                FieldDropBox fieldDropBox = (FieldDropBox)dataBox;
52
53             }
54             else if(eClass_IsDerived(dataBox._class, class(FieldBox)))
55             {
56                FieldBox fieldBox = (FieldBox)dataBox;
57
58             }
59 */
60
61             dropBox = TableDropBox
62             {
63                dataBox, borderStyle = 0, anchor = { 0, 0, 0, 0 }, 
64                modifyVirtualArea = false, activeStipple = false;
65                showNone = true;
66                nameField = *class_data(nameField);
67                table = *class_data(table);
68
69                bool DataBox::NotifySelect(DropBox control, DataRow row, Modifiers mods)
70                {
71                   uint id = row ? row.tag : 0;
72                   SetData(&id, mods.closingDropDown);
73                   return true;
74                }
75
76                bool DataBox::NotifyTextEntry(DropBox _dropBox, char * string, bool confirmed)
77                {
78                   TableDropBox dropBox = (TableDropBox)_dropBox;
79                   //Table tbl = dropBox.table.db.OpenTable(dropBox.table.name, { tableRows });
80                   //if(tbl)
81                   {
82                      /*FieldIndex indexedFields[1];
83                      Row r { };*/
84                      char * trimmed = new char[strlen(string) + 1];
85                      /*indexedFields[0] = { dropBox.nameField };
86                      tbl.GenerateIndex(1, indexedFields, false);
87                      r.tbl = tbl;*/
88                      DataRow row = null;
89
90                      TrimLSpaces(string, trimmed);
91                      TrimRSpaces(trimmed, trimmed);
92
93                      /*if(r.Find(dropBox.nameField, middle, nil, trimmed))
94                      {
95                         if(dropBox.filterField)
96                         {
97                            // TODO: Improve this... Multi field?
98                            while(true)
99                            {
100                               DataRow row;
101                               Id id = 0;
102                               Field fldId = dropBox.table.FindField(defaultIdField);
103                               r.GetData(fldId, id);
104                               row = dropBox.FindSubRow(id);
105                               if(row)
106                               {
107                                  dropBox.SelectRow(row);
108                                  break;
109                               }
110                               //if(!r.Find(dropBox.nameField, next, nil, trimmed))
111                               if(!r.Next())
112                                  break;
113                            }
114                         }
115                         else
116                         {
117                            Id id = 0;
118                            Field fldId = dropBox.table.FindField(defaultIdField);
119                            r.GetData(fldId, id);
120                            dropBox.SelectRow(dropBox.FindSubRow(id));
121                         }
122                      }
123                      */
124
125                      {
126                         for(row = dropBox.firstRow; row; row = row.next)
127                         {
128                            char * string = row.string;
129                            if(string && !strcmp(trimmed, string))
130                               break;
131                         }
132                      }
133                      if(row)
134                      {
135                         dropBox.SelectRow(row);
136                      }
137                      else
138                      {
139                         dropBox.changeContents = false;
140                         dropBox.contents = trimmed;
141                         dropBox.SelectRow(null);
142                         dropBox.changeContents = true;
143                      }
144                      //delete r;
145                   }
146                   return true;
147                }
148             };
149             if(class_data(Refill))
150                dropBox.Refill = class_data(Refill);
151
152             // dropBox.Refill();
153          }
154          dataBox.OnConfigure(dropBox);
155          dropBox.Create();
156          dropBox.currentRow = dropBox.FindSubRow(this);
157          if(!dropBox.currentRow && this)
158             dataBox.SetData((uint *)&this, false);
159          {
160             DataRow r = dropBox.currentRow;
161             if(r)
162                for(r = r.parent; r; r = r.parent)
163                   r.collapsed = false;
164          }
165          return dropBox;
166       }
167       return null;
168    }
169
170    char * OnGetString(char * tempString, void * fieldData, bool * needClass)
171    {
172       if(&this)
173       {
174          // FIXME
175          Table tbl = *class_data(table);
176          Field idField = tbl.FindField(defaultIdField);
177          Row r;
178          idRowCacheMutex.Wait();
179          if(!tbl.cachedIdRow)
180          {
181             tbl.cachedIdRow = Row { tbl };
182             incref tbl.cachedIdRow;
183          }
184          r = tbl.cachedIdRow;
185
186          if(this)
187          {
188             if(r.Find(idField, middle, nil, this))
189             {
190                String name = null;
191                Field * nameField = class_data(nameField);
192                if(nameField)
193                {
194 #ifdef _DEBUG
195                   char * fn = nameField->name;
196 #endif
197                   // Get name data from row
198                   int64 data = 0;
199                   Class type = nameField->type;
200                   if(type.type == unitClass && !type.typeSize)
201                   {
202                      Class dataType = eSystem_FindClass(type.module, type.dataTypeString);
203                      if(dataType)
204                         type = dataType;
205                   }
206                   if(type.type == structClass)
207                      data = (int64)new0 byte[type.structSize];
208                   ((bool (*)())(void *)r.GetData)(r, *nameField, type, (type.type == structClass) ? (void *)data : &data);
209
210                   if(type.type == systemClass || type.type == unitClass || type.type == bitClass || type.type == enumClass)
211                      name = (String)type._vTbl[__ecereVMethodID_class_OnGetString](type, (void *)&data, tempString, null, null);
212                   else
213                      name = (String)type._vTbl[__ecereVMethodID_class_OnGetString](type, (void *)data, tempString, null, null);
214
215                   strcpy(tempString, name ? name : "");
216                   if(!(type.type == systemClass || type.type == unitClass || type.type == bitClass || type.type == enumClass))
217                      type._vTbl[__ecereVMethodID_class_OnFree](type, data);
218                }
219                else
220                {
221                   PrintLn("Id::OnGetString -- data type"/*, this._class.name, */" has no class_data(nameField)");
222                }
223             }
224          }
225          else
226          {
227             sprintf(tempString, $"(Click to add a new %s...)", $"item"/*class_data(addText)*/);
228          }
229          // delete r;
230          idRowCacheMutex.Release();
231       }
232       return tempString;
233    }
234 }
235
236 public class IdList
237 {
238 public:
239    int count;
240    Id * ids;
241    class_data Class type;
242    class_property Class type
243    {
244       set {  class_data(type) = value; }
245       get { return class_data(type); }
246    };
247
248    public void Clear()
249    {
250       if(this)
251       {
252          delete ids;
253          count = 0;
254       }
255    }
256    
257    public bool Includes(Id id)
258    {
259       if(this)
260       {
261          int c;
262          for(c = 0; c < count; c++)
263             if(ids[c] == id) 
264                return true;
265       }
266       return false;
267    }
268
269    public bool Add(Id id)
270    {
271       int c;
272       for(c = 0; c < count; c++)
273          if(ids[c] == id) break;
274       if(c == count)
275       {
276          ids = renew ids Id[count + 1];
277          ids[count] = id;
278          count++;
279          return true;
280       }
281       return false;
282    }
283
284    public bool Delete(Id id)
285    {
286       int c;
287       for(c = 0; c < count; c++)
288          if(ids[c] == id)
289          {
290             if(c < count - 1)
291                memcpy(ids + c, ids + c + 1, (count - 1 - c) * sizeof(Id));
292             ids = renew ids Id[count - 1];
293             count--;
294             return true;
295          }
296       return false;
297    }
298
299    void OnUnserialize(IOChannel channel)
300    {
301       int c, count;
302
303       this = null;
304
305       channel.Unserialize(count);
306       if(count != MAXDWORD)
307       {
308          IdList idList = eInstance_New(_class); //IdList { };
309          idList.count = count;
310          idList.ids = new Id[count];
311          for(c = 0; c < count; c++)
312          {
313             Id id;
314             channel.Unserialize(id);
315             //Add(id);
316             idList.ids[c] = id;
317          }
318          this = idList;
319       }
320    }
321
322    void OnSerialize(IOChannel channel)
323    {
324       if(this)
325       {
326          int c;
327          channel.Serialize(count);
328          for(c = 0; c < count; c++)
329             channel.Serialize(ids[c]);
330       }
331       else
332       {
333          Id none = MAXDWORD;
334          channel.Serialize(none);
335       }
336    }
337
338    void OnDisplay(Surface surface, int x, int y, int width, void * fieldData, Alignment alignment, DataDisplayFlags displayFlags)
339    {
340
341    }
342    
343    char * OnGetString(char * stringOutput, void * fieldData, bool * needClass)
344    {
345       stringOutput[0] = 0;
346       if(this)
347       {
348          int c;
349          for(c = 0; c<count; c++)
350          {
351             char tempString[256];
352             Class type = class_data(type);
353             if(c) strcat(stringOutput, ", ");
354
355             if(type)
356                type._vTbl[__ecereVMethodID_class_OnGetString](type, &ids[c], tempString, null, null);
357             // strcatf(stringOutput, "%d", ids[c]);
358             strcat(stringOutput, tempString);
359          }
360       }
361       return stringOutput;
362    }
363
364    bool OnGetDataFromString(char * string)
365    {
366       char value[256];
367       this = IdList { };
368       while(GetAlNum(&string, value, sizeof(value)))
369          if(isdigit(value[0]))
370             Add(atoi(value));
371       return true;
372    }
373
374    int OnCompare(IdList b)
375    {
376       if(count > b.count) return 1;
377       else if(count < b.count) return -1;
378       else
379       {
380          int c;
381          for(c = 0; c<count; c++)
382          {
383             int idA = ids[c], idB = b.ids[c];
384             if(idA > idB) return 1;
385             else if(idA < idB) return -1;
386          }         
387       }
388       return 0;
389    }
390
391    Window OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)
392    {
393       ListBox list
394       {
395          dataBox,
396          borderStyle = 0,
397          alwaysEdit = true,
398          anchor = { 0, 0, 0, 0 };
399
400          bool DataBox::NotifyChanged(ListBox listBox, DataRow row)
401          {
402             Id id = row.GetData(null);
403             if(!id)
404             {
405                if(row.next)
406                   listBox.DeleteRow(row);
407             }            
408             else
409             {
410                if(row == listBox.lastRow)
411                {
412                   row = listBox.AddRow();
413                   row.SetData(null, 0);
414                   listBox.scroll.y = listBox.scrollArea.h;
415                }
416                else if(row.next == listBox.lastRow)
417                   listBox.scroll.y = listBox.scrollArea.h;
418             }
419             Modified();
420             return true;
421          }
422       };
423
424       int c;
425       DataRow r;
426       /*if(!this)
427          this = eInstance_New(_class);
428          */
429       if(this || !this)    // FIXME
430       {
431          Class type = class_data(type);
432          list.AddField({ type, editable = true });
433       }
434       for(c = 0; c < (this ? count : 0); c++)
435       {
436          r = list.AddRow();
437          r.SetData(null, ids[c]);
438       }
439       r = list.AddRow();
440       r.SetData(null, 0);
441       list.Create();
442       list.modifiedDocument = false;
443       return list;
444    }
445
446    bool OnSaveEdit(Window window, void * object)
447    {
448       ListBox list = (ListBox) window;
449       if(list.modifiedDocument)
450       {
451          DataRow r;
452          if(!this)
453             this = eInstance_New(_class);
454          Clear();
455          for(r = list.firstRow; r; r = r.next)
456          {
457             Id id = r.GetData(null);
458             if(id)
459                Add(id);         
460          }
461          return true;
462       }
463       return false;
464    }
465    
466    ~IdList()
467    {
468       delete ids;
469    }
470 }
471
472 static void FreeString(String string)
473 {
474    delete string;
475 }
476
477 public class StringList
478 {
479    StringBinaryTree strings
480    {  
481       CompareKey = (void *)BinaryTree::CompareString;
482       FreeKey = (void *)FreeString;
483    };
484
485    void Clear()
486    {
487       strings.Free();
488    }
489    
490    bool Includes(String string)
491    {
492       return strings.FindString(string) != null;
493    }
494
495    bool Add(String string)
496    {
497       BTNode node { key = (uint)CopyString(string) };
498       if(strings.Add(node))
499          return true;
500       else
501       {
502          FreeString((String)node.key);
503          delete node;
504       }
505       return false;
506    }
507
508    bool Delete(String string)
509    {
510       BTNode node = strings.FindString(string);
511       if(node)
512       {
513          strings.Delete(node);
514          return true;
515       }
516       return false;
517    }
518
519    void _OnUnserialize(IOChannel channel)
520    {
521       channel.Get(strings);
522    }
523    
524    void OnUnserialize(IOChannel channel)
525    {
526       this = eInstance_New(class(StringList));
527       _OnUnserialize(channel);
528    }
529
530    void OnSerialize(IOChannel channel)
531    {
532       channel.Put(strings);
533    }
534
535    int OnCompare(StringList b)
536    {
537       BTNode nodeA = strings.first, nodeB = b.strings.first;
538       for(; nodeA || nodeB; nodeA = nodeA ? nodeA.next : null, nodeB = nodeB ? nodeB.next : null)
539       {
540          int result;
541          if(nodeA && !nodeB) return 1;
542          else if(nodeB && !nodeA) return -1;
543          else
544          {
545             result = strcmp((char *)nodeA.key, (char *)nodeB.key);
546             if(result) return result;
547          }
548       }         
549       return 0;
550    }
551
552    Window OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)
553    {
554       ListBox list
555       {
556          dataBox,
557          borderStyle = 0,
558          alwaysEdit = true,
559          anchor = { 0, 0, 0, 0 };
560
561          bool OnKeyHit(Key key, unichar ch)
562          {
563             return (key == enter) ? false : ListBox::OnKeyHit(key, ch);
564          }
565          
566          bool DataBox::NotifyChanged(ListBox listBox, DataRow row)
567          {
568             String string = row.GetData(null);
569             if(!string || !string[0])
570             {
571                if(row.next)
572                {
573                   listBox.DeleteRow(row);
574                   listBox.firstChild.Activate();
575                }
576             }            
577             else
578             {
579                if(row == listBox.lastRow)
580                {
581                   row = listBox.AddRow();
582                   row.SetData(null, null);
583                   listBox.scroll.y = listBox.scrollArea.h;
584                }
585                else
586                {
587                   row = row.next;
588                   /*
589                   if(row.next == listBox.lastRow)
590                      listBox.scroll.y = listBox.scrollArea.h;
591                   */
592                }
593                listBox.SelectRow(row);
594             }
595             Modified();
596             return true;
597          }
598
599          bool DataBox::NotifyEdited(ListBox listBox, DataRow row)
600          {
601             listBox.firstChild.Activate();
602             return true;
603          }
604
605          bool DataBox::NotifyModified(ListBox listBox, DataRow row)
606          {
607             String string = row.GetData(null);
608             if(!string || !string[0])
609             {
610                if(row.next)
611                {
612                   listBox.DeleteRow(row);
613                   listBox.firstChild.Activate();
614                }
615             }            
616             else
617             {
618                if(row == listBox.lastRow)
619                {
620                   row = listBox.AddRow();
621                   row.SetData(null, null);
622                   listBox.scroll.y = listBox.scrollArea.h;
623                }
624                else if(row.next == listBox.lastRow)
625                   listBox.scroll.y = listBox.scrollArea.h;
626             }
627             Modified();
628             return true;
629          }
630       };
631
632       BTNode node;
633       DataRow r;
634       
635       /*
636       {
637          if(!this)
638             this = eInstance_New(_class);
639       }
640       */
641
642       if(this || !this)    // FIXME
643       {
644       list.AddField({ class(char *), editable = true });
645       }
646       for(node = strings.first; node; node = node.next)
647       {
648          r = list.AddRow();
649          r.SetData(null, (String)node.key);
650       }
651       r = list.AddRow();
652       r.SetData(null, null);
653       list.Create();
654       list.modifiedDocument = false;
655       return list;
656    }
657
658    bool OnSaveEdit(Window window, void * object)
659    {
660       ListBox list = (ListBox) window;
661       if(list.modifiedDocument)
662       {
663          
664          DataRow r;
665          if(list.activeChild)
666             ((DataBox)list.activeChild).SaveData();
667
668          if(!this)
669             this = eInstance_New(_class);
670
671          // TODO: Fix how to get the data box...
672          Clear();
673          for(r = list.firstRow; r; r = r.next)
674          {
675             String string = r.GetData(null);
676             if(string)
677             {
678                Add(string);
679             }
680          }
681          return true;
682       }
683       return false;
684    }
685    
686    ~StringList()
687    {
688       strings.Free();
689    }
690 }
691
692 public class FixedMultiLineString : String
693 {
694    Window OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)
695    {
696       // Don't show the editbox right away so that the text is highlighted by default
697       char * string = "";
698       EditBox editBox
699       {
700          dataBox, visible = false,
701          borderStyle = 0,
702          textHorzScroll = true,
703          modifyVirtualArea = false,
704          anchor = { 0, 0, 0, 0 };
705          multiLine = true;
706
707          void DataBox::NotifyUpdate(EditBox editBox)
708          {
709             Modified();          
710             modifiedDocument = true;
711          }
712       };
713       editBox.contents = this;
714       editBox.visible = true;
715
716       editBox.Create();
717       if(!dataBox.active)
718          editBox.contents = this;
719       return editBox;
720    }
721
722    bool OnSaveEdit(Window window, void * object)
723    {
724       bool changed = false;
725       EditBox editBox = (EditBox)window;
726       if(editBox.modifiedDocument)
727       {
728          EditLine line;
729          int size = 0;
730          char * string;
731
732          delete this;
733
734          for(line = editBox.firstLine; line; line = line.next)
735             size += line.count+1;
736          this = string = new char[size+1];
737          size = 0;
738          for(line = editBox.firstLine; line; line = line.next)
739          {
740             memcpy(string + size, line.text, line.count);
741             size += line.count;
742             if(line.next)
743             {
744                string[size] = '\n';
745                size++;
746             }
747          }
748          string[size] = '\0';
749
750          changed = true;
751       }
752       return changed;
753    }
754 };
755
756 public class CIString : String
757 {
758    
759 }
760
761 public class MultiLineString : String
762 {
763    Window OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)
764    {
765       // Don't show the editbox right away so that the text is highlighted by default
766       char * string = "";
767       EditBox editBox
768       {
769          dataBox, visible = false,
770          borderStyle = 0,
771          hasHorzScroll = true, hasVertScroll = true,
772          modifyVirtualArea = false,
773          autoSize = dataBox.autoSize;
774          anchor = { 0, 0, 0, 0 };
775          multiLine = true;
776
777          void DataBox::NotifyUpdate(EditBox editBox)
778          {
779             Modified();
780             modifiedDocument = true;
781          }
782
783          bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
784          {
785             opacity = active ? 1.0f : parent.opacity;
786             return true;
787          }
788       };
789       editBox.contents = this;
790       editBox.visible = true;
791
792       editBox.Create();
793       if(!dataBox.active)
794          editBox.contents = this;
795       return editBox;
796    }
797
798    bool OnSaveEdit(Window window, void * object)
799    {
800       bool changed = false;
801       EditBox editBox = (EditBox)window;
802       if(editBox.modifiedDocument)
803       {
804          EditLine line;
805          int size = 0;
806          char * string;
807
808          delete this;
809
810          for(line = editBox.firstLine; line; line = line.next)
811             size += line.count+1;
812          this = string = new char[size+1];
813          size = 0;
814          for(line = editBox.firstLine; line; line = line.next)
815          {
816             memcpy(string + size, line.text, line.count);
817             size += line.count;
818             if(line.next)
819             {
820                string[size] = '\n';
821                size++;
822             }
823          }
824          string[size] = '\0';
825
826          changed = true;
827       }
828       return changed;
829    }
830 };
831
832 public struct DataList : OldList
833 {
834    class_data Class type;
835    class_data char * typeName;
836    // class_property Class type { set { class_data(type) = value; } };
837    class_property char * type { set { class_data(typeName) = value; } };
838    class_property Class dataType { get { return class_data(type); } };
839
840    void OnUnserialize(IOChannel channel)
841    {
842       Class type;
843       if(!class_data(type))
844          class_data(type) = eSystem_FindClass(__thisModule.application, class_data(typeName));
845       type = class_data(type);
846       
847       this = { };
848
849       while(true)
850       {
851          bool truth;
852          OldLink link;
853          channel.Unserialize(truth);
854          if(!truth) break;
855          link = OldLink { };
856
857          if(type)
858          {
859             if(type.type == structClass)
860                link.data = new0 byte[type.structSize];
861             type._vTbl[__ecereVMethodID_class_OnUnserialize](type, (type.type == structClass) ? link.data : &link.data, channel);
862          }
863          Add(link);
864       }
865    }
866
867    void OnSerialize(IOChannel channel)
868    {
869       OldLink node = first;
870       Class type;
871       if(!class_data(type))
872          class_data(type) = eSystem_FindClass(__thisModule.application, class_data(typeName));
873       type = class_data(type);
874
875       while(true)
876       {
877          bool truth = true;
878          if(node)
879          {
880             channel.Serialize(truth);
881             if(type.type == bitClass || type.type == unitClass || (type.type == systemClass && type.typeSize))
882                type._vTbl[__ecereVMethodID_class_OnSerialize](type, &node.data, channel);
883             else
884                type._vTbl[__ecereVMethodID_class_OnSerialize](type, node.data, channel);
885             node = node.next;
886          }
887          else
888          {
889             truth = false;
890             channel.Serialize(truth);
891             node = null;
892          }
893          if(!node) break;
894       }
895    }
896
897    int OnCompare(DataList b)
898    {
899       OldLink nodeA = first, nodeB = b.first;
900       for(; nodeA || nodeB; nodeA = nodeA ? nodeA.next : null, nodeB = nodeB ? nodeB.next : null)
901       {
902          int result;
903          if(nodeA && !nodeB) return 1;
904          else if(nodeB && !nodeA) return -1;
905          else
906          {
907             Class type = class_data(type);
908             result = type._vTbl[__ecereVMethodID_class_OnCompare](type, 
909                (type.type == systemClass || type.type == bitClass || type.type == enumClass || type.type == unitClass) ? &nodeA.data : (void *)nodeA.data,
910                (type.type == systemClass || type.type == bitClass || type.type == enumClass || type.type == unitClass) ? &nodeB.data : (void *)nodeB.data);
911             if(result) return result;
912          }
913       }         
914       return 0;
915    }
916
917    Window OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)
918    {
919       ListBox list
920       {
921          dataBox,
922          borderStyle = 0,
923          alwaysEdit = true,
924          anchor = { 0, 0, 0, 0 };
925
926          bool OnKeyHit(Key key, unichar ch)
927          {
928             return (key == enter) ? false : ListBox::OnKeyHit(key, ch);
929          }
930          
931          bool DataBox::NotifyChanged(ListBox listBox, DataRow row)
932          {
933             Class type = ((subclass(DataList))this.type).dataType;
934             if(type.type == normalClass && !strcmp(type.dataTypeString, "char *"))
935             {
936                String string = row.GetData(null);
937                if(!string || !string[0])
938                {
939                   /*if(row.next)
940                   {
941                      listBox.DeleteRow(row);
942                      listBox.firstChild.Activate();
943                   }*/
944                }            
945                else
946                {
947                   if(row == listBox.lastRow)
948                   {
949                      /*row = listBox.AddRow();
950                      row.SetData(null, null);
951                      listBox.scroll.y = listBox.scrollArea.h;*/
952                   }
953                   else
954                   {
955                      row = row.next;
956                   }
957                   listBox.SelectRow(row);
958                }
959                //Modified();
960             }
961             return true;
962          }
963          /*
964          bool DataBox::NotifyModified(ListBox listBox, DataRow row)
965          {
966             Class type = ((subclass(DataList))this.type).dataType;
967             if(type.type == normalClass && !strcmp(type.dataTypeString, "char *"))
968             {
969                String string = row.GetData(null);
970                if(!string || !string[0])
971                {
972                   if(row.next)
973                   {
974                      listBox.DeleteRow(row);
975                      listBox.firstChild.Activate();
976                   }
977                }            
978                else
979                {
980                   if(row == listBox.lastRow)
981                   {
982                      row = listBox.AddRow();
983                      row.SetData(null, null);
984                      listBox.scroll.y = listBox.scrollArea.h;
985                   }
986                   else if(row.next == listBox.lastRow)
987                      listBox.scroll.y = listBox.scrollArea.h;
988                }
989                Modified();
990             }
991             return true;
992          }
993          */
994          bool DataBox::NotifyEditing(ListBox listBox, DataRow row)
995          {
996             Class type = ((subclass(DataList))this.type).dataType;
997             DataBox editData = (DataBox)listBox.firstChild;
998             // if(type.type != normalClass || strcmp(type.dataTypeString, "char *"))
999             {
1000                if(type)
1001                {
1002                   if(type.type == normalClass && !*(void **)editData.data && strcmp(type.dataTypeString, "char *"))
1003                   {
1004                      *(void **)editData.data = eInstance_New(type);
1005                      row.SetData(null, *(void **)editData.data);
1006                   }
1007                }
1008
1009                if(row == listBox.lastRow)
1010                {
1011                   listBox.alwaysEdit = false;
1012                   if(type.type == normalClass || type.type == structClass || type.type == noHeadClass)
1013                      listBox.AddRow().SetData(null, null);
1014                   else
1015                      listBox.AddRow().SetData(null, 0);
1016                   listBox.scroll.y = listBox.scrollArea.h;
1017                   listBox.alwaysEdit = true;
1018                }
1019             }            
1020             return true;
1021          }
1022
1023          bool DataBox::NotifyEdited(ListBox listBox, DataRow row)
1024          {
1025             listBox.firstChild.Activate();
1026             return true;
1027          }
1028
1029          bool DataBox::NotifyEditDone(ListBox listBox, DataRow row)
1030          {
1031             Class type = ((subclass(DataList))this.type).dataType;
1032             // if(type.type != normalClass || strcmp(type.dataTypeString, "char *"))
1033             if(type)
1034             {
1035                void * data = ((type.type == normalClass || type.type == noHeadClass || type.type == structClass) ? row.GetData(null) : *(uint *)row.GetData(null));
1036                if(!data)
1037                {
1038                   //if(strcmp(type.dataTypeString, char *"))
1039                      //listBox.currentRow = null;
1040                   if(row != listBox.lastRow)
1041                   {
1042                      listBox.alwaysEdit = false;
1043                      listBox.DeleteRow(row);
1044                      listBox.alwaysEdit = true;
1045                   }
1046                }
1047             }
1048             Modified();
1049             return true;
1050          }
1051    
1052          void OnDestroy()
1053          {
1054             Class type = firstField.dataType;
1055             // if(type.type != normalClass || strcmp(type.dataTypeString, "char *"))
1056             if(type)
1057             {
1058                if(type.type == normalClass && strcmp(type.dataTypeString, "char *"))
1059                   eInstance_Delete(lastRow.GetData(null));
1060                if(type.type == normalClass || type.type == structClass || type.type == noHeadClass)
1061                   lastRow.SetData(null, null);
1062                else
1063                   lastRow.SetData(null, 0);
1064             }
1065          }
1066       };
1067
1068       OldLink node;
1069       DataRow r;
1070       Class type;
1071       if(!class_data(type))
1072          class_data(type) = eSystem_FindClass(__thisModule.application, class_data(typeName));
1073       type = class_data(type);
1074       
1075       list.AddField({ type, editable = true });
1076       for(node = first; node; node = node.next)
1077       {
1078          r = list.AddRow();
1079          if(type)
1080          {
1081             if(type.type == normalClass && !strcmp(type.dataTypeString, "char *"))
1082                r.SetData(null, CopyString((String)node.data));
1083             else
1084                r.SetData(null, node.data);
1085          }
1086       }
1087       r = list.AddRow();
1088       if(type.type == normalClass || type.type == structClass || type.type == noHeadClass)
1089          r.SetData(null, null);
1090       else
1091          r.SetData(null, 0);
1092       list.Create();
1093       list.modifiedDocument = false;
1094       return list;
1095    }
1096
1097    bool OnSaveEdit(Window window, void * object)
1098    {
1099       ListBox list = (ListBox) window;
1100       if(list.modifiedDocument)
1101       {
1102          Class type = class_data(type);
1103          DataRow r;
1104          if(list.activeChild)
1105             ((DataBox)list.activeChild).SaveData();
1106
1107          if(type.type != normalClass || !strcmp(type.dataTypeString, "char *"))
1108             OnFree();
1109          else if(type.type == structClass)
1110             Free(OldLink::Free);
1111          else
1112             Free(null);
1113
1114          for(r = list.firstRow; r; r = r.next)
1115          {
1116             if(type.type == noHeadClass || type.type == normalClass || type.type == structClass)
1117             {
1118                void * data = r.GetData(null);
1119                if(data)
1120                {
1121                   if(type.type == normalClass && !strcmp(type.dataTypeString, "char *"))
1122                      Add(OldLink { data = CopyString(data) });
1123                   else if(type.type == structClass)
1124                   {
1125                      OldLink link { data = new byte[type.structSize] };
1126                      Add(link);
1127                      memcpy(link.data, data, type.structSize);
1128                   }
1129                   else
1130                      Add(OldLink { data = data });
1131                }
1132             }
1133             else
1134             {
1135                uint i = r.GetData(null);
1136                if(i)
1137                   Add(OldLink { data = (void *)i });
1138             }
1139          }
1140          return true;
1141       }
1142       return false;
1143    }
1144
1145    void OnFree()
1146    {  
1147       Class type;
1148       OldLink node;
1149
1150       if(!class_data(type))
1151          class_data(type) = eSystem_FindClass(__thisModule.application, class_data(typeName));
1152       type = class_data(type);
1153       while(node = first)
1154       {
1155          // TO STUDY: ONFREE SHOULD BE USED ONLY FOR LISTBOX?
1156          if(type)
1157          {
1158             if(type.type == normalClass && strcmp(type.dataTypeString, "char *"))
1159                eInstance_Delete(node.data);
1160             else if(type.type == structClass)
1161                delete node.data;
1162             else
1163                type._vTbl[__ecereVMethodID_class_OnFree](type, node.data);
1164          }
1165          Delete(node);
1166       }
1167    }
1168 };