3 define sgs = 3; // screen sgs size
7 extern int __ecereVMethodID_class_OnFree;
8 extern int __ecereVMethodID_class_OnGetString;
11 char * defaultNameField = "Name";
12 char * defaultIdField = "Id";
13 char * defaultActiveField = "Active";
15 public void SetDefaultIdField(char * value) { defaultIdField = value; }
16 public void SetDefaultNameField(char * value) { defaultNameField = value; }
18 public class ButtonStyle : Button
20 font = { $"Arial", 10, bold = true };
21 creationActivation = doNothing;
24 public class Group : Window
28 //inactive = true; // TOFIX causes problems...
30 public Label title { this, font = { $"Arial", 10, bold = true }, position = { 16, 2 } };
34 title.labeledWindow = this;
38 void OnRedraw(Surface surface)
40 int x = clientSize.w - 1, y = clientSize.h - 1;
42 surface.SetBackground(gray);
44 surface.Rectangle(0, 10, x - shadowS, y - shadowS);
45 surface.Area(shadowS / 2, y - shadowS + 1, x, y);
46 surface.Area(x - shadowS + 1, 10 + shadowS / 2, x, y);
48 surface.SetBackground(white);
50 surface.Rectangle(10, 0, title.size.w + 22, title.size.h + 4);
51 surface.Area(11, 1, title.size.w + 21, title.size.h + 3);
55 public class CheckBool : bool
57 Window OnEdit(DataBox dataBox, DataBox obsolete, int x, int y, int w, int h, void * userData)
59 if(this || !this) { // FIXME
60 Button button = dataBox.keepEditor ? (Button)obsolete : null;
65 dataBox, borderStyle = 0, text = dataBox.text, anchor = { 0, 0, 0, 0 },
66 // size = { 100, 22 };
67 modifyVirtualArea = false, isCheckbox = true;
69 bool DataBox::NotifyClicked(Button control, int x, int y, Modifiers mods)
71 bool checked = control.checked;
72 SetData(&checked, false);
77 button.checked = this;
85 String GetNameString(Row r, Field nameField)
88 if(nameField.type != class(String) && nameField.type != class(char *))
90 char tempString[4096];
91 Class type = nameField.type;
93 if(type.type == structClass)
94 data = (int64)new0 byte[type.structSize];
95 ((bool (*)())(void *)r.GetData)(r, nameField, type, (type.type == structClass) ? (void *)data : &data);
96 s = CopyString((String)type._vTbl[__ecereVMethodID_class_OnGetString](type, (void *)data, tempString, null, null));
97 type._vTbl[__ecereVMethodID_class_OnFree](type, data);
98 if(type.type == structClass)
100 void * _data = (void *)data;
105 r.GetData(nameField, s);
109 public class TableDropBox : DropBox
111 anchor = { left = 130, top = 180, right = shadowS + sgs * 2 };
115 public bool filtered;
116 public Field nameField;
117 public uint exclusion;
119 public Field filterField;
121 public property uint filter
128 get { return filter; }
130 public property Field nameField { set { nameField = value; } }
131 public property uint exclusion { set { exclusion = value; } }
132 public property Table table { set { table = value; if(!nameField && value) nameField = value.FindField(defaultNameField); } }
134 public virtual void Refill()
139 Field fldId = table.FindField(defaultIdField);
140 if(fldId && nameField)
143 if(filterField && filtered)
145 if(eClass_IsDerived(filterField.type, class(Id)))
147 FieldIndex indexedFields[1];
148 // Table tbl = table.db.OpenTable(table.name, { tableRows });
149 char * name = table.name;
150 Database db = table.db;
151 Table tbl = db.OpenTable(name, { tableRows });
156 indexedFields[0] = { filterField };
157 tbl.GenerateIndex(1, indexedFields, false);
161 for(r.Find(filterField, middle, nil, filter); !r.nil; r.Next()) //while(r.Next())
165 r.GetData(filterField, idFilter);
166 r.GetData(fldId, id);
167 if(idFilter != filter)
169 if(!exclusion || id != exclusion)
171 String s = GetNameString(r, nameField);
172 AddString(s).tag = id;
178 else if(eClass_IsDerived(filterField.type, class(IdList)))
184 r.GetData(filterField, idList);
185 r.GetData(fldId, id);
186 if(idList && idList.Includes(filter) && (!exclusion || !idList.Includes(exclusion)))
188 String s = GetNameString(r, nameField);
189 AddString(s).tag = id;
201 r.GetData(fldId, id);
204 String s = GetNameString(r, nameField);
205 AddString(s).tag = id;
215 String s = GetNameString(r, nameField);
216 r.GetData(fldId, id);
217 AddString(s).tag = id;
227 public property Field filterField { set { filterField = value; } }
229 bool OnKeyHit(Key key, unichar ch)
231 if((SmartKey)key == del)
233 SelectRow(null); //currentRow = null;
236 else if((SmartKey)key == enter)
237 parent.CycleChildren(true, false, false, true);
239 return DropBox::OnKeyHit(key, ch);
242 bool NotifySelect(DropBox dropBox, DataRow row, Modifiers mods)
244 modifiedDocument = true;
254 public void EditNotifyCharsAdded()
256 if(!editBox.NotifyUpdate || editBox.NotifyUpdate == EditBox::NotifyUpdate)
257 editBox.NotifyUpdate = EditNotifyUpdate;
260 public void EditNotifyUpdate(EditBox editBox)
263 char * contents = editBox.contents;
264 int len = strlen(contents);
265 if(len && editBox.charPos == len)
267 EditLine line = editBox.firstLine;
270 editBox.GetSelPos(null, null, &x1, null, null, &x2, false);
273 for(row = firstRow; row; row = row.next)
275 char * string = row.string;
276 if(string && SearchString(string, 0, contents, false, false) == string)
279 editBox.contents = row.string;
280 editBox.SetSelPos(line, 0, len, line, 0, strlen(string));
286 editBox.NotifyUpdate = null;
290 public class DropDataBox : DataBox
302 showNone = false; //true;
304 public property uint filter { set { filtered = true; filter = value; } get { return filter; } }
305 public property bool filtered { set { filtered = value; } }
306 public property uint exclusion { set { exclusion = value; } }
307 public property Field filterField { set { filterField = value; } }
308 public property Field nameField { set { nameField = value; } }
309 public virtual void TableDropBox::RefillFunction();
310 public property bool showNone { set { showNone = value; } }
316 TableDropBox dropBox = (TableDropBox) editor;
317 uint id = data ? *(uint *)data : MAXDWORD;
318 void * notifyChanged = (void *)NotifyChanged;
320 OnConfigure(dropBox);
322 NotifyChanged = null;
324 NotifyChanged = notifyChanged;
326 if(id != MAXDWORD) dropBox.SelectRow(dropBox.FindSubRow(id));
330 void OnConfigure(TableDropBox dropBox)
332 if(RefillFunction != DropDataBox::RefillFunction) dropBox.Refill = RefillFunction;
333 if(nameField) dropBox.nameField = nameField;
334 if(filterField) dropBox.filterField = filterField;
336 dropBox.filter = filter;
338 dropBox.filtered = false;
340 dropBox.exclusion = exclusion;
341 dropBox.showNone = showNone;
345 public class EditDropDataBox : DropDataBox
347 void OnConfigure(TableDropBox dropBox)
349 DropDataBox::OnConfigure(dropBox);
350 dropBox.editText = true;
351 dropBox.editBox.NotifyCharsAdded = (void *)TableDropBox::EditNotifyCharsAdded;
355 public class FieldDataBox : DataBox
358 anchor = { left = 110, right = shadowS + sgs * 2 };
362 int64 dataHolder; // THERE SEEMS TO BE A BUG WHEN ACCESSING row ACROSS .so
365 public property Row row
370 public property EditSection editor
374 parent = value.editArea;
376 value.AddFieldEditor(this);
381 public property Field field
386 if(field) type = null;
390 type._vTbl[__ecereVMethodID_class_OnFree](type, dataHolder);
391 if(type.type == structClass)
393 void * dataPtr = (void *)dataHolder;
400 dataType = value ? value.type : null;
402 text = field ? field.name : null;
404 if(dataType && dataType.type == structClass)
406 dataHolder = (int64)new0 byte[dataType.structSize];
407 data = (void *)dataHolder;
409 else if(dataType && (dataType.type == noHeadClass || dataType.type == normalClass))
411 if(eClass_IsDerived(dataType, class(String)))
412 dataHolder = (int64)CopyString("");
414 dataHolder = (int64)eInstance_New(dataType);
415 data = (void *)&dataHolder;
422 if(!type) type = dataType;
431 SetData(null, false);
433 if(type && (type.type == noHeadClass || type.type == normalClass))
435 if(eClass_IsDerived(type, class(String)))
436 dataHolder = (int64)CopyString("");
438 dataHolder = (int64)eInstance_New(type);
439 data = (void *)&dataHolder;
449 if(visible && field && row)
451 SetData(null, false);
452 master.modifiedDocument = false;
454 ((bool (*)())(void *)Row::GetData)(row, field, field.type, data);
456 if(!dataHolder && type && (type.type == noHeadClass || type.type == normalClass))
458 if(eClass_IsDerived(type, class(String)))
459 dataHolder = (int64)CopyString("");
461 dataHolder = (int64)eInstance_New(type);
462 data = (void *)&dataHolder;
470 public virtual void Save()
473 if(visible && field && row)
475 Class type = field.type;
476 if(!DataBox::SaveData())
479 ((bool (*)())(void *)Row::SetData)(row, field, type,
480 (type.type == noHeadClass || type.type == normalClass) ? *(void **)data : data);
486 if(visible && created)
490 bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
494 if(modifiedDocument && !DataBox::SaveData())
500 bool OnKeyDown(Key key, unichar ch)
502 if((SmartKey)key == enter)
504 DataBox::OnKeyDown(key, ch);
508 return DataBox::OnKeyDown(key, ch);
511 bool OnKeyHit(Key key, unichar ch)
513 if((SmartKey)key == enter)
514 parent.CycleChildren(true, false, false, true);
516 return DataBox::OnKeyHit(key, ch);
519 public bool Window::NotifyChanged(bool closingDropDown)
521 modifiedDocument = true;
525 public bool Window::NotifyModified()
527 modifiedDocument = true;
534 SetData(null, false);
538 type._vTbl[__ecereVMethodID_class_OnFree](type, dataHolder);
539 if(type.type == structClass)
541 void * dataPtr = (void *)dataHolder;
549 public class FieldCheckButton : FieldDataBox
552 type = class(CheckBool);
553 // TOCHECK: When not here, the virtual area goes wild (anchor.right is not reset)
557 public class FieldDropDataBox : FieldDataBox
570 public property uint filter { set { filtered = true; filter = value; } get { return filter; } }
571 public property bool filtered { set { filtered = value; } }
572 public property uint exclusion { set { exclusion = value; } }
573 public property Field filterField { set { filterField = value; } }
574 public property Field nameField { set { nameField = value; } }
575 public virtual void TableDropBox::RefillFunction();
576 public property bool showNone { set { showNone = value; } }
582 TableDropBox dropBox = (TableDropBox) editor;
583 uint id = data ? *(uint *)data : MAXDWORD;
584 OnConfigure(dropBox);
586 if(id != MAXDWORD) dropBox.SelectRow(dropBox.FindSubRow(id));
590 void OnConfigure(TableDropBox dropBox)
592 if(RefillFunction != FieldDropDataBox::RefillFunction) dropBox.Refill = RefillFunction;
593 if(nameField) dropBox.nameField = nameField;
594 if(filterField) dropBox.filterField = filterField;
596 dropBox.filter = filter;
598 dropBox.filtered = false;
599 dropBox.exclusion = exclusion;
600 dropBox.showNone = showNone;
604 public class EditFieldDropDataBox : FieldDropDataBox
608 void OnConfigure(TableDropBox dropBox)
610 FieldDropDataBox::OnConfigure(dropBox);
611 dropBox.editText = true;
612 dropBox.editBox.NotifyCharsAdded = (void *)TableDropBox::EditNotifyCharsAdded;
617 TableDropBox dropBox = (TableDropBox) editor;
619 if(!dropBox.currentRow && dropBox.contents[0])
621 Row row { dropBox.table };
623 OnAddTextEntry(row, dropBox, dropBox.contents);
628 dropBox.SelectRow(dropBox.FindSubRow(sysID));
630 FieldDataBox::Save();
633 public virtual bool OnAddTextEntry(Row row, TableDropBox dropBox, char * entry)
636 row.SetData(dropBox.nameField, entry);
641 public class ListSection : Group
645 anchor = { left = sgs, top = 32 + sgs * 3, bottom = 55 + sgs * 3 };
649 public property EditSection editor
658 public property Table table
667 FieldIndex indexedFields[1];
669 if(!fldId) fldId = table.FindField(defaultIdField);
670 if(!fldName) fldName = table.FindField(defaultNameField);
671 if(!fldActive) fldActive = table.FindField(defaultActiveField);
673 indexedFields[0] = { fldId };
674 table.Index(1, indexedFields);
676 editor.editRow.tbl = table;
684 public Field fldId, fldName, fldActive;
686 public virtual DialogResult Window::NotifySaveConfirmation(ListSection listSection)
688 return MessageBox { master = this, type = yesNoCancel, text = $"List Editor", contents = $"You have modified this entry. Would you like to save it before proceeding?" }.Modal();
691 bool OnClose(bool parentClosing)
693 if(editor && editor.modifiedDocument)
695 switch(NotifySaveConfirmation(master, this))
702 editor.modifiedDocument = false;
709 public void RefillList()
712 //if(fldId && fldName)
715 NotifyRefillList(master, this, r);
719 editor.modifiedDocument = false;
722 public virtual void Window::NotifyRefillList(ListSection listSection, Row r)
724 if(listSection.fldId && listSection.fldName)
726 bool stringName = !strcmp(listSection.fldName.type.dataTypeString, "char *");
731 r.GetData(listSection.fldId, id);
733 r.GetData(listSection.fldName, s);
735 s = PrintString("Entry ", id);
736 listSection.list.AddString(s).tag = id;
742 public virtual bool Window::NotifyNew(ListSection listSection, Row r);
744 public ButtonStyle btnNew
746 this, anchor = { right = shadowS + sgs * 2, top = 24 }, hotKey = altW, text = $"New";
748 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
750 list.NotifySelect(this, list, null, 0);
751 if(!editor.modifiedDocument)
753 uint id; // = table.rowsCount + 1; // this is bad with deleted rows, won't work, how to have unique id?
756 if(r.Last()) // this will reuse ids in cases where the item(s) with the last id have been deleted
758 r.GetData(fldId, id);
769 // Patch for SQLite driver which auto-increments IDs
771 if(r.GetData(fldId, curID))
774 r.SetData(fldId, id);
776 if(!strcmp(fldName.type.dataTypeString, "char *"))
777 r.SetData(fldName, $"[New]");
780 r.SetData(fldActive, active);
782 if(NotifyNew(master, this, r))
783 list.currentRow = list.AddString($"[New]");
788 list.currentRow.tag = id;
789 SelectListRow(list.currentRow);
796 public virtual bool Window::NotifyDeleteConfirmation(ListSection listSection)
798 return MessageBox { master = this, type = yesNo, text = $"List Editor",
799 contents = $"You are about to delete an entry.\n"
800 "Do you wish to continue?"
804 public virtual void Window::NotifyDeleting(ListSection listSection);
805 public virtual void Window::NotifyDeleted(ListSection listSection);
807 public ButtonStyle btnDelete
809 this, anchor = { right = shadowS + sgs * 2, top = 24 }, hotKey = altD, text = $"Delete";
811 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
815 if(NotifyDeleteConfirmation(master, this))
817 NotifyDeleting(master, this);
819 editor.editRow.Delete();
820 list.DeleteRow(list.currentRow);
823 NotifyDeleted(master, this);
825 SelectListRow(list.currentRow);
833 public bool FilterNotifyChanged(bool closeDropDown)
838 SelectListRow(list.firstRow);
846 this, anchor = { left = sgs * 2, top = 22 + 22 + sgs * 4, right = shadowS + sgs * 2, bottom = shadowS + sgs * 2 };
847 alwaysHighLight = true;
849 bool NotifySelect(ListBox listBox, DataRow row, Modifiers mods)
852 if(/*row && */row != lastRow)
855 if(editor.modifiedDocument)
858 list.currentRow = lastRow;
860 switch(NotifySaveConfirmation(master, this))
867 editor.modifiedDocument = false;
868 list.currentRow = row;
878 public virtual void Window::NotifySelectListRow(ListSection listSection, uint id);
880 public void SelectListRow(DataRow row)
882 // Time startTime = GetTime();
888 if(list.currentRow != row)
889 list.currentRow = row;
890 if(editor.editRow.Find(fldId, middle, nil, id))
892 editor.listRow = row;
893 NotifySelectListRow(master, this, id);
897 // Logf("SelectListRow took %f seconds\n", GetTime() - startTime);
900 public void SelectFirst()
903 SelectListRow(list.firstRow);
907 public void RefreshState()
911 editor.btnSave.disabled = !(bool)list.currentRow;
912 editor.btnReload.disabled = !(bool)list.currentRow;
913 btnDelete.disabled = !(bool)list.currentRow;
914 editor.disabled = !(bool)list.firstRow;
918 void OnResize(int width, int height)
920 int x = width - btnDelete.size.w - 20;
922 btnDelete.position.x = x;
924 btnNew.position.x = x = x - btnNew.size.w - sgs * 2;
927 bool OnPostCreate(void)
929 OnResize(clientSize.w, clientSize.h);
931 if(editor) editor.modifiedDocument = false;
932 return Window::OnPostCreate();
937 public class EditSection : Group
942 anchor = { right = sgs, top = 32 + sgs * 3, bottom = 55 + sgs * 3 };
946 editBoxes.Free(null);
967 OldList editBoxes { };
969 public Window editArea { this, borderStyle = deep, tabCycle = true, anchor = { left = 8, top = 54, right = 10, bottom = 10 }, hasVertScroll = true, dontHideScroll = true };
971 public ButtonStyle btnSave
973 this, anchor = { right = shadowS + sgs * 2, top = 24 }, hotKey = altV, text = $"Save";
975 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
982 public ButtonStyle btnReload
984 this, anchor = { left = 10, top = 24 }, hotKey = altV, text = $"Revert";
986 bool NotifyClicked(Button button, int x, int y, Modifiers mods)
994 void AddFieldEditor(FieldDataBox box)
996 editBoxes.Add(OldLink { data = box });
999 virtual void Window::NotifyInitFields(EditSection editSection);
1004 for(link = editBoxes.first; link; link = link.next)
1006 FieldDataBox dataBox = link.data;
1009 NotifyInitFields(master, this);
1014 modifiedDocument = false;
1017 virtual void Window::NotifyEditSave(EditSection edit, String name)
1019 edit.listRow.string = name;
1024 bool stringName = !strcmp(list.fldName.type.dataTypeString, "char *");
1028 editRow.tbl.db.Begin();
1029 for(link = editBoxes.first; link; link = link.next)
1031 FieldDataBox dataBox = link.data;
1034 editRow.tbl.db.Commit();
1035 // ADDED THIS HERE FOR SQLITE TO REFRESH
1036 editRow.Find(list.fldId, middle, nil, list.list.currentRow.tag);
1039 editRow.GetData(list.fldName, name);
1041 name = PrintString("Entry ", list.list.currentRow.tag);
1043 NotifyEditSave(master, this, name);
1045 list.list.Sort(null, 1);
1046 list.list.currentRow = list.list.currentRow;
1048 modifiedDocument = false;
1051 virtual void Window::NotifyEditLoad(EditSection editSection);
1056 for(link = editBoxes.first; link; link = link.next)
1058 FieldDataBox dataBox = link.data;
1061 NotifyEditLoad(master, this);
1063 modifiedDocument = false;
1066 virtual void Window::NotifyEditClear(EditSection editSection);
1071 for(link = editBoxes.first; link; link = link.next)
1073 FieldDataBox dataBox = link.data;
1074 editRow.Select(nil);
1077 NotifyEditClear(master, this);
1078 modifiedDocument = false;
1083 // TO CHECK: Why is there a jump in the scroll thumb size when this is not here?
1086 modifiedDocument = false;