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