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