EDA/gui: Fixed stringification of some types
[sdk] / eda / libeda / src / gui.ec
1 import "idList"
2
3 define sgs = 3; // screen sgs size
4 define shadowS = 4;
5
6 default:
7 extern int __ecereVMethodID_class_OnFree;
8 extern int __ecereVMethodID_class_OnGetString;
9 private:
10
11 const char * defaultNameField = "Name";
12 const char * defaultIdField = "Id";
13 const char * defaultActiveField = "Active";
14
15 public void SetDefaultIdField(const char * value) { defaultIdField = value; }
16 public void SetDefaultNameField(const char * value) { defaultNameField = value; }
17
18 public class ButtonStyle : Button
19 {
20    font = { $"Arial", 10, bold = true };
21    creationActivation = doNothing;
22 }
23
24 public class Group : Window
25 {
26    size = { 84, 31 };
27    tabCycle = true;
28    //inactive = true; // TOFIX causes problems...
29
30 public:
31    Label title { this, font = { $"Arial", 10, bold = true }, position = { 16, 2 } };
32
33    bool OnCreate()
34    {
35       title.labeledWindow = this;
36       return true;
37    }
38
39    void OnRedraw(Surface surface)
40    {
41       int x = clientSize.w - 1, y = clientSize.h - 1;
42
43       surface.SetBackground(gray);
44
45       surface.Rectangle(0, 10, x - shadowS, y - shadowS);
46       surface.Area(shadowS / 2, y - shadowS + 1, x, y);
47       surface.Area(x - shadowS + 1, 10 + shadowS / 2, x, y);
48
49       surface.SetBackground(white);
50
51       surface.Rectangle(10, 0, title.size.w + 22, title.size.h + 4);
52       surface.Area(11, 1, title.size.w + 21, title.size.h + 3);
53    }
54 }
55
56 public class CheckBool : bool
57 {
58    Window OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)
59    {
60       Button button = dataBox.keepEditor ? (Button)dataBox.editor : null;
61       if(!button)
62       {
63          button = Button
64          {
65             dataBox, borderStyle = 0, text = dataBox.text, anchor = { 0, 0, 0, 0 },
66             // size = { 100, 22 };
67             modifyVirtualArea = false, isCheckbox = true;
68
69             bool DataBox::NotifyClicked(Button control, int x, int y, Modifiers mods)
70             {
71                bool checked = control.checked;
72                if(readOnly)
73                   control.checked = !checked;
74                else
75                   SetData(&checked, false);
76                return true;
77             }
78          };
79       }
80       button.checked = this;
81       button.Create();
82       return button;
83    }
84 }
85
86 String GetNameString(Row r, Field nameField)
87 {
88    String s = null;
89    if(nameField.type != class(String) && nameField.type != class(char *))
90    {
91       char tempString[4096];
92       Class type = nameField.type;
93       int64 data = 0;
94       if(type.type == structClass)
95          data = (int64)(intptr)new0 byte[type.structSize];
96       ((bool (*)())(void *)r.GetData)(r, nameField, type, (type.type == structClass) ? (void *)(intptr)data : &data);
97       if(type.type == systemClass || type.type == enumClass || type.type == bitClass)
98          s = CopyString(((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, &data, tempString, null, null));
99       else
100          s = CopyString(((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, (void *)(intptr)data, tempString, null, null));
101       ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, (void *)(intptr)data);
102       if(type.type == structClass)
103       {
104          void * _data = (void *)(intptr)data;
105          delete _data;
106       }
107    }
108    else
109       r.GetData(nameField, s);
110    return s;
111 }
112
113 public class TableDropBox : DropBox
114 {
115    anchor = { left = 130, top = 180, right = shadowS + sgs * 2 };
116    borderStyle = deep;
117
118 public:
119    uint filter;
120    bool filtered;
121    Field nameField;
122    uint exclusion;
123    Table table;
124    Field filterField;
125
126    property uint filter
127    {
128       set
129       {
130          filter = value;
131          filtered = true;
132       }
133       get { return filter; }
134    }
135    property Field nameField { set { nameField = value; } }
136    property uint exclusion { set { exclusion = value; } }
137    property Table table { set { table = value; if(!nameField && value) nameField = value.FindField(defaultNameField); } }
138
139    virtual void Refill()
140    {
141       Clear();
142       if(table)
143       {
144          Field fldId = table.FindField(defaultIdField);
145          if(fldId && nameField)
146          {
147             Row r { table };
148             if(filterField && filtered)
149             {
150                if(eClass_IsDerived(filterField.type, class(Id)))
151                {
152                   FieldIndex indexedFields[1];
153                   // Table tbl = table.db.OpenTable(table.name, { tableRows });
154                   const char * name = table.name;
155                   Database db = table.db;
156                   Table tbl = db.OpenTable(name, { tableRows });
157                   if(tbl)
158                   {
159                      delete r;
160
161                      indexedFields[0] = { filterField };
162                      tbl.GenerateIndex(1, indexedFields, false);
163
164                      r = Row { tbl };
165
166                      for(r.Find(filterField, middle, nil, filter); !r.nil; r.Next()) //while(r.Next())
167                      {
168                         Id id;
169                         Id idFilter;
170                         r.GetData(filterField, idFilter);
171                         r.GetData(fldId, id);
172                         if(idFilter != filter)
173                            break;
174                         if(!exclusion || id != exclusion)
175                         {
176                            String s = GetNameString(r, nameField);
177                            AddString(s).tag = id;
178                            delete s;
179                         }
180                      }
181                   }
182                }
183                else if(eClass_IsDerived(filterField.type, class(IdList)))
184                {
185                   while(r.Next())
186                   {
187                      Id id;
188                      IdList idList;
189                      r.GetData(filterField, idList);
190                      r.GetData(fldId, id);
191                      if(idList && idList.Includes(filter) && (!exclusion || !idList.Includes(exclusion)))
192                      {
193                         String s = GetNameString(r, nameField);
194                         AddString(s).tag = id;
195                         delete s;
196                      }
197                      delete idList;
198                   }
199                }
200             }
201             else if(exclusion)
202             {
203                while(r.Next())
204                {
205                   Id id;
206                   r.GetData(fldId, id);
207                   if(id != exclusion)
208                   {
209                      String s = GetNameString(r, nameField);
210                      AddString(s).tag = id;
211                      delete s;
212                   }
213                }
214             }
215             else
216             {
217                while(r.Next())
218                {
219                   Id id;
220                   String s = GetNameString(r, nameField);
221                   r.GetData(fldId, id);
222                   AddString(s).tag = id;
223                   delete s;
224                }
225             }
226             delete r;
227          }
228       }
229       Sort(null, 1);
230    }
231
232    property Field filterField { set { filterField = value; } }
233
234    bool OnKeyHit(Key key, unichar ch)
235    {
236       if((SmartKey)key == del)
237       {
238          SelectRow(null); //currentRow = null;
239          return false;
240       }
241       else if((SmartKey)key == enter)
242          parent.CycleChildren(true, false, false, true);
243
244       return DropBox::OnKeyHit(key, ch);
245    }
246
247    bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
248    {
249       modifiedDocument = true;
250       return true;
251    }
252
253    bool OnCreate()
254    {
255       Refill();
256       return true;
257    }
258
259    void EditNotifyCharsAdded()
260    {
261       if(!editBox.NotifyUpdate || editBox.NotifyUpdate == EditBox::NotifyUpdate)
262          editBox.NotifyUpdate = EditNotifyUpdate;
263    }
264
265    void EditNotifyUpdate(EditBox editBox)
266    {
267       DataRow row;
268       const char * contents = editBox.contents;
269       int len = strlen(contents);
270       if(len && editBox.charPos == len)
271       {
272          EditLine line = editBox.firstLine;
273          int x1, x2;
274
275          editBox.GetSelPos(null, null, &x1, null, null, &x2, false);
276          if(x2 == x1)
277          {
278             for(row = firstRow; row; row = row.next)
279             {
280                const char * string = row.string;
281                if(string && SearchString(string, 0, contents, false, false) == string)
282                {
283                   // SelectRow(row);
284                   editBox.contents = row.string;
285                   editBox.SetSelPos(line, 0, len, line, 0, strlen(string));
286                   break;
287                }
288             }
289          }
290       }
291       editBox.NotifyUpdate = null;
292    }
293 }
294
295 public class DropDataBox : DataBox
296 {
297    size.h = 24;
298    keepEditor = true;
299    borderStyle = deep;
300    showNone = false; //true;
301
302 public:
303
304    property uint filter { set { filtered = true; filter = value; } get { return filter; } }
305    property bool filtered { set { filtered = value; } }
306    property uint exclusion { set { exclusion = value; } }
307    property Field filterField { set { filterField = value; } }
308    property Field nameField { set { nameField = value; } }
309    virtual void TableDropBox::RefillFunction();
310    property bool showNone { set { showNone = value; } }
311
312    void Refill()
313    {
314       if(editor)
315       {
316          TableDropBox dropBox = (TableDropBox) editor;
317          uint id = data ? *(uint *)data : MAXDWORD;
318          void * notifyChanged = (void *)NotifyChanged;
319
320          OnConfigure(dropBox);
321
322          NotifyChanged = null;
323          dropBox.Refill();
324          NotifyChanged = notifyChanged;
325
326          if(id != MAXDWORD) dropBox.SelectRow(dropBox.FindSubRow(id));
327       }
328    }
329
330    void OnConfigure(TableDropBox dropBox)
331    {
332       if(RefillFunction != DropDataBox::RefillFunction) dropBox.Refill = RefillFunction;
333       if(nameField) dropBox.nameField = nameField;
334       if(filterField) dropBox.filterField = filterField;
335       if(filtered)
336          dropBox.filter = filter;
337       else
338          dropBox.filtered = false;
339
340       dropBox.exclusion = exclusion;
341       dropBox.showNone = showNone;
342    }
343
344 private:
345    Field nameField;
346    Field filterField;
347    uint exclusion;
348    uint filter;
349    bool filtered;
350    bool showNone;
351 }
352
353 public class EditDropDataBox : DropDataBox
354 {
355    void OnConfigure(TableDropBox dropBox)
356    {
357       DropDataBox::OnConfigure(dropBox);
358       dropBox.editText = true;
359       dropBox.editBox.NotifyCharsAdded = (void *)TableDropBox::EditNotifyCharsAdded;
360    }
361 }
362
363 public class FieldDataBox : DataBox
364 {
365    size = { 100, 22 };
366    anchor = { left = 110, right = shadowS + sgs * 2 };
367    borderStyle = deep;
368
369 public:
370    property Row row
371    {
372       set { row = value; }
373       get { return row; }
374    }
375    property EditSection editor
376    {
377       set
378       {
379          parent = value.editArea;
380          master = value;
381          value.AddFieldEditor(this);
382          row = value.editRow;
383       }
384    };
385
386    property Field field
387    {
388       set
389       {
390          Class dataType;
391          if(field) type = null;
392
393          if(dataHolder)
394          {
395             ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, (void *)(intptr)dataHolder);
396             if(type.type == structClass)
397             {
398                void * dataPtr = (void *)(intptr)dataHolder;
399                delete dataPtr;
400             }
401             dataHolder = 0;
402          }
403
404          field = value;
405          dataType = value ? value.type : null;
406          if(!text)
407             text = field ? field.name : null;
408
409          if(dataType && dataType.type == structClass)
410          {
411             dataHolder = (int64)(intptr)new0 byte[dataType.structSize];
412             data = (void *)(intptr)dataHolder;
413          }
414          else if(dataType && (dataType.type == noHeadClass || dataType.type == normalClass))
415          {
416             if(eClass_IsDerived(dataType, class(String)))
417                dataHolder = (int64)(intptr)CopyString("");
418             else
419                dataHolder = (int64)(intptr)eInstance_New(dataType);
420             data = (void *)&dataHolder;
421          }
422          else
423          {
424             dataHolder = 0;
425             data = &dataHolder;
426          }
427          if(!type) type = dataType;
428       }
429    }
430
431    void Clear()
432    {
433       if(data)
434          SetData(null, false);
435
436       if(type && (type.type == noHeadClass || type.type == normalClass))
437       {
438          if(eClass_IsDerived(type, class(String)))
439             dataHolder = (int64)(intptr)CopyString("");
440          else
441             dataHolder = (int64)(intptr)eInstance_New(type);
442          data = (void *)&dataHolder;
443       }
444
445       if(created)
446          Refresh();
447    }
448
449    void Load()
450    {
451       if(field && row)
452       {
453          SetData(null, false);
454          master.modifiedDocument = false;
455
456          ((bool (*)())(void *)Row::GetData)(row, field, field.type, data);
457
458          if(!dataHolder && type && (type.type == noHeadClass || type.type == normalClass))
459          {
460             if(eClass_IsDerived(type, class(String)))
461                dataHolder = (int64)(intptr)CopyString("");
462             else
463                dataHolder = (int64)(intptr)eInstance_New(type);
464             data = (void *)&dataHolder;
465          }
466
467          // if(created)
468          Refresh();
469       }
470    }
471
472    virtual void Save()
473    {
474       if(field && row)
475       {
476          Class type = field.type;
477          if(!DataBox::SaveData())
478             Refresh();
479
480          ((bool (*)())(void *)Row::SetData)(row, field, type,
481             (type.type == noHeadClass || type.type == normalClass) ? *(void **)data : data);
482       }
483    }
484
485    void Init()
486    {
487       if(created)
488          Refresh();
489    }
490
491    bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
492    {
493       if(!active)
494       {
495          if(modifiedDocument && !DataBox::SaveData())
496             Refresh();
497       }
498       return true;
499    }
500
501    bool OnKeyDown(Key key, unichar ch)
502    {
503       if((SmartKey)key == enter)
504       {
505          DataBox::OnKeyDown(key, ch);
506          return true;
507       }
508       else
509          return DataBox::OnKeyDown(key, ch);
510    }
511
512    bool OnKeyHit(Key key, unichar ch)
513    {
514       if((SmartKey)key == enter)
515          parent.CycleChildren(true, false, false, true);
516
517       return DataBox::OnKeyHit(key, ch);
518    }
519
520    bool Window::NotifyChanged(DataBox dataBox, bool closingDropDown)
521    {
522       modifiedDocument = true;
523       return true;
524    }
525
526    bool Window::NotifyModified()
527    {
528       modifiedDocument = true;
529       return true;
530    }
531
532    ~FieldDataBox()
533    {
534       if(data)
535          SetData(null, false);
536
537       if(dataHolder)
538       {
539          ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, (void *)(intptr)dataHolder);
540          if(type.type == structClass)
541          {
542             void * dataPtr = (void *)(intptr)dataHolder;
543             delete dataPtr;
544          }
545          dataHolder = 0;
546       }
547    }
548
549 private:
550    Field field;
551    int64 dataHolder; // THERE SEEMS(ED?) TO BE A BUG WHEN ACCESSING row ACROSS .so
552    Row row;
553 }
554
555 public class FieldCheckButton : FieldDataBox
556 {
557    borderStyle = 0;
558    type = class(CheckBool);
559    // TOCHECK: When not here, the virtual area goes wild (anchor.right is not reset)
560    position = { 0, 0 };
561 }
562
563 public class FieldDropDataBox : FieldDataBox
564 {
565    size.h = 24;
566    keepEditor = true;
567    showNone = true;
568
569 public:
570    property uint filter { set { filtered = true; filter = value; } get { return filter; } }
571    property bool filtered { set { filtered = value; } }
572    property uint exclusion { set { exclusion = value; } }
573    property Field filterField { set { filterField = value; } }
574    property Field nameField { set { nameField = value; } }
575    virtual void TableDropBox::RefillFunction();
576    property bool showNone { set { showNone = value; } }
577
578    void Refill()
579    {
580       if(editor)
581       {
582          TableDropBox dropBox = (TableDropBox) editor;
583          uint id = data ? *(uint *)data : MAXDWORD;
584          OnConfigure(dropBox);
585          dropBox.Refill();
586          if(id != MAXDWORD) dropBox.SelectRow(dropBox.FindSubRow(id));
587       }
588    }
589
590    void OnConfigure(TableDropBox dropBox)
591    {
592       if(RefillFunction != FieldDropDataBox::RefillFunction) dropBox.Refill = RefillFunction;
593       if(nameField) dropBox.nameField = nameField;
594       if(filterField) dropBox.filterField = filterField;
595       if(filtered)
596          dropBox.filter = filter;
597       else
598          dropBox.filtered = false;
599       dropBox.exclusion = exclusion;
600       dropBox.showNone = showNone;
601    }
602
603 private:
604    Field nameField;
605    Field filterField;
606    uint exclusion;
607    uint filter;
608    bool filtered;
609    bool showNone;
610 }
611
612 public class EditFieldDropDataBox : FieldDropDataBox
613 {
614    // showNone = false;
615 public:
616    void OnConfigure(TableDropBox dropBox)
617    {
618       FieldDropDataBox::OnConfigure(dropBox);
619       dropBox.editText = true;
620       dropBox.editBox.NotifyCharsAdded = (void *)TableDropBox::EditNotifyCharsAdded;
621    }
622
623    void Save()
624    {
625       TableDropBox dropBox = (TableDropBox) editor;
626
627       if(!dropBox.currentRow && dropBox.contents[0])
628       {
629          Row row { dropBox.table };
630          Id sysID;
631          OnAddTextEntry(row, dropBox, dropBox.contents);
632          sysID = row.sysID;
633          delete row;
634
635          dropBox.Refill();
636          dropBox.SelectRow(dropBox.FindSubRow(sysID));
637       }
638       FieldDataBox::Save();
639    }
640
641    virtual bool OnAddTextEntry(Row row, TableDropBox dropBox, const char * entry)
642    {
643       row.Add();
644       row.SetData(dropBox.nameField, entry);
645       return true;
646    }
647 }
648
649 public class ListSection : Group
650 {
651    text = $"List";
652    size = { 710, 287 };
653    anchor = { left = sgs, top = 32 + sgs * 3, bottom = 55 + sgs * 3 };
654 public:
655    property EditSection editor
656    {
657       set
658       {
659          editor = value;
660          value.list = this;
661       }
662    }
663
664    property Table table
665    {
666       set
667       {
668          if(value)
669          {
670             table = value;
671             if(table)
672             {
673                FieldIndex indexedFields[1];
674
675                if(!fldId) fldId = table.FindField(defaultIdField);
676                if(!fldName) fldName = table.FindField(defaultNameField);
677                if(!fldActive) fldActive = table.FindField(defaultActiveField);
678
679                indexedFields[0] = { fldId };
680                table.Index(1, indexedFields);
681
682                editor.editRow.tbl = table;
683
684                RefillList();
685             }
686          }
687       }
688    }
689    Field fldId, fldName, fldActive;
690
691    virtual DialogResult Window::NotifySaveConfirmation(ListSection listSection)
692    {
693       return MessageBox { master = this, type = yesNoCancel, text = $"List Editor", contents = $"You have modified this entry. Would you like to save it before proceeding?" }.Modal();
694    }
695
696    bool OnClose(bool parentClosing)
697    {
698       if(editor && editor.modifiedDocument)
699       {
700          switch(NotifySaveConfirmation(master, this))
701          {
702             case cancel:
703                return false;
704             case yes:
705                editor.EditSave();
706             case no:
707                editor.modifiedDocument = false;
708                break;
709          }
710       }
711       return true;
712    }
713
714    void RefillList()
715    {
716       list.Clear();
717       //if(fldId && fldName)
718       {
719          Row r { table };
720          NotifyRefillList(master, this, r);
721          delete r;
722       }
723       list.Sort(null, 1);
724       editor.modifiedDocument = false;
725    }
726
727    virtual void Window::NotifyRefillList(ListSection listSection, Row r)
728    {
729       if(listSection.fldId && listSection.fldName)
730       {
731          Class type = listSection.fldName.type;
732          bool stringName = !strcmp(type.dataTypeString, "char *");
733          while(r.Next())
734          {
735             Id id = 0;
736             String s = null;
737             r.GetData(listSection.fldId, id);
738             if(stringName)
739                r.GetData(listSection.fldName, s);
740             else
741             {
742                s = GetNameString(r, listSection.fldName);
743                // s = PrintString("Entry ", id);
744             }
745             listSection.list.AddString(s).tag = id;
746             delete s;
747          }
748       }
749    }
750
751    virtual bool Window::NotifyNew(ListSection listSection, Row r);
752
753    ButtonStyle btnNew
754    {
755       this, anchor = { right = shadowS + sgs * 2, top = 24 }, hotKey = altW, text = $"New";
756
757       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
758       {
759          list.NotifySelect(this, list, null, 0);
760          if(!editor.modifiedDocument)
761          {
762             uint id; // = table.rowsCount + 1; // this is bad with deleted rows, won't work, how to have unique id?
763             Row r { table };
764
765             if(r.Last())   // this will reuse ids in cases where the item(s) with the last id have been deleted
766             {
767                r.GetData(fldId, id);
768                id++;
769             }
770             else
771                id = 1;
772
773             editor.EditClear();
774             {
775                bool active = true;
776                r.Add();
777                {
778                   // Patch for SQLite driver which auto-increments IDs
779                   int curID = 0;
780                   if(r.GetData(fldId, curID))
781                      id = curID;
782                   else
783                      r.SetData(fldId, id);
784                }
785                if(!strcmp(fldName.type.dataTypeString, "char *"))
786                   r.SetData(fldName, $"[New]");
787
788                if(fldActive)
789                   r.SetData(fldActive, active);
790
791                if(NotifyNew(master, this, r))
792                   list.currentRow = list.AddString($"[New]");
793                delete r;
794             }
795
796             list.Sort(null, 1);
797             list.currentRow.tag = id;
798             SelectListRow(list.currentRow);
799             RefreshState();
800          }
801          return true;
802       }
803    };
804
805    virtual bool Window::NotifyDeleteConfirmation(ListSection listSection)
806    {
807       return MessageBox {  master = this, type = yesNo, text = $"List Editor",
808                            contents =  $"You are about to delete an entry.\n"
809                                         "Do you wish to continue?"
810                   }.Modal() == yes;
811    }
812
813    virtual void Window::NotifyDeleting(ListSection listSection);
814    virtual void Window::NotifyDeleted(ListSection listSection);
815
816    ButtonStyle btnDelete
817    {
818       this, anchor = { right = shadowS + sgs * 2, top = 24 }, hotKey = altD, text = $"Delete";
819
820       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
821       {
822          if(list.currentRow)
823          {
824             if(NotifyDeleteConfirmation(master, this))
825             {
826                NotifyDeleting(master, this);
827
828                editor.editRow.Delete();
829                list.DeleteRow(list.currentRow);
830                editor.EditClear();
831
832                NotifyDeleted(master, this);
833
834                SelectListRow(list.currentRow);
835                RefreshState();
836             }
837          }
838          return true;
839       }
840    };
841
842    bool FilterNotifyChanged(DataBox dataBox, bool closeDropDown)
843    {
844       editor.EditClear();
845       RefillList();
846       if(list.firstRow)
847          SelectListRow(list.firstRow);
848       RefreshState();
849       return true;
850    }
851
852    ListBox list
853    {
854       this, anchor = { left = sgs * 2, top = 22 + 22 + sgs * 4, right = shadowS + sgs * 2, bottom = shadowS + sgs * 2 };
855       alwaysHighLight = true;
856
857       bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
858       {
859          bool result = true;
860          if(/*row && */row != lastRow)
861          {
862             if(editor.modifiedDocument)
863             {
864                if(row)
865                   list.currentRow = lastRow;
866                result = false;
867                switch(NotifySaveConfirmation(master, this))
868                {
869                   case cancel:
870                      return false;
871                   case yes:
872                      editor.EditSave();
873                   case no:
874                      editor.modifiedDocument = false;
875                      list.currentRow = row;
876                      break;
877                }
878             }
879             SelectListRow(row);
880          }
881          return result;
882       }
883    };
884
885    virtual void Window::NotifySelectListRow(ListSection listSection, uint64 id);
886
887    void SelectListRow(DataRow row)
888    {
889       // Time startTime = GetTime();
890       if(row)
891       {
892          uint64 id = row.tag;
893          lastRow = row;
894
895          if(list.currentRow != row)
896             list.currentRow = row;
897          if(editor.editRow.Find(fldId, middle, nil, id))
898          {
899             editor.listRow = row;
900             NotifySelectListRow(master, this, id);
901             editor.EditLoad();
902          }
903       }
904       // Logf("SelectListRow took %f seconds\n", GetTime() - startTime);
905    }
906
907    void SelectFirst()
908    {
909       if(list.firstRow)
910          SelectListRow(list.firstRow);
911       RefreshState();
912    }
913
914    void RefreshState()
915    {
916       if(editor)
917       {
918          editor.btnSave.disabled = !list.currentRow;
919          editor.btnReload.disabled = !list.currentRow;
920          btnDelete.disabled = !list.currentRow;
921          editor.disabled = !list.firstRow;
922       }
923    }
924
925    void OnResize(int width, int height)
926    {
927       int x = width - btnDelete.size.w - 20;
928
929       btnDelete.position.x = x;
930       if(btnNew.visible)
931          btnNew.position.x = x = x - btnNew.size.w - sgs * 2;
932    }
933
934    bool OnPostCreate(void)
935    {
936       OnResize(clientSize.w, clientSize.h);
937       SelectFirst();
938       if(editor) editor.modifiedDocument = false;
939       return Window::OnPostCreate();
940    }
941
942 private:
943    EditSection editor;
944    Table table;
945    DataRow lastRow;
946 }
947
948 public class EditSection : Group
949 {
950    tabCycle = true;
951    text = $"Entry";
952    size = { 710, 55 };
953    anchor = { right = sgs, top = 32 + sgs * 3, bottom = 55 + sgs * 3 };
954
955    ~EditSection()
956    {
957       editBoxes.Free(null);
958    }
959
960 public:
961    ListSection list;
962
963    property Table table
964    {
965       set
966       {
967          if(value)
968          {
969             table = value;
970             InitFields();
971          }
972       }
973    }
974    Table table;
975
976    Row editRow { };
977    DataRow listRow;
978    OldList editBoxes { };
979
980    Window editArea { this, borderStyle = deep, tabCycle = true, anchor = { left = 8, top = 54, right = 10, bottom = 10 }, hasVertScroll = true, dontHideScroll = true };
981
982    ButtonStyle btnSave
983    {
984       this, anchor = { right = shadowS + sgs * 2, top = 24 }, hotKey = altV, text = $"Save";
985
986       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
987       {
988          EditSave();
989          return true;
990       }
991    };
992
993    ButtonStyle btnReload
994    {
995       this, anchor = { left = 10, top = 24 }, hotKey = altV, text = $"Revert";
996
997       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
998       {
999          EditLoad();
1000          return true;
1001       }
1002    };
1003
1004
1005    void AddFieldEditor(FieldDataBox box)
1006    {
1007       editBoxes.Add(OldLink { data = box });
1008    }
1009
1010    virtual void Window::NotifyInitFields(EditSection editSection);
1011
1012    void InitFields()
1013    {
1014       OldLink link;
1015       for(link = editBoxes.first; link; link = link.next)
1016       {
1017          FieldDataBox dataBox = link.data;
1018          dataBox.Init();
1019       }
1020       NotifyInitFields(master, this);
1021    }
1022
1023    void EditNew()
1024    {
1025       modifiedDocument = false;
1026    }
1027
1028    virtual void Window::NotifyEditSave(EditSection edit, String name)
1029    {
1030       edit.listRow.string = name;
1031    }
1032
1033    void EditSave()
1034    {
1035       bool stringName = !strcmp(list.fldName.type.dataTypeString, "char *");
1036       OldLink link;
1037       String name = null;
1038
1039       editRow.tbl.db.Begin();
1040       for(link = editBoxes.first; link; link = link.next)
1041       {
1042          FieldDataBox dataBox = link.data;
1043          dataBox.Save();
1044       }
1045       editRow.tbl.db.Commit();
1046       // ADDED THIS HERE FOR SQLITE TO REFRESH
1047       editRow.Find(list.fldId, middle, nil, list.list.currentRow.tag);
1048
1049       if(stringName)
1050          editRow.GetData(list.fldName, name);
1051       else
1052          name = PrintString("Entry ", list.list.currentRow.tag);
1053
1054       NotifyEditSave(master, this, name);
1055       delete name;
1056       list.list.Sort(null, 1);
1057       list.list.currentRow = list.list.currentRow;
1058
1059       modifiedDocument = false;
1060    }
1061
1062    virtual void Window::NotifyEditLoad(EditSection editSection);
1063
1064    void EditLoad()
1065    {
1066       OldLink link;
1067       for(link = editBoxes.first; link; link = link.next)
1068       {
1069          FieldDataBox dataBox = link.data;
1070          dataBox.Load();
1071       }
1072       NotifyEditLoad(master, this);
1073
1074       modifiedDocument = false;
1075    }
1076
1077    virtual void Window::NotifyEditClear(EditSection editSection);
1078
1079    void EditClear()
1080    {
1081       OldLink link;
1082       for(link = editBoxes.first; link; link = link.next)
1083       {
1084          FieldDataBox dataBox = link.data;
1085          editRow.Select(nil);
1086          dataBox.Clear();
1087       }
1088       NotifyEditClear(master, this);
1089       modifiedDocument = false;
1090    }
1091
1092    bool OnPostCreate()
1093    {
1094       // TO CHECK: Why is there a jump in the scroll thumb size when this is not here?
1095       anchor = anchor;
1096
1097       modifiedDocument = false;
1098       return true;
1099    }
1100 }