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