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