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