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