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