cleaned all trailing white space from source files.
[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 char * defaultNameField = "Name";
12 char * defaultIdField = "Id";
13 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                SetData(&checked, false);
73                return true;
74             }
75          };
76       }
77       button.checked = this;
78       button.Create();
79       return button;
80    }
81 }
82
83 String GetNameString(Row r, Field nameField)
84 {
85    String s = null;
86    if(nameField.type != class(String) && nameField.type != class(char *))
87    {
88       char tempString[4096];
89       Class type = nameField.type;
90       int64 data = 0;
91       if(type.type == structClass)
92          data = (int64)new0 byte[type.structSize];
93       ((bool (*)())(void *)r.GetData)(r, nameField, type, (type.type == structClass) ? (void *)data : &data);
94       s = CopyString(((char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, (void *)data, tempString, null, null));
95       ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, (void *)data);
96       if(type.type == structClass)
97       {
98          void * _data = (void *)data;
99          delete _data;
100       }
101    }
102    else
103       r.GetData(nameField, s);
104    return s;
105 }
106
107 public class TableDropBox : DropBox
108 {
109    anchor = { left = 130, top = 180, right = shadowS + sgs * 2 };
110    borderStyle = deep;
111
112 public:
113    uint filter;
114    bool filtered;
115    Field nameField;
116    uint exclusion;
117    Table table;
118    Field filterField;
119
120    property uint filter
121    {
122       set
123       {
124          filter = value;
125          filtered = true;
126       }
127       get { return filter; }
128    }
129    property Field nameField { set { nameField = value; } }
130    property uint exclusion { set { exclusion = value; } }
131    property Table table { set { table = value; if(!nameField && value) nameField = value.FindField(defaultNameField); } }
132
133    virtual void Refill()
134    {
135       Clear();
136       if(table)
137       {
138          Field fldId = table.FindField(defaultIdField);
139          if(fldId && nameField)
140          {
141             Row r { table };
142             if(filterField && filtered)
143             {
144                if(eClass_IsDerived(filterField.type, class(Id)))
145                {
146                   FieldIndex indexedFields[1];
147                   // Table tbl = table.db.OpenTable(table.name, { tableRows });
148                   char * name = table.name;
149                   Database db = table.db;
150                   Table tbl = db.OpenTable(name, { tableRows });
151                   if(tbl)
152                   {
153                      delete r;
154
155                      indexedFields[0] = { filterField };
156                      tbl.GenerateIndex(1, indexedFields, false);
157
158                      r = Row { tbl };
159
160                      for(r.Find(filterField, middle, nil, filter); !r.nil; r.Next()) //while(r.Next())
161                      {
162                         Id id;
163                         Id idFilter;
164                         r.GetData(filterField, idFilter);
165                         r.GetData(fldId, id);
166                         if(idFilter != filter)
167                            break;
168                         if(!exclusion || id != exclusion)
169                         {
170                            String s = GetNameString(r, nameField);
171                            AddString(s).tag = id;
172                            delete s;
173                         }
174                      }
175                   }
176                }
177                else if(eClass_IsDerived(filterField.type, class(IdList)))
178                {
179                   while(r.Next())
180                   {
181                      Id id;
182                      IdList idList;
183                      r.GetData(filterField, idList);
184                      r.GetData(fldId, id);
185                      if(idList && idList.Includes(filter) && (!exclusion || !idList.Includes(exclusion)))
186                      {
187                         String s = GetNameString(r, nameField);
188                         AddString(s).tag = id;
189                         delete s;
190                      }
191                      delete idList;
192                   }
193                }
194             }
195             else if(exclusion)
196             {
197                while(r.Next())
198                {
199                   Id id;
200                   r.GetData(fldId, id);
201                   if(id != exclusion)
202                   {
203                      String s = GetNameString(r, nameField);
204                      AddString(s).tag = id;
205                      delete s;
206                   }
207                }
208             }
209             else
210             {
211                while(r.Next())
212                {
213                   Id id;
214                   String s = GetNameString(r, nameField);
215                   r.GetData(fldId, id);
216                   AddString(s).tag = id;
217                   delete s;
218                }
219             }
220             delete r;
221          }
222       }
223       Sort(null, 1);
224    }
225
226    property Field filterField { set { filterField = value; } }
227
228    bool OnKeyHit(Key key, unichar ch)
229    {
230       if((SmartKey)key == del)
231       {
232          SelectRow(null); //currentRow = null;
233          return false;
234       }
235       else if((SmartKey)key == enter)
236          parent.CycleChildren(true, false, false, true);
237
238       return DropBox::OnKeyHit(key, ch);
239    }
240
241    bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
242    {
243       modifiedDocument = true;
244       return true;
245    }
246
247    bool OnCreate()
248    {
249       Refill();
250       return true;
251    }
252
253    void EditNotifyCharsAdded()
254    {
255       if(!editBox.NotifyUpdate || editBox.NotifyUpdate == EditBox::NotifyUpdate)
256          editBox.NotifyUpdate = EditNotifyUpdate;
257    }
258
259    void EditNotifyUpdate(EditBox editBox)
260    {
261       DataRow row;
262       char * contents = editBox.contents;
263       int len = strlen(contents);
264       if(len && editBox.charPos == len)
265       {
266          EditLine line = editBox.firstLine;
267          int x1, x2;
268
269          editBox.GetSelPos(null, null, &x1, null, null, &x2, false);
270          if(x2 == x1)
271          {
272             for(row = firstRow; row; row = row.next)
273             {
274                char * string = row.string;
275                if(string && SearchString(string, 0, contents, false, false) == string)
276                {
277                   // SelectRow(row);
278                   editBox.contents = row.string;
279                   editBox.SetSelPos(line, 0, len, line, 0, strlen(string));
280                   break;
281                }
282             }
283          }
284       }
285       editBox.NotifyUpdate = null;
286    }
287 }
288
289 public class DropDataBox : DataBox
290 {
291    size.h = 24;
292    keepEditor = true;
293    borderStyle = deep;
294    showNone = false; //true;
295
296 public:
297
298    property uint filter { set { filtered = true; filter = value; } get { return filter; } }
299    property bool filtered { set { filtered = value; } }
300    property uint exclusion { set { exclusion = value; } }
301    property Field filterField { set { filterField = value; } }
302    property Field nameField { set { nameField = value; } }
303    virtual void TableDropBox::RefillFunction();
304    property bool showNone { set { showNone = value; } }
305
306    void Refill()
307    {
308       if(editor)
309       {
310          TableDropBox dropBox = (TableDropBox) editor;
311          uint id = data ? *(uint *)data : MAXDWORD;
312          void * notifyChanged = (void *)NotifyChanged;
313
314          OnConfigure(dropBox);
315
316          NotifyChanged = null;
317          dropBox.Refill();
318          NotifyChanged = notifyChanged;
319
320          if(id != MAXDWORD) dropBox.SelectRow(dropBox.FindSubRow(id));
321       }
322    }
323
324    void OnConfigure(TableDropBox dropBox)
325    {
326       if(RefillFunction != DropDataBox::RefillFunction) dropBox.Refill = RefillFunction;
327       if(nameField) dropBox.nameField = nameField;
328       if(filterField) dropBox.filterField = filterField;
329       if(filtered)
330          dropBox.filter = filter;
331       else
332          dropBox.filtered = false;
333
334       dropBox.exclusion = exclusion;
335       dropBox.showNone = showNone;
336    }
337
338 private:
339    Field nameField;
340    Field filterField;
341    uint exclusion;
342    uint filter;
343    bool filtered;
344    bool showNone;
345 }
346
347 public class EditDropDataBox : DropDataBox
348 {
349    void OnConfigure(TableDropBox dropBox)
350    {
351       DropDataBox::OnConfigure(dropBox);
352       dropBox.editText = true;
353       dropBox.editBox.NotifyCharsAdded = (void *)TableDropBox::EditNotifyCharsAdded;
354    }
355 }
356
357 public class FieldDataBox : DataBox
358 {
359    size = { 100, 22 };
360    anchor = { left = 110, right = shadowS + sgs * 2 };
361    borderStyle = deep;
362
363 public:
364    property Row row
365    {
366       set { row = value; }
367       get { return row; }
368    }
369    property EditSection editor
370    {
371       set
372       {
373          parent = value.editArea;
374          master = value;
375          value.AddFieldEditor(this);
376          row = value.editRow;
377       }
378    };
379
380    property Field field
381    {
382       set
383       {
384          Class dataType;
385          if(field) type = null;
386
387          if(dataHolder)
388          {
389             ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, (void *)dataHolder);
390             if(type.type == structClass)
391             {
392                void * dataPtr = (void *)dataHolder;
393                delete dataPtr;
394             }
395             dataHolder = 0;
396          }
397
398          field = value;
399          dataType = value ? value.type : null;
400          if(!text)
401             text = field ? field.name : null;
402
403          if(dataType && dataType.type == structClass)
404          {
405             dataHolder = (int64)new0 byte[dataType.structSize];
406             data = (void *)dataHolder;
407          }
408          else if(dataType && (dataType.type == noHeadClass || dataType.type == normalClass))
409          {
410             if(eClass_IsDerived(dataType, class(String)))
411                dataHolder = (int64)CopyString("");
412             else
413                dataHolder = (int64)eInstance_New(dataType);
414             data = (void *)&dataHolder;
415          }
416          else
417          {
418             dataHolder = 0;
419             data = &dataHolder;
420          }
421          if(!type) type = dataType;
422       }
423    }
424
425    void Clear()
426    {
427       if(visible)
428       {
429          if(data)
430             SetData(null, false);
431
432          if(type && (type.type == noHeadClass || type.type == normalClass))
433          {
434             if(eClass_IsDerived(type, class(String)))
435                dataHolder = (int64)CopyString("");
436             else
437                dataHolder = (int64)eInstance_New(type);
438             data = (void *)&dataHolder;
439          }
440
441          if(created)
442             Refresh();
443       }
444    }
445
446    void Load()
447    {
448       if(visible && 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(visible && 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(visible && 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(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, 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(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 }