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