ide/CodeEditor: Customizable color scheme support
[sdk] / ide / src / designer / Sheet.ec
1 import "ide"
2
3 import "CodeObject"
4
5 static __attribute__((unused)) void UnusedFunction()
6 {
7    int a = 0;
8    Module b = 0;
9    a.OnGetString(0,0,0);
10    a.OnFree();
11    a.OnCopy(null);
12    a.OnCompare(null);
13    a.OnSaveEdit(null,0);
14    a.OnEdit(null,null,0,0,0,0,0);
15    a.OnDisplay(null,0,0,0,0,0,0);
16    b.OnLoad();
17 }
18
19 extern int __ecereVMethodID_class_OnEdit;
20 extern int __ecereVMethodID_class_OnDisplay;
21 extern int __ecereVMethodID_class_OnGetString;
22 extern int __ecereVMethodID_class_OnFree;
23 extern int __ecereVMethodID_class_OnCompare;
24 extern int __ecereVMethodID_class_OnCopy;
25 extern int __ecereVMethodID_class_OnSaveEdit;
26
27 //#define SHOW_METHODS
28
29
30 // *** THESE METHODS SHOULD BE IMPROVED UPON AND USED TO SET PROPERTIES FROM NOW ON ***
31 //     (Other locations where this will be useful: JSON (DataValue version?), CodeEditor, ...)
32 // This takes in a value according to the any_object rules
33
34 void SetPropValue(Property prop, void * object, any_object value)
35 {
36    Class type = prop.dataTypeClass;
37    if(!type)
38       type = prop.dataTypeClass = eSystem_FindClass(prop._class.module, prop.dataTypeString);
39
40    if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
41    {
42       ((void (*)(void *, void *))(void *)prop.Set)(object, value);
43    }
44    // TOFIX: How to swiftly handle classes with base data type?
45    else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
46    {
47       ((void (*)(void *, double))(void *)prop.Set)(object, *(double *)value);
48    }
49    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
50    {
51       ((void (*)(void *, float))(void *)prop.Set)(object, *(float *)value);
52    }
53    else if(type.typeSize == sizeof(int64))// || !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
54    {
55       ((void (*)(void *, int64))(void *)prop.Set)(object, *(int64 *)value);
56    }
57    else if(type.typeSize == sizeof(int))// || !strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
58    {
59       ((void (*)(void *, int))(void *)prop.Set)(object, *(int *)value);
60    }
61    else if(type.typeSize == sizeof(short int)) // || !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||  !strcmp(type.dataTypeString, "int16"))
62    {
63       ((void (*)(void *, short))(void *)prop.Set)(object, *(short *)value);
64    }
65    else if(type.typeSize == sizeof(byte))// || !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
66    {
67       ((void (*)(void *, byte))(void *)prop.Set)(object, *(byte *)value);
68    }
69    else
70    {
71       ((void (*)(void *, void *))(void *)prop.Set)(object, value);
72    }
73 }
74
75 /*
76 any_object GetPropValue(Property prop, Instance object)
77 {
78    if(object)
79    {
80       Class type = prop.dataTypeClass;
81       if(!type)
82       {
83          type = prop.dataTypeClass = eSystem_FindClass(prop._class.module, prop.dataTypeString);
84       }
85
86       if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
87       {
88          return ((void*(*)())(void *)prop.Get)(object);
89       }
90       // TOFIX: How to swiftly handle classes with base data type?
91       else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
92       {
93          // NOTE: must return double by reference
94          double d = ((double(*)(void *))(void *)prop.Get)(object);
95          return d;
96       }
97       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
98       {
99          // NOTE: must return float by reference
100          float f =((float(*)(void *))(void *)prop.Get)(object);
101          return f;
102       }
103       else if(type.typeSize == sizeof(int64))// || !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
104       {
105          return ((int64(*)(void *))(void *)prop.Get)(object);
106       }
107       else if(type.typeSize == sizeof(int))// || !strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
108       {
109          return ((int(*)(void *))(void *)prop.Get)(object);
110       }
111       else if(type.typeSize == sizeof(short int)) // || !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||  !strcmp(type.dataTypeString, "int16"))
112       {
113          return ((short(*)(void *))(void *)prop.Get)(object);
114       }
115       else if(type.typeSize == sizeof(byte))// || !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
116       {
117          return ((byte(*)(void *))(void *)prop.Get)(object);
118       }
119       else
120       {
121          return ((int (*)(void *))(void *)prop.Get)(object);
122       }
123    }
124    else
125       return 0;
126 }
127 */
128 void CopyProperty(Property prop, Instance dest, Instance src)
129 {
130    Class type = prop.dataTypeClass;
131    if(!type)
132       type = prop.dataTypeClass = eSystem_FindClass(prop._class.module, prop.dataTypeString);
133
134    if(type.type == structClass)
135    {
136       void * propData = new0 byte[type.structSize];
137       ((void (*)(void *, void *))(void *)prop.Get)(src, propData);
138       ((void (*)(void *, void *))(void *)prop.Set)(dest, propData);
139       delete propData;
140    }
141    else if(type.type == normalClass || type.type == noHeadClass)
142    {
143       // TOCHECK: Why was there a return here?
144       /*return */((void (*)(void *, void *))(void *)prop.Set)(dest, ((void*(*)(void *))(void *)prop.Get)(src));
145    }
146    // TOFIX: How to swiftly handle classes with base data type?
147    else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
148    {
149       ((void (*)(void *, double))(void *)prop.Set)(dest, ((double(*)(void *))(void *)prop.Get)(src));
150    }
151    else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
152    {
153       ((void (*)(void *, float))(void *)prop.Set)(dest, ((float(*)(void *))(void *)prop.Get)(src));
154    }
155    else if(type.typeSize == sizeof(int64))// || !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
156    {
157       ((void (*)(void *, int64))(void *)prop.Set)(dest, ((int64(*)(void *))(void *)prop.Get)(src));
158    }
159    else if(type.typeSize == sizeof(int))// || !strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
160    {
161       ((void (*)(void *, int))(void *)prop.Set)(dest, ((int(*)(void *))(void *)prop.Get)(src));
162    }
163    else if(type.typeSize == sizeof(short int)) // || !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||  !strcmp(type.dataTypeString, "int16"))
164    {
165       ((void (*)(void *, short))(void *)prop.Set)(dest, ((short(*)(void *))(void *)prop.Get)(src));
166    }
167    else if(type.typeSize == sizeof(byte))// || !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
168    {
169       ((void (*)(void *, byte))(void *)prop.Set)(dest, ((byte(*)(void *))(void *)prop.Get)(src));
170    }
171    else
172    {
173       ((void (*)(void *, int))(void *)prop.Set)(dest, ((int (*)(void *))(void *)prop.Get)(src));
174    }
175 }
176
177 void GetProperty(Property prop, Instance object, DataValue value)
178 {
179    if(object)
180    {
181       Class type = prop.dataTypeClass;
182       if(!type)
183       {
184          type = prop.dataTypeClass = eSystem_FindClass(prop._class.module, prop.dataTypeString);
185 #ifdef _DEBUG
186          if(prop._class.module.application == __thisModule &&
187             prop.dataTypeClass.module.application == ((Designer)GetActiveDesigner()).codeEditor.privateModule)
188             printf("Warning");
189 #endif
190       }
191
192       if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
193       {
194          value.p = ((void*(*)(void *))(void *)prop.Get)(object);
195       }
196       // TOFIX: How to swiftly handle classes with base data type?
197       else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
198       {
199          value.d = ((double(*)(void *))(void *)prop.Get)(object);
200       }
201       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
202       {
203          value.f = ((float(*)(void *))(void *)prop.Get)(object);
204       }
205       else if(type.typeSize == sizeof(int64))// || !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
206       {
207          value.i64 = ((int64(*)(void *))(void *)prop.Get)(object);
208       }
209       else if(type.typeSize == sizeof(int))// || !strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
210       {
211          value.i = ((int(*)(void *))(void *)prop.Get)(object);
212       }
213       else if(type.typeSize == sizeof(short int)) // || !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||  !strcmp(type.dataTypeString, "int16"))
214       {
215          value.s = ((short(*)(void *))(void *)prop.Get)(object);
216       }
217       else if(type.typeSize == sizeof(byte))// || !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
218       {
219          value.uc = ((byte(*)(void *))(void *)prop.Get)(object);
220       }
221       else
222       {
223          value.i = ((int (*)(void *))(void *)prop.Get)(object);
224       }
225    }
226    else
227       value.i64 = 0;
228 }
229
230 void SetProperty(Property prop, Instance object, DataValue value)
231 {
232    if(object)
233    {
234       Class type = prop.dataTypeClass;
235       if(!type)
236          type = prop.dataTypeClass = eSystem_FindClass(prop._class.module, prop.dataTypeString);
237
238       if(type.type == normalClass || type.type == noHeadClass || type.type == structClass)
239       {
240          ((void (*)(void *, void *))(void *)prop.Set)(object, value);
241       }
242       // TOFIX: How to swiftly handle classes with base data type?
243       else if(type == class(double) || !strcmp(type.dataTypeString, "double"))
244       {
245          ((void (*)(void *, double))(void *)prop.Set)(object, value.d);
246       }
247       else if(type == class(float) || !strcmp(type.dataTypeString, "float"))
248       {
249          ((void (*)(void *, float))(void *)prop.Set)(object, value.f);
250       }
251       else if(type.typeSize == sizeof(int64))// || !strcmp(type.dataTypeString, "int64") || !strcmp(type.dataTypeString, "unsigned int64") || !strcmp(type.dataTypeString, "uint64"))
252       {
253          ((void (*)(void *, int64))(void *)prop.Set)(object, value.i64);
254       }
255       else if(type.typeSize == sizeof(int))// || !strcmp(type.dataTypeString, "int") || !strcmp(type.dataTypeString, "unsigned int") || !strcmp(type.dataTypeString, "uint"))
256       {
257          ((void (*)(void *, int))(void *)prop.Set)(object, value.i);
258       }
259       else if(type.typeSize == sizeof(short int)) // || !strcmp(type.dataTypeString, "short") || !strcmp(type.dataTypeString, "unsigned short") || !strcmp(type.dataTypeString, "uint16") ||  !strcmp(type.dataTypeString, "int16"))
260       {
261          ((void (*)(void *, short))(void *)prop.Set)(object, value.s);
262       }
263       else if(type.typeSize == sizeof(byte))// || !strcmp(type.dataTypeString, "char") || !strcmp(type.dataTypeString, "unsigned char") || !strcmp(type.dataTypeString, "byte"))
264       {
265          ((void (*)(void *, byte))(void *)prop.Set)(object, value.uc);
266       }
267       else
268       {
269          ((void (*)(void *, int))(void *)prop.Set)(object, value.i);
270       }
271    }
272 }
273
274 class Sheet : Window
275 {
276    text = $"Sheet";
277    borderStyle = sizable;
278    hasClose = true;
279    //tabCycle = true;
280    size.w = 300;
281    anchor = { left = 0, top = 0, bottom = 0 };
282    background = formColor;
283
284    Sheet()
285    {
286       dropBox.AddField(dropField);
287       properties.AddField(propertyName);
288       properties.AddField(propertyValue);
289       methods.AddField(methodName);
290    }
291
292    ~Sheet()
293    {
294       categories.Free(null);
295    }
296
297    DropBox dropBox
298    {
299       this,
300       anchor = { left = 0, top = 0, right = 0 };
301
302       bool NotifySelect(DropBox control, DataRow row, Modifiers keyFlags)
303       {
304          ObjectInfo selected = (ObjectInfo)(row ? (void *)(intptr)row.tag : null);
305          ToolBox toolBox = ((IDEWorkSpace)parent).toolBox;
306
307          if(codeEditor && selected)
308             codeEditor.SelectObject(selected);
309
310          // TODO: Get containing class of object
311          toolBox.selectedClass = selected ? selected.oClass : null;
312
313          object = selected ? selected.instance : null;
314
315          methods.Clear();
316          ListProperties(true);
317
318          {
319             DataRow row;
320             row = methods.currentRow;
321             if(row)
322                strcpy(selectedMethod, ((CodeObject)row.GetData(methodName)).name);
323          }
324
325
326          if(selected && selected.instance && codeEditor)
327          {
328             Class _class;
329             int rowHeight = methods.rowHeight;
330
331             propertyValue.userData = (void *)selected.instance;
332
333             // Fill up the methods
334             {
335                CodeObjectType type;
336                {
337                   for(_class = selected.instance._class; _class && _class.type != systemClass; _class = _class.base)
338                   {
339                      int id;
340                      for(id = _class.base ? _class.base.vTblSize : 0; id<_class.vTblSize; id++)
341                      {
342                         Method method;
343                         for(method = (Method)_class.methods.first; method; method = (Method)((BTNode)method).next)
344                         {
345                            if(method.type == virtualMethod && method.vid == id)
346                            {
347                               if(!method.dataType)
348                                  method.dataType = ProcessTypeString(method.dataTypeString, false);
349
350                               type = method.dataType.thisClass ? typeEvent : typeMethod;
351                               {
352                                  DataRow row = methods.AddRow();
353                                  CodeObject codeObject
354                                  {
355                                     eventsUp = (selected.oClass == selected) ? false : true;
356                                     type = type;
357                                     method = method;
358                                     name = method.name;
359                                     overriden = codeEditor.FindMethod(method.name, &codeObject.function, null);
360                                  };
361                                  if(!codeObject.overriden || codeObject.overriden == 2)
362                                     codeEditor.FindCompatibleMethods(method, codeObject.compatible);
363
364                                  row.SetData(methodName, codeObject);
365
366                                  if(!strcmp(method.name, selectedMethod))
367                                     methods.currentRow = row;
368                               }
369                            }
370                         }
371                      }
372                   }
373                }
374             }
375             methods.Sort(methodName, 1);
376             {
377                DataRow row;
378                for(row = methods.firstRow; row; row = row.next)
379                {
380                   CodeObject codeObject = row.GetData(methodName);
381                   CreateButtons(codeObject, row.index * rowHeight, rowHeight, row);
382                }
383             }
384          }
385          return true;
386       }
387    };
388    DataField dropField { dataType = class(CodeObject) };
389
390    Button propBtn
391    {
392       this, inactive = true, text = $"Properties", bevelOver = true, isRadio = true;
393       size.h = 20;
394       anchor = { left = 0, bottom = 3, right = 0.5 };
395       bitmap = null;
396
397       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
398       {
399          text = $"Properties";
400          button.font = { $"Tahoma", 8.25f, bold = true };
401          methBtn.font = null;
402
403          methods.visible = false;
404          methBtn.Activate();      // Ensure proper cycling (until tab order?)
405          properties.visible = true;
406
407          alphabetical.disabled = false;
408          categorized.disabled = false;
409
410          properties.Activate();
411
412          // ((IDEWorkSpace)master).SheetSelected(Properties);
413          return true;
414       }
415    };
416
417    Button methBtn
418    {
419       this, inactive = true, bevelOver = true;
420       text = $"Methods";
421       isRadio = true;
422       bitmap = null;
423       size.h = 20;
424       anchor = { bottom = 3, left = 0.5, right = 0 };
425
426       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
427       {
428          text = $"Methods";
429          button.font = { $"Tahoma", 8.25f, bold = true };
430          propBtn.font = null;
431
432          properties.visible = false;
433          methBtn.Activate();      // Ensure proper cycling (until tab order?)
434          methods.visible = true;
435
436          alphabetical.disabled = true;
437          categorized.disabled = true;
438
439          methods.Activate();
440
441          // ((IDEWorkSpace)master).SheetSelected(Methods);
442          return true;
443       }
444    };
445
446    // Menu
447    menu = Menu { };
448    MenuPlacement editMenu { menu, text = $"Edit" };
449    Menu viewMenu { menu, text = $"View" };
450
451    // Property Sheet
452    ListBox properties
453    {
454       this, anchor = { left = 0, right = 0, top = 50, bottom = 25 };
455       hasVertScroll = true, alwaysEdit = true, collapseControl = true, resizable = true;
456       /*
457       background = colorScheme.viewsBackground;
458       foreground = colorScheme.viewsText;
459       selectionText = colorScheme.sheetSelectionText;
460       selectionColor = colorScheme.sheetSelectionColor;
461       */
462
463       bool NotifySelect(ListBox control, DataRow row, Modifiers keyFlags)
464       {
465          /*
466          if(row)
467          {
468             strcpy(selectedProp, (char *)row.GetData(propertyName));
469             selectedScroll = properties.scroll.y;
470             selectedScroll -= row.index * properties.rowHeight;
471          }
472          else
473             selectedProp[0] = 0;
474          if(row)
475          {
476             prop = ((PropertyInfo)row.GetData(propertyValue)).prop;
477          }
478          else
479             prop = null;
480          */
481          return true;
482       }
483    };
484
485    DataField propertyName { dataType = class(char *), width = 130 };
486    DataField propertyValue { dataType = class(PropertyInfo), width = 0, editable = true };
487
488    // Method Sheet
489    ListBox methods
490    {
491       this, anchor = { left = 0, right = 0, top = 50, bottom = 25 };
492       hasVertScroll = true;
493       /*
494       background = colorScheme.viewsBackground;
495       foreground = colorScheme.viewsText;
496       */
497       // alwaysEdit = true;
498       // resizable = true;
499
500       bool NotifyDoubleClick(ListBox control, int x, int y, Modifiers mods)
501       {
502          CodeObject object = control.GetData(methodName);
503          if(object)
504             codeEditor.AddMethod(object.method);
505          return false;
506       }
507
508       bool NotifyRightClick(ListBox control, int x, int y, Modifiers mods)
509       {
510          CodeObject object = control.GetData(methodName);
511          Menu menu { };
512          PopupMenu popupMenu;
513          if(object.overriden == 0)
514          {
515             MenuItem { menu, $"Override", o, enter, bold = true, NotifySelect = OverrideMethodSelected };
516             if(object.compatible.count)
517             {
518                Menu attachMenu { menu, $"Attach", a };
519                OldLink compatible;
520                for(compatible = object.compatible.first; compatible; compatible = compatible.next)
521                {
522                   ClassFunction function = compatible.data;
523                   MenuItem { attachMenu, function.declarator.symbol.string, id = (int64)(intptr)function, NotifySelect = AttachMethodSelected };
524                }
525             }
526          }
527          else if(object.overriden == 1)
528          {
529             MenuItem { menu, $"Go to", g, enter, bold = true, NotifySelect = GotoMethodSelected };
530             MenuItem { menu, $"Detach", d, d, NotifySelect = DetachMethodSelected };
531             MenuItem { menu, $"Delete", del, del, NotifySelect = DeleteMethodSelected };
532          }
533          else if(object.overriden == 2)
534          {
535             MenuItem { menu, $"Go to", g, enter, bold = true, NotifySelect = GotoMethodSelected };
536             MenuItem { menu, $"Detach", d, d, NotifySelect = DetachMethodSelected };
537             if(object.compatible.count > 1)
538             {
539                Menu attachMenu { menu, $"Reattach", r };
540                OldLink compatible;
541                for(compatible = object.compatible.first; compatible; compatible = compatible.next)
542                {
543                   ClassFunction function = compatible.data;
544                   if(function != object.function)
545                   {
546                      MenuItem { attachMenu, function.declarator.symbol.string, id = (int64)(intptr)function, NotifySelect = ReattachMethodSelected };
547                   }
548                }
549             }
550          }
551
552          attachMethod = object.method;
553          popupMenu = PopupMenu { menu = menu, master = this, position =
554             {
555                x + control.absPosition.x - app.desktop.absPosition.x,
556                y + control.absPosition.y - app.desktop.absPosition.y
557             } };
558          popupMenu.Create();
559          // popupMenu.Capture();
560          return false;
561       }
562
563       bool NotifyKeyDown(ListBox listBox, DataRow row, Key key, unichar ch)
564       {
565          if(row)
566          {
567             CodeObject object = row.GetData(methodName);
568             switch((SmartKey)key)
569             {
570                case enter:
571                   if(!object.overriden)
572                      listBox.NotifyDoubleClick(this, listBox, 0,0, 0);
573                   else
574                      codeEditor.GoToMethod(object.method.name);
575                   break;
576                case del:
577                   if(object.deleteBtn)
578                      object.deleteBtn.NotifyClicked(this, object.deleteBtn, 0,0,0);
579                   else if(object.detachBtn)
580                      object.detachBtn.NotifyClicked(this, object.detachBtn, 0,0,0);
581                   break;
582                case a:
583                   if(object.attachBtn)
584                      object.attachBtn.NotifyPushed(this, object.attachBtn, 0,0,0);
585                   break;
586                case d:
587                   if(object.detachBtn)
588                      object.detachBtn.NotifyClicked(this, object.detachBtn, 0,0,0);
589                   break;
590             }
591          }
592          return true;
593       }
594    };
595
596    DataField methodName { dataType = class(CodeObject) };
597
598 #ifdef SHOW_METHODS
599    methBtn.font = { $"Tahoma", 8.25, bold = true };
600    methBtn.checked = true;
601    properties.visible = false;
602    text = $"Methods";
603 #else
604    propBtn.font = { $"Tahoma", 8.25f, bold = true };
605    propBtn.checked = true;
606    methods.visible = false;
607    text = $"Properties";
608 #endif
609
610    Button alphabetical
611    {
612       this, bevelOver = true, inactive = true, position = { 25, 25 }, size = { 24, 24 };
613       bitmap = { "<:ecere>elements/orderAscending.png" };
614       // isRadio = true;
615
616       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
617       {
618          if(!alphabetical.checked)
619          {
620             alphabetical.checked = true;
621             categorized.checked = false;
622
623             ListProperties(true);
624          }
625          return true;
626       }
627    };
628
629    Button categorized
630    {
631       this, bevelOver = true, checked = true, inactive = true, position = { 0, 25 }, size = { 24, 24 };
632       bitmap = { "<:ecere>elements/orderCategorized.png" };
633       // isRadio = true;
634
635       bool NotifyClicked(Button button, int x, int y, Modifiers mods)
636       {
637          if(!categorized.checked)
638          {
639             categorized.checked = true;
640             alphabetical.checked = false;
641
642             ListProperties(true);
643          }
644          return true;
645       }
646    };
647
648    property CodeEditor codeEditor
649    {
650       set
651       {
652          if(codeEditor != value)
653          {
654             codeEditor = value;
655             // Refill it up
656             dropBox.Clear();
657             dropField.userData = codeEditor;
658             methodName.userData = codeEditor;
659
660             if(codeEditor)
661                codeEditor.EnumerateObjects(this);
662           }
663       }
664       get
665       {
666          return codeEditor;
667       }
668    }
669
670    property SheetType sheetSelected
671    {
672       set
673       {
674          if(methBtn.checked != (value == SheetType::methods))
675             ToggleSheet();
676       }
677       get
678       {
679          return methBtn.checked ? methods : properties;
680       }
681    }
682
683    bool OnClose(bool parentClosing)
684    {
685       if(!parentClosing)
686       {
687          visible = false;
688          return false;
689       }
690       return true;
691    }
692
693    bool OnKeyDown(Key key, unichar ch)
694    {
695       if(key == shiftTab)
696          dropBox.Activate();
697       else if(key == tab)
698          ToggleSheet();
699       else if(key == escape)
700       {
701          Window activeClient = ide.activeClient;
702          if(activeClient)
703             activeClient.Activate();
704          else
705             ide.RepositionWindows(true);
706       }
707       return true;
708    }
709
710    bool OnKeyHit(Key key, unichar ch)
711    {
712       return properties.OnKeyHit(key, ch);
713    }
714
715    bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
716    {
717       if(active && codeEditor)
718          codeEditor.EnsureUpToDate();
719       return true;
720    }
721
722    void ListProperties(bool clear)
723    {
724       DataRow row = dropBox.currentRow;
725       ObjectInfo selected = row ? (ObjectInfo)(intptr)row.tag : null;
726
727       //int scroll = 0;
728       bool categorized = this.categorized.checked;
729       bool currentRow = false;
730       char selectedProp[1024];
731
732       if(clear)
733       {
734          Category cat;
735          DataRow row = properties.currentRow;
736          if(row)
737          {
738             DataRow propRow = row;
739             char * propName;
740             while(propRow && propRow.parent && !propRow.parent.isHeader)
741                propRow = row.parent;
742
743             propName = propRow.GetData(propertyName);
744             strcpy(this.selectedProp, propName);
745             selectedScroll = properties.scroll.y;
746             selectedScroll -= propRow.index * properties.rowHeight;
747          }
748          currentRow = this.selectedProp[0] ? true : false;
749
750          for(cat = categories.first; cat; cat = cat.next)
751          {
752             cat.collapsed = cat.row.collapsed;
753             cat.row = null;
754          }
755
756          // Preserve selected property (PropertySheetSelect will null it)
757          strcpy(selectedProp, this.selectedProp);
758          properties.Clear();
759          strcpy(this.selectedProp, selectedProp);
760       }
761       if(selected && selected.instance && codeEditor)
762       {
763          Instance test = eInstance_New(selected.instance._class);
764          Class _class = null;
765          incref test;
766
767          // Put it in the same desktop window...
768          if(selected.classDefinition)
769             codeEditor.designer.PrepareTestObject(test);
770
771          // Fill up the properties
772          while(_class != selected.instance._class)
773          {
774             Class lastClass = _class;
775             Property propIt;
776
777             for(_class = selected.instance._class; _class.base != lastClass && _class.base.type != systemClass && _class.inheritanceAccess != privateAccess; _class = _class.base);
778
779             for(propIt = _class.membersAndProperties.first; propIt; propIt = propIt.next)
780             {
781                if(propIt.isProperty)
782                {
783                   Property prop = eClass_FindProperty(selected.instance._class, propIt.name, GetPrivateModule());
784
785                   if(prop && prop.Set && prop.Get && prop.compiled && (!prop.category || strcmpi(prop.category, $"Deprecated")))
786                   {
787                      bool disabled = Code_IsPropertyDisabled(selected, prop.name);
788                      bool bold;
789                      Class dataType = prop.dataTypeClass;
790                      if(!dataType)
791                         dataType = prop.dataTypeClass = eSystem_FindClass(codeEditor.privateModule, prop.dataTypeString);
792
793                      if(!strcmp(_class.name, "DesignerBase"))
794                         disabled = true;
795                      bold = !disabled && Code_IsPropertyModified(test, selected, prop);
796
797                      if(dataType)
798                      {
799                         DataRow row;
800                         PropertyInfo info { prop, disabled, bold ? codeEditor.boldFont : codeEditor.normalFont };
801                         const char * name = prop.category ? prop.category : $"Misc";
802                         Category category = categories.FindName(name, false);
803
804                         // Hide properties like this for now..
805                         if(name && !strcmp(name, "Private"))
806                         {
807                            delete info;
808                            continue;
809                         }
810
811                         if(!category)
812                         {
813                            category = Category { name = name };
814                            categories.AddName(category);
815                         }
816                         if(!category.row && categorized)
817                         {
818                            PropertyInfo catInfo { null, false, null, name };
819                            category.row = properties.AddRow();
820                            category.row.SetData(propertyName, name );
821                            category.row.SetData(propertyValue, catInfo);
822                            category.row.isHeader = true;
823                            category.row.collapsed = category.collapsed;
824                         }
825
826                         if(clear)
827                         {
828                            row = categorized ? category.row.FindRow((int64)(intptr)prop) : properties.FindRow((int64)(intptr)prop);
829                            if(!row)
830                               row = categorized ? category.row.AddRow() : properties.AddRow();
831                            row.tag = (int64)(intptr)prop;
832                         }
833                         else
834                            row = categorized ? category.row.FindRow((int64)(intptr)prop) : properties.FindRow((int64)(intptr)prop);
835
836                         row.SetData(propertyName, prop.name);
837                         row.SetData(propertyValue, info);
838
839                         if(clear && !strcmp(prop.name, this.selectedProp))
840                            properties.currentRow = row;
841
842                         if(!dataType.noExpansion && (dataType.type == structClass || dataType.type == normalClass || dataType.type == noHeadClass || dataType.type == bitClass))
843                         {
844                            DataMember member;
845
846                            if(clear)
847                               row.collapsed = true;
848
849                            for(member = dataType.membersAndProperties.first; member; member = member.next)
850                            {
851                               if(member.isProperty)
852                               {
853                                  Property subProp = (Property)member;
854                                  if(!subProp.conversion && subProp.Get && subProp.Set)
855                                  {
856                                     DataRow subRow;
857                                     PropertyInfo info { prop, disabled, bold ? codeEditor.boldFont : codeEditor.normalFont, null, null, subProp };
858
859                                     if(clear)
860                                     {
861                                        subRow = row.AddRow();
862                                        subRow.tag = (int64)(intptr)subProp;
863                                     }
864                                     else
865                                        subRow = row.FindRow((int64)(intptr)subProp);
866
867                                     subRow.SetData(propertyName, subProp.name);
868                                     subRow.SetData(propertyValue, info);
869                                  }
870                               }
871                               else if(member.name)
872                               {
873                                  DataRow subRow;
874                                  PropertyInfo info { prop, disabled, bold ? codeEditor.boldFont : codeEditor.normalFont, null, member, null };
875                                  if(clear)
876                                  {
877                                     subRow = row.AddRow();
878                                     subRow.tag = (int64)(intptr)member;
879                                  }
880                                  else
881                                     subRow = row.FindRow((int64)(intptr)member);
882
883                                  subRow.SetData(propertyName, member.name);
884                                  subRow.SetData(propertyValue, info);
885                               }
886                               else
887                               {
888                                  DataMember subMember;
889                                  for(subMember = member.members.first; subMember; subMember = subMember.next)
890                                  {
891                                     DataRow subRow;
892                                     PropertyInfo info { prop, disabled, bold ? codeEditor.boldFont : codeEditor.normalFont, null, subMember, null, member.offset };
893                                     if(clear)
894                                     {
895                                        subRow = row.AddRow();
896                                        subRow.tag = (int64)(intptr)subMember;
897                                     }
898                                     else
899                                        subRow = row.FindRow((int64)(intptr)subMember);
900
901                                     subRow.SetData(propertyName, subMember.name);
902                                     subRow.SetData(propertyValue, info);
903                                  }
904                               }
905                            }
906                         }
907                      }
908                   }
909                }
910             }
911          }
912          delete test;
913          // Sort alphabetically for now...
914          if(clear)
915          {
916             // properties.Sort(null, 1);
917             properties.Sort(propertyValue, 1);
918             if(!properties.currentRow)
919             {
920                bool found = false;
921
922                for(_class = selected.instance._class; _class; _class = _class.base)
923                {
924                   Property prop;
925                   for(prop = _class.membersAndProperties.first; prop; prop = prop.next)
926                   {
927                      if(prop.isProperty && prop.Set && prop.Get && prop.compiled)
928                      {
929                         if(_class.defaultProperty && !strcmp(prop.name, _class.defaultProperty))
930                         {
931                            DataRow row;
932                            const char * name = prop.category ? prop.category : $"Misc";
933                            Category category = categories.FindName(name, false);
934                            row = category ? (categorized ? category.row.FindRow((int64)(intptr)prop) : properties.FindRow((int64)(intptr)prop)) : null;
935                            properties.currentRow = row;
936                            found = true;
937                            break;
938                         }
939                      }
940                   }
941                   if(found) break;
942                }
943                if(!found)
944                   properties.currentRow = properties.firstRow;
945             }
946
947             if(currentRow)
948             {
949                DataRow row = properties.currentRow;
950                properties.scroll.y = selectedScroll + row.index * properties.rowHeight;
951             }
952          }
953       }
954    }
955
956    void AddObject(ObjectInfo object, const char * name, CodeObjectType type, bool select)
957    {
958       DataRow after = null;
959       DataRow row;
960       CodeObject codeObject;
961       char * bitmap = null;
962       bool foundClass = false;
963
964       for(row = dropBox.firstRow; row; row = row.next)
965       {
966          CodeObject codeObject = row.GetData(null);
967          if(codeObject.object.oClass == object.oClass)
968             foundClass = true;
969          else if(foundClass)
970             break;
971          after = row;
972       }
973
974       row = (DataRow)dropBox.AddRowAfter(after);
975
976       row.tag = (int64)(intptr)object;
977
978       codeObject =
979       {
980          object = object;
981          name = name;
982          type = type;
983          indent = (type == typeClass) ? 0 : 1;
984       };
985
986       if(type != typeClass)
987          bitmap = (char *)(intptr)eClass_GetProperty(object.instance._class, "icon");
988       if(bitmap)
989       {
990          codeObject.bitmap = { bitmap };
991          AddResource(codeObject.bitmap);
992       }
993
994       row.SetData(null, codeObject);
995
996       if(select)
997       {
998          this.object = object ? object.instance : null;
999          propertyValue.userData = object ? (void *)object.instance : null;
1000          dropBox.SelectRow(row);
1001       }
1002    }
1003
1004    void DeleteObject(ObjectInfo object)
1005    {
1006       DataRow row = dropBox.FindRow((int64)(intptr)object);
1007       if(row)
1008       {
1009          CodeObject codeObject = row.GetData(null);
1010
1011          if(codeObject.bitmap)
1012             RemoveResource(codeObject.bitmap);
1013          dropBox.DeleteRow(row);
1014       }
1015    }
1016
1017    void SelectObject(ObjectInfo object)
1018    {
1019       if(this)
1020       {
1021          DataRow row = dropBox.FindRow((int64)(intptr)object);
1022          this.object = object ? object.instance : null;
1023          propertyValue.userData = object ? (void *)object.instance : null;
1024          dropBox.SelectRow(row);
1025       }
1026    }
1027
1028    void RenameObject(ObjectInfo object, const char * name)
1029    {
1030       DataRow row = dropBox.FindRow((int64)(intptr)object);
1031       CodeObject codeObject = row.GetData(null);
1032       // Isn't this useless? Shouldn't it be after?
1033       codeObject.name = name;
1034       // row.SetData(null, codeObject);  // Is this necessary?
1035    }
1036
1037    void DataBox::EditSetData(any_object setValue, bool closingDropDown)
1038    {
1039       ((Sheet)master.master).SetData(setValue, this);
1040    }
1041
1042    void SetData(any_object setValue, DataBox dataBox)
1043    {
1044       //PropertyInfo propertyPtr = row.GetData(null);
1045       PropertyInfo propertyPtr = properties.GetData(null);
1046       Property prop = propertyPtr ? propertyPtr.prop : null;
1047       Instance object = this.object;
1048       if(prop)
1049       {
1050          Class dataType = prop.dataTypeClass;
1051          if(!dataType)
1052             dataType = prop.dataTypeClass = eSystem_FindClass(codeEditor.privateModule, prop.dataTypeString);
1053          if(propertyPtr.subMember)
1054          {
1055             DataMember member = propertyPtr.subMember;
1056             Class subDataType = member.dataTypeClass;
1057             if(!member.dataTypeClass)
1058                subDataType = member.dataTypeClass = eSystem_FindClass(codeEditor.privateModule, member.dataTypeString);
1059             if(subDataType)
1060             {
1061                void * data = null;
1062                if(!subDataType.dataType)
1063                   subDataType.dataType = ProcessTypeString(subDataType.dataTypeString, false);
1064
1065                if(dataType.type == structClass)
1066                {
1067                   data = new0 byte[dataType.structSize];
1068                   ((void (*)(void *, void *))(void *)prop.Get)(object, data);
1069                   // CopyBytes((byte *)data + member.offset + propertyPtr.extraOffset, &setValue, subDataType.size);
1070                   CopyBytes((byte *)data + member.offset + propertyPtr.extraOffset, (void *)setValue, subDataType.dataType.size);
1071                   ((void (*)(void *, void *))(void *)prop.Set)(object, data);
1072                }
1073                else if(dataType.type == normalClass || dataType.type == noHeadClass)
1074                {
1075                }
1076                else
1077                {
1078                   if(dataType.type == bitClass)
1079                   {
1080                      BitMember bitMember = (BitMember) member;
1081                      if(subDataType)
1082                      {
1083                         DataValue value { 0 };
1084                         value.ui = ((uint (*)(void *))(void *)prop.Get)(object);
1085                         value.ui &= ~ (uint)bitMember.mask;
1086                         value.ui |= *(uint32 *)setValue << bitMember.pos;
1087                         ((void (*)(void *, uint))(void *)prop.Set)(object, value.ui);
1088                      }
1089                   }
1090                   else
1091                   {
1092                      // TODO: What does this handle?
1093                      data = dataType.typeSize ? new0 byte[dataType.typeSize] : null;
1094                      ((void (*)(void *, void *))(void *)prop.Get)(object, data);
1095                      // CopyBytes((byte *)data + member.offset + propertyPtr.extraOffset, &setValue, subDataType.typeSize);
1096                      CopyBytes((byte *)data + member.offset + propertyPtr.extraOffset, (void *)setValue, subDataType.dataType.size);
1097                      // TODO: Support non 32 bit datatypes here
1098                      ((void (*)(void *, void *))(void *)prop.Set)(object, data);
1099                   }
1100                }
1101
1102                if(data) ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType,&data);
1103                delete data;
1104             }
1105          }
1106          else if(propertyPtr.subProperty)
1107          {
1108             Property subProperty = propertyPtr.subProperty;
1109             Class subDataType = subProperty.dataTypeClass;
1110             if(!subDataType)
1111                subDataType = subProperty.dataTypeClass = eSystem_FindClass(codeEditor.privateModule, subProperty.dataTypeString);
1112             if(subDataType)
1113             {
1114                void * data = null;
1115
1116                if(dataType.type == structClass)
1117                {
1118                   data = new0 byte[dataType.structSize];
1119                   ((void (*)(void *, void *))(void *)prop.Get)(object, data);
1120                   ((void (*)(void *, uint))(void *)subProperty.Set)(data, *(uint32 *)setValue);
1121                   ((void (*)(void *, void *))(void *)prop.Set)(object, data);
1122                }
1123                else if(dataType.type == normalClass || dataType.type == noHeadClass)
1124                {
1125                   Instance current = (Instance)((void *(*)(void *))(void *)prop.Get)(object);
1126                   Instance propObject = eInstance_New(dataType);
1127                   CopyInstanceData(dataType, propObject, current);
1128                   ((void (*)(void *, uint))(void *)subProperty.Set)(propObject, (uint32)setValue);
1129                   ((void (*)(void *, void *))(void *)prop.Set)(object, propObject);
1130                }
1131                else
1132                {
1133                   data = dataType.typeSize ? new0 byte[dataType.typeSize] : null;
1134                   ((void (*)(void *, void *))(void *)prop.Get)(object, data);
1135                   ((void (*)(void *, uint))(void *)subProperty.Set)(data, (uint32)setValue);
1136                   // TODO: Support not 32 bit data types here
1137                   ((void (*)(void *, void *))(void *)prop.Set)(object, data);
1138                }
1139
1140                if(data) ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType,&data);
1141                delete data;
1142             }
1143          }
1144          else
1145          {
1146             SetPropValue(prop, object, (uint32)setValue);
1147          }
1148          Code_FixProperty(propertyPtr.prop, object);
1149
1150          properties.Update(null);
1151          dropBox.Update(null);
1152          codeEditor.designer.Update(null);
1153          codeEditor.Update(null);   // patch for redraw bug if on top
1154
1155          ListProperties(false);
1156          // DataRow values were changed by ListProperties, need to re-query
1157          propertyPtr = properties.GetData(null);
1158          if(propertyPtr)
1159             dataBox.editor.font = { propertyPtr.font.faceName, propertyPtr.font.size, propertyPtr.font.bold };
1160
1161          codeEditor.ModifyCode();
1162       }
1163    }
1164
1165    bool SaveEdit(PropertyInfo propertyPtr, Instance object)
1166    {
1167       codeEditor.designer.Update(null);
1168       codeEditor.Update(null);   // patch for redraw bug if on top
1169       properties.Update(null);
1170       dropBox.Update(null);
1171
1172       Code_FixProperty(propertyPtr.prop, object);
1173       ListProperties(false);
1174
1175       codeEditor.ModifyCode();
1176       return true;
1177    }
1178
1179    void ToggleSheet()
1180    {
1181       if(!propBtn.checked)
1182       {
1183          propBtn.checked = true;
1184          propBtn.NotifyClicked(this, propBtn, 0,0,0);
1185       }
1186       else
1187       {
1188          methBtn.checked = true;
1189          methBtn.NotifyClicked(this, methBtn, 0,0,0);
1190       }
1191    }
1192
1193    bool AttachMethodSelected(MenuItem selection, Modifiers mods)
1194    {
1195       ClassFunction function = (ClassFunction)(intptr)selection.id;
1196       codeEditor.AttachMethod(attachMethod, function);
1197       return true;
1198    }
1199
1200    bool ReattachMethodSelected(MenuItem selection, Modifiers mods)
1201    {
1202       ClassFunction function = (ClassFunction)(intptr)selection.id;
1203       codeEditor.ReAttachMethod(attachMethod, function);
1204       return true;
1205    }
1206
1207    bool OverrideMethodSelected(MenuItem selection, Modifiers mods)
1208    {
1209       CodeObject object = methods.GetData(methodName);
1210       if(object)
1211          codeEditor.AddMethod(object.method);
1212       return true;
1213    }
1214
1215    bool GotoMethodSelected(MenuItem selection, Modifiers mods)
1216    {
1217       CodeObject object = methods.GetData(methodName);
1218       if(object)
1219          codeEditor.GoToMethod(object.method.name);
1220       return true;
1221    }
1222
1223    bool DetachMethodSelected(MenuItem selection, Modifiers mods)
1224    {
1225       CodeObject object = methods.GetData(methodName);
1226       if(object)
1227          codeEditor.DetachMethod(object.method, object.function, object.overriden);
1228       return true;
1229    }
1230
1231    bool DeleteMethodSelected(MenuItem selection, Modifiers mods)
1232    {
1233       CodeObject object = methods.GetData(methodName);
1234       if(object)
1235          object.deleteBtn.NotifyClicked(this, object.deleteBtn, 0,0,0);
1236       return true;
1237    }
1238
1239    bool AddMethodClicked(Button button, int x, int y, Modifiers mods)
1240    {
1241       DataRow row = (DataRow)(intptr)button.id;
1242       CodeObject object = row.GetData(methodName);
1243       codeEditor.AddMethod(object.method);
1244       return true;
1245    }
1246
1247    void CreateButtons(CodeObject codeObject, int y, int h, DataRow row)
1248    {
1249       if(codeObject.overriden)
1250       {
1251          if(codeObject.overriden == 1)
1252          {
1253             codeObject.deleteBtn = Button
1254             {
1255                methods, master = this,
1256                inactive = true,
1257                bitmap = { ":actions/delete.png", alphaBlend = true },
1258                anchor = { right = 16, top = y },
1259                size = { 16, h },
1260                id = (int64)(intptr)row;
1261
1262                bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1263                {
1264                   CodeObject codeObject = ((DataRow)(intptr)button.id).GetData(null);
1265                   bool confirmation = !Code_IsFunctionEmpty(codeObject.function, codeObject.method, codeEditor.selected);
1266
1267                   if(confirmation)
1268                   {
1269                      char title[1024];
1270                      sprintf(title, $"Delete %s", codeObject.name);
1271                      if(MessageBox
1272                         {
1273                            master = parent, type = okCancel, text = title,
1274                            contents = $"Method still contains code. Are you sure you want to delete it?"
1275                         }.Modal() == ok)
1276                         confirmation = false;
1277                   }
1278
1279                   if(!confirmation && codeObject.function.attached.count)
1280                   {
1281                      char title[1024];
1282                      sprintf(title, $"Delete %s", codeObject.name);
1283                      confirmation = true;
1284                      if(MessageBox
1285                         {
1286                            master = parent, type = okCancel, text = title,
1287                            contents = $"Other methods are still attached to this method. Are you sure you want to delete it?"
1288                         }.Modal() == ok)
1289                         confirmation = false;
1290                   }
1291
1292                   if(!confirmation)
1293                   {
1294                      codeEditor.DeleteMethod(codeObject.function);
1295                   }
1296                   return false;
1297                }
1298             };
1299             incref codeObject.deleteBtn;
1300             codeObject.deleteBtn.Create();
1301          }
1302
1303          if(codeObject.overriden == 2 || !codeObject.function.attached.count)
1304          {
1305             codeObject.detachBtn = Button
1306             {
1307                methods,
1308                master = methods.master,
1309                inactive = true,
1310                bitmap = { ":actions/detach.png" },
1311                anchor = { right = 0, top = y },
1312                size = { 16, h },
1313                id = (int64)(intptr)row;
1314
1315                bool NotifyClicked(Button button, int x, int y, Modifiers mods)
1316                {
1317                   DataRow row = (DataRow)(intptr)button.id;
1318                   CodeObject object = row.GetData(methodName);
1319
1320                   codeEditor.DetachMethod(object.method, object.function, object.overriden);
1321                   return true;
1322                }
1323             };
1324             incref codeObject.detachBtn;
1325             codeObject.detachBtn.Create();
1326          }
1327       }
1328       else
1329       {
1330          if(codeObject.compatible.count)
1331          {
1332             codeObject.attachBtn = Button
1333             {
1334                parent = methods, master = methods.master,
1335                inactive = true,
1336                bitmap = { ":actions/attach.png" },
1337                anchor = { right = 0, top = y },
1338                size = { 16, h },
1339                id = (int64)(intptr)row;
1340
1341                bool NotifyPushed(Button button, int x, int y, Modifiers mods)
1342                {
1343                   // Create menu
1344                   DataRow row = (DataRow)(intptr)button.id;
1345                   CodeObject object = row.GetData(methodName);
1346                   OldLink compatible;
1347                   PopupMenu popupMenu;
1348
1349                   Menu menu { };
1350
1351                   for(compatible = object.compatible.first; compatible; compatible = compatible.next)
1352                   {
1353                      ClassFunction function = compatible.data;
1354                      MenuItem { menu, function.declarator.symbol.string, id = (int64)(intptr)function, NotifySelect = AttachMethodSelected };
1355                   }
1356                   attachMethod = object.method;
1357
1358                   popupMenu = PopupMenu
1359                   {
1360                      master = this, menu = menu,
1361                      position =
1362                      {
1363                         button.absPosition.x - app.desktop.position.x,
1364                         button.absPosition.y - app.desktop.position.y + button.size.h
1365                      };
1366                   };
1367                   popupMenu.Create();
1368                   button.ReleaseCapture();
1369                   popupMenu.Capture();
1370                   return false;
1371                }
1372             };
1373             incref codeObject.attachBtn;
1374             codeObject.attachBtn.Create();
1375          }
1376       }
1377    }
1378    Instance object;
1379    Method attachMethod;
1380    char selectedMethod[1024];
1381    CodeEditor codeEditor;
1382    OldList categories;
1383    char selectedProp[1024];
1384    int selectedScroll;
1385 }
1386
1387 static int String_OnCompare(const char ** string1, const char ** string2)
1388 {
1389    int result = 0;
1390    if(*string1 && *string2)
1391       result = strcmpi(*string1, *string2);
1392    else if(!*string1 && *string2)
1393       result = 1;
1394    else if(*string1 && !*string2)
1395       result = -1;
1396    return result;
1397 }
1398
1399 static void CopyInstanceData(Class dataType, Instance propObject, Instance current)
1400 {
1401    Class _class;
1402    for(_class = dataType; _class && _class.type != systemClass; _class = _class.base)
1403    {
1404       DataMember member;
1405       for(member = _class.membersAndProperties.first; member; member = member.next)
1406       {
1407          Class memberType = member.dataTypeClass;
1408          if(!memberType)
1409             memberType = member.dataTypeClass = eSystem_FindClass(((Designer)GetActiveDesigner()).codeEditor.privateModule, member.dataTypeString);
1410          if(member.isProperty)
1411          {
1412             Property subProp = (Property) member;
1413             if(subProp.Get && subProp.Set)
1414                CopyProperty(subProp, propObject, current);
1415          }
1416          else if(member.id > -1)
1417          {
1418             if(memberType)
1419                // TOCHECK: I have serious doubts this works in many cases.
1420                ((void (*)(void *, void *, void *))(void *)memberType._vTbl[__ecereVMethodID_class_OnCopy])(memberType, (byte *)propObject + member.offset, (byte *)current + member.offset);
1421             else
1422                memcpy((byte *)propObject + member.offset, (byte *)current + member.offset, member.memberOffset);
1423          }
1424       }
1425    }
1426 }
1427
1428 class PropertyInfo : struct
1429 {
1430 public:
1431    Property prop;
1432    bool disabled;
1433    FontResource font;
1434    const char * categoryName;
1435    DataMember subMember;
1436    Property subProperty;
1437    uint extraOffset;
1438
1439    void OnDisplay(Surface surface, int x, int y, int width, Instance object, Alignment alignment, DataDisplayFlags displayFlags)
1440    {
1441       Property prop = this.prop;
1442
1443       surface.TextFont(font.font);
1444       if(disabled)
1445       {
1446          surface.SetBackground(Color { 170, 170, 170 });
1447          surface.Area(0,0, x+width-1, y+100);
1448       }
1449       else if(prop && prop.dataTypeString)
1450       {
1451          Class dataType = prop.dataTypeClass;
1452          Module module = ((Designer)GetActiveDesigner()).codeEditor.privateModule;
1453          if(!dataType)
1454             dataType = prop.dataTypeClass = eSystem_FindClass(module, prop.dataTypeString);
1455
1456          if(dataType && prop.Get)
1457          {
1458             void * dataPtr, * data = null, * subData = null;
1459             DataValue valueData, valueSubData;
1460             uint64 bitValue;
1461
1462             // Get main prop
1463             if(dataType.type == structClass)
1464             {
1465                data = new0 byte[dataType.structSize];
1466                ((void (*)(void *, void *))(void *)prop.Get)(object, data);
1467                dataPtr = data;
1468             }
1469             else
1470             {
1471                GetProperty(prop, object, &valueData);
1472
1473                if(dataType.type == normalClass)
1474                   dataPtr = valueData.p;
1475                else
1476                   dataPtr = &valueData;
1477             }
1478
1479             // Get sub prop
1480             if(this.subMember)
1481             {
1482                DataMember member = this.subMember;
1483                Class subDataType = member.dataTypeClass;
1484                if(!subDataType)
1485                   subDataType = member.dataTypeClass = eSystem_FindClass(((Designer)GetActiveDesigner()).codeEditor.privateModule, member.dataTypeString);
1486                if(subDataType)
1487                {
1488                   if(dataType.type == bitClass)
1489                   {
1490                      BitMember bitMember = (BitMember)member;
1491                      bitValue = (valueData.ui64 & bitMember.mask) >> bitMember.pos;
1492                      // TOCHECK: endian safe?
1493                      dataPtr = &bitValue;
1494                   }
1495                   else
1496                      dataPtr = (byte *)dataPtr + member.offset + this.extraOffset;
1497                }
1498                dataType = subDataType;
1499             }
1500             else if(this.subProperty)
1501             {
1502                Property subProperty = this.subProperty;
1503                Class subDataType = subProperty.dataTypeClass;
1504                if(!subDataType)
1505                   subDataType = subProperty.dataTypeClass = eSystem_FindClass(((Designer)GetActiveDesigner()).codeEditor.privateModule, subProperty.dataTypeString);
1506                if(!subProperty.Get) subDataType = null;
1507                if(subDataType)
1508                {
1509                   if(subDataType.type == structClass)
1510                   {
1511                      subData = new0 byte[subDataType.structSize];
1512                      ((void (*)(void *, void *))(void *)subProperty.Get)(dataPtr, subData);
1513                      dataPtr = subData;
1514                   }
1515                   else
1516                   {
1517                      GetProperty(subProperty, dataPtr, &valueSubData);
1518                      if(subDataType.type == normalClass)
1519                         dataPtr = valueSubData.p;
1520                      else
1521                         dataPtr = &valueSubData;
1522                   }
1523                }
1524                dataType = subDataType;
1525             }
1526
1527             if(dataType)
1528                ((void (*)(void *, void *, void *, int, int, int, void *, uint, uint))(void *)dataType._vTbl[__ecereVMethodID_class_OnDisplay])(dataType, dataPtr, surface, x, y, width, null, alignment, displayFlags);
1529
1530             delete data;
1531             delete subData;
1532          }
1533       }
1534    }
1535
1536    Window OnEdit(DataBox dataBox, Window obsolete, int x, int y, int w, int h, void * unused)
1537    {
1538       Window editData = null;
1539       Property prop = this.prop;
1540
1541       dataBox.SetData = Sheet::EditSetData;
1542       if(prop && prop.dataTypeString && !this.disabled)
1543       {
1544          Sheet propertyWindow = (Sheet)dataBox.master.master;
1545          Instance object = propertyWindow.object;
1546          Class dataType = prop.dataTypeClass;
1547          if(!dataType)
1548             dataType = prop.dataTypeClass = eSystem_FindClass(((Designer)GetActiveDesigner()).codeEditor.privateModule, prop.dataTypeString);
1549
1550          if(dataType && prop.Get)
1551          {
1552             void * dataPtr, * data = null, * subData = null;
1553             DataValue valueData, valueSubData;
1554             uint64 bitValue;
1555             bool isEditBoxMultiLineContents = false;
1556
1557             // Get main prop
1558             if(dataType.type == structClass)
1559             {
1560                data = new0 byte[dataType.structSize];
1561                ((void (*)(void *, void *))(void *)prop.Get)(object, data);
1562                dataPtr = data;
1563             }
1564             else
1565             {
1566                //bool freeDataForm = false, freeDataTest = false;
1567                // Because contents property is broken for mutiline EditBox at the moment
1568                if(!strcmp(prop.name, "contents") && !strcmp(prop._class.name, "EditBox") && ((EditBox)object).multiLine)
1569                {
1570                   isEditBoxMultiLineContents = true;
1571                   dataType = eSystem_FindClass(((Designer)GetActiveDesigner()).codeEditor.privateModule, "MultiLineString");
1572                   valueData.p = ((EditBox)object).multiLineContents;
1573                   dataBox.size.h = 3*(h-2);
1574                   h = dataBox.clientSize.h;
1575                }
1576                else
1577                   GetProperty(prop, object, &valueData);
1578
1579                if(dataType.type == normalClass)
1580                   dataPtr = valueData.p;
1581                else
1582                   dataPtr = &valueData;
1583             }
1584
1585             // Get sub prop
1586             if(this.subMember)
1587             {
1588                DataMember member = this.subMember;
1589                Class subDataType = member.dataTypeClass;
1590                if(!subDataType)
1591                   subDataType = member.dataTypeClass = eSystem_FindClass(((Designer)GetActiveDesigner()).codeEditor.privateModule, member.dataTypeString);
1592                if(subDataType)
1593                {
1594                   if(dataType.type == bitClass)
1595                   {
1596                      BitMember bitMember = (BitMember)member;
1597                      bitValue = (valueData.ui64 & bitMember.mask) >> bitMember.pos;
1598                      // TOCHECK: endian safe?
1599                      dataPtr = &bitValue;
1600                   }
1601                   else
1602                      dataPtr = (byte *)dataPtr + member.offset + this.extraOffset;
1603                }
1604                dataType = subDataType;
1605             }
1606             else if(this.subProperty)
1607             {
1608                Property subProperty = this.subProperty;
1609                Class subDataType = subProperty.dataTypeClass;
1610                if(!subDataType)
1611                   subDataType = subProperty.dataTypeClass = eSystem_FindClass(((Designer)GetActiveDesigner()).codeEditor.privateModule, subProperty.dataTypeString);
1612                if(!subProperty.Get) subDataType = null;
1613                if(subDataType)
1614                {
1615                   if(subDataType.type == structClass)
1616                   {
1617                      subData = new0 byte[subDataType.structSize];
1618                      ((void (*)(void *, void *))(void *)subProperty.Get)(dataPtr, subData);
1619                      dataPtr = subData;
1620                   }
1621                   else
1622                   {
1623                      GetProperty(subProperty, dataPtr, &valueSubData);
1624                      if(subDataType.type == normalClass)
1625                         dataPtr = valueSubData.p;
1626                      else
1627                         dataPtr = &valueSubData;
1628                   }
1629                }
1630                dataType = subDataType;
1631             }
1632
1633             if(dataType)
1634             {
1635                Window (* onEdit)(void *, void *, DataBox, void *, int, int, int, int, void*) = (void *)dataType._vTbl[__ecereVMethodID_class_OnEdit];
1636                if(onEdit)
1637                   editData = onEdit(dataType, dataPtr, dataBox, obsolete,  x, y, w, h, object /*unused*/);
1638             }
1639
1640             delete data;
1641             delete subData;
1642
1643             if(isEditBoxMultiLineContents)
1644                delete valueData.p;
1645
1646             dataBox.background = Color { 90, 120, 150 };
1647             // dataBox.background = viewsBackground;
1648             editData.font = { font.faceName, font.size, font.bold };
1649
1650             if(eClass_IsDerived(editData._class, class(DropBox)))
1651             {
1652                DropBox db = (DropBox)editData;
1653                db.selectionColor = colorScheme.sheetSelectionColor;
1654                db.selectionText = colorScheme.viewsBackground;
1655             }
1656             else if(eClass_IsDerived(editData._class, class(EditBox)))
1657             {
1658                EditBox eb = (EditBox)editData;
1659                eb.selectionColor = colorScheme.sheetSelectionColor;
1660                eb.selectionText = colorScheme.viewsBackground;
1661             }
1662          }
1663       }
1664       return editData;
1665    }
1666
1667    int OnCompare(PropertyInfo data2)
1668    {
1669       const char * category1 = prop ? prop.category : categoryName;
1670       const char * category2 = data2.prop ? data2.prop.category : data2.categoryName;
1671       int result;
1672
1673       if(!category1) category1 = $"Misc";
1674       if(!category2) category2 = $"Misc";
1675
1676       if(!prop)
1677       {
1678          result = String_OnCompare(&category1, &category2);
1679       }
1680       else
1681       //if(!result)
1682       {
1683          if(subMember && !data2.subMember)
1684          {
1685             result = 1;
1686          }
1687          else if(!subMember && data2.subMember)
1688          {
1689             result = 1;
1690          }
1691          else if(subMember && data2.subMember)
1692          {
1693             if(subMember.id < data2.subMember.id)
1694                result = -1;
1695             else if(subMember.id > data2.subMember.id)
1696                result = 1;
1697             else
1698                result = 0;
1699          }
1700          else if(subProperty && !data2.subProperty)
1701          {
1702             result = 1;
1703          }
1704          else if(!subProperty && data2.subProperty)
1705          {
1706             result = 1;
1707          }
1708          else if(subProperty && data2.subProperty)
1709          {
1710             if(subProperty.id < data2.subProperty.id)
1711                result = -1;
1712             else if(subProperty.id > data2.subProperty.id)
1713                result = 1;
1714             else
1715                result = 0;
1716          }
1717          else if(prop && !data2.prop)
1718             result = 1;
1719          else if(!prop && data2.prop)
1720             result = -1;
1721          else
1722             // result = ((String)prop.name).OnCompare(data2.prop.name);
1723             // result = String::OnCompare((String)prop.name, (String)data2.prop.name);
1724             result = String_OnCompare((const char **)&prop.name, (const char **)&data2.prop.name);
1725       }
1726       return result;
1727    }
1728
1729    bool OnSaveEdit(Window editControl, void * unusedData)
1730    {
1731       Property prop = this.prop;
1732       if(prop)
1733       {
1734          Sheet sheet = (Sheet)editControl.master.master.master;
1735          Instance object = sheet.object;
1736          Class mainDataType = prop.dataTypeClass;
1737          Class dataType;
1738          bool result = false;
1739          void * dataPtr, * data = null, * subData = null;
1740          void * propObject = null;
1741          DataValue valueData { 0 };
1742          DataValue valueSubData { 0 };
1743          uint64 bitValue;
1744
1745          if(!mainDataType)
1746             mainDataType = prop.dataTypeClass = eSystem_FindClass(((Designer)GetActiveDesigner()).codeEditor.privateModule, prop.dataTypeString);
1747          dataType = mainDataType;
1748
1749          // Because contents property is broken for mutiline EditBox at the moment
1750          if(!strcmp(prop.name, "contents") && !strcmp(prop._class.name, "EditBox") && ((EditBox)object).multiLine)
1751             dataType = eSystem_FindClass(((Designer)GetActiveDesigner()).codeEditor.privateModule, "MultiLineString");
1752
1753          // Prepare main prop
1754          if(dataType.type == structClass)
1755          {
1756             data = new0 byte[dataType.structSize];
1757             if(this.subMember || this.subProperty)
1758                ((void (*)(void *, void *))(void *)prop.Get)(object, data);
1759             dataPtr = data;
1760             propObject = data;
1761          }
1762          else if(dataType.type == normalClass || dataType.type == noHeadClass)
1763          {
1764             dataPtr = &valueData;
1765
1766             if(this.subMember || this.subProperty)
1767             {
1768                Instance current = (Instance)((void *(*)(void *))(void *)prop.Get)(object);
1769                propObject = valueData.p = eInstance_New(dataType);
1770                CopyInstanceData(dataType, propObject, current);
1771             }
1772          }
1773          else
1774          {
1775
1776             if(this.subMember || this.subProperty)
1777                GetProperty(prop, object, &valueData);
1778
1779             dataPtr = &valueData;
1780             propObject = &valueData;
1781          }
1782
1783          // Prepare sub prop
1784          if(this.subMember)
1785          {
1786             DataMember member = this.subMember;
1787             Class subDataType = member.dataTypeClass;
1788             if(!subDataType)
1789                subDataType = member.dataTypeClass = eSystem_FindClass(((Designer)GetActiveDesigner()).codeEditor.privateModule, member.dataTypeString);
1790             if(subDataType)
1791             {
1792                if(dataType.type == bitClass)
1793                {
1794                   BitMember bitMember = (BitMember)member;
1795                   bitValue = (valueData.ui64 & bitMember.mask) >> bitMember.pos;
1796                   // TOCHECK: endian safe?
1797                   dataPtr = &bitValue;
1798                }
1799                else
1800                   dataPtr = (byte *)dataPtr + member.offset + this.extraOffset;
1801             }
1802             dataType = subDataType;
1803          }
1804          else if(this.subProperty)
1805          {
1806             Property subProperty = this.subProperty;
1807             Class subDataType = subProperty.dataTypeClass;
1808
1809             if(!subDataType)
1810                subDataType = subProperty.dataTypeClass =
1811                   eSystem_FindClass(((Designer)GetActiveDesigner()).codeEditor.privateModule, subProperty.dataTypeString);
1812             if(!subProperty.Get) subDataType = null;
1813             if(subDataType)
1814             {
1815                if(subDataType.type == structClass)
1816                {
1817                   subData = new0 byte[subDataType.structSize];
1818                   dataPtr = subData;
1819                }
1820                else
1821                   dataPtr = &valueSubData;
1822             }
1823             dataType = subDataType;
1824          }
1825
1826          if(dataType)
1827          {
1828             bool (* onSaveEdit)(void *, void *, Window, void *) = (void *)dataType._vTbl[__ecereVMethodID_class_OnSaveEdit];
1829             if(!onSaveEdit || onSaveEdit(dataType, dataPtr, editControl, null))
1830             {
1831                if(mainDataType.type == bitClass && this.subMember)
1832                {
1833                   BitMember bitMember = (BitMember)this.subMember;
1834                   valueData.ui64 &= ~bitMember.mask;
1835                   valueData.ui64 |= bitValue << bitMember.pos;
1836                }
1837                if(this.subProperty)
1838                {
1839                   if(dataType.type == structClass)
1840                      ((void (*)(void *, void *))(void *)this.subProperty.Set)(propObject, subData);
1841                   else if(dataType.type == unitClass || dataType.type == enumClass || dataType.type == bitClass || dataType.type == systemClass)
1842                   {
1843                      if(!strcmp(dataType.dataTypeString, "float"))
1844                         ((void(*)(void *,float))(void *)this.subProperty.Set)(propObject, valueSubData.f);
1845                      else if(!strcmp(dataType.dataTypeString, "double"))
1846                         ((void(*)(void *,double))(void *)this.subProperty.Set)(propObject, valueSubData.d);
1847                      else if(!strcmp(dataType.dataTypeString, "byte"))
1848                         ((void(*)(void *,byte))(void *)this.subProperty.Set)(propObject, valueSubData.uc);
1849                      else if(!strcmp(dataType.dataTypeString, "uint16"))
1850                         ((void(*)(void *,uint16))(void *)this.subProperty.Set)(propObject, valueSubData.us);
1851                      else
1852                         ((void (*)(void *, uint))(void *)this.subProperty.Set)(propObject, valueSubData.ui);
1853                   }
1854                   else
1855                      ((void (*)(void *, void *))(void *)this.subProperty.Set)(propObject, valueSubData.p);
1856                }
1857                if(mainDataType.type == structClass)
1858                   ((void (*)(void *, void *))(void *)prop.Set)(object, data);
1859                else if(mainDataType.type == unitClass || mainDataType.type == enumClass || mainDataType.type == bitClass || mainDataType.type == systemClass)
1860                {
1861                   if(!strcmp(mainDataType.dataTypeString, "float"))
1862                      ((void(*)(void *,float))(void *)prop.Set)(object, valueData.f);
1863                   else if(!strcmp(mainDataType.dataTypeString, "double"))
1864                      ((void(*)(void *,double))(void *)prop.Set)(object, valueData.d);
1865                   else if(!strcmp(mainDataType.dataTypeString, "byte"))
1866                      ((void(*)(void *,byte))(void *)prop.Set)(object, valueData.uc);
1867                   else if(!strcmp(mainDataType.dataTypeString, "uint16"))
1868                      ((void(*)(void *,uint16))(void *)prop.Set)(object, valueData.us);
1869                   else
1870                      ((void (*)(void *, uint))(void *)prop.Set)(object, valueData.ui);
1871                }
1872                else
1873                   ((void (*)(void *, void *))(void *)prop.Set)(object, valueData.p);
1874
1875                result = true;
1876             }
1877             if(data == dataPtr)     ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, &data);
1878             if(subData == dataPtr)  ((void (*)(void *, void *))(void *)dataType._vTbl[__ecereVMethodID_class_OnFree])(dataType, &subData);
1879          }
1880          delete data;
1881          delete subData;
1882
1883          if(result)
1884             return sheet.SaveEdit(this, object);
1885       }
1886       return false;
1887    }
1888 };
1889
1890 class Category : struct
1891 {
1892    Category prev, next;
1893    const char * name;
1894    DataRow row;
1895    bool collapsed;
1896 };