compiler/libec; ecere; ide: Safer default virtual method calls
[sdk] / ecere / src / gui / controls / DataBox.ec
1 namespace gui::controls;
2
3 import "Window"
4
5 default:
6 extern int __ecereVMethodID_class_OnGetString;
7 extern int __ecereVMethodID_class_OnGetDataFromString;
8 extern int __ecereVMethodID_class_OnCompare;
9 extern int __ecereVMethodID_class_OnSerialize;
10 extern int __ecereVMethodID_class_OnUnserialize;
11 extern int __ecereVMethodID_class_OnFree;
12 extern int __ecereVMethodID_class_OnEdit;
13 extern int __ecereVMethodID_class_OnCopy;
14 extern int __ecereVMethodID_class_OnDisplay;
15 extern int __ecereVMethodID_class_OnSaveEdit;
16 private:
17
18 public class DataBox : CommonControl
19 {
20 public:
21    Class type;
22    void * data;
23    void * fieldData;
24    Window editor;
25    bool readOnly;
26    bool keepEditor;
27    bool autoSize;
28    bool needUpdate;
29    String stringValue;
30    needUpdate = true;
31
32    ~DataBox()
33    {
34       delete stringValue;
35    }
36
37    virtual void SetData(any_object newData, bool closingDropDown)
38    {
39       //((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnCopy])(type, data, newData);
40       needUpdate = true;
41       if(type)
42       {
43          if(type.type == normalClass || type.type == noHeadClass)
44          {
45             if(((void **)data)[0])
46                ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, ((void **)data)[0]);
47             ((void **)data)[0] = newData;
48          }
49          else
50          {
51             // Free old data first
52             ((void (*)(void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnFree])(type, data);
53             ((void (*)(void *, void *, void *))(void *)type._vTbl[__ecereVMethodID_class_OnCopy])(type, data, newData);
54          }
55       }
56       if(created)
57          NotifyChanged(master, this, closingDropDown);
58       //editor.Activate();
59    }
60
61    bool SaveData()      // TODO: Clear this up, along with Saving DataBox
62    {
63       bool (* onSaveEdit)(void *, void *, Window, void *) = (void *)type._vTbl[__ecereVMethodID_class_OnSaveEdit];
64       if(editor && (!onSaveEdit || onSaveEdit(type, data, editor, null)))
65       {
66          Refresh();
67          NotifyChanged(master, this, false);
68          // Refresh();
69          return true;
70       }
71       return false;
72    }
73
74    void Modified()
75    {
76       NotifyModified(master);
77    }
78
79    void Refresh()
80    {
81       needUpdate = true;
82       if(created)
83       {
84          if(!keepEditor)
85          {
86             editor.Destroy(0);
87             editor = null;
88          }
89          OnPostCreate();
90       }
91    }
92
93    virtual bool Window::NotifyModified();
94    virtual bool Window::NotifyChanged(DataBox dataBox, bool closingDropDown);
95    virtual void OnConfigure(Window editor);
96
97 private:
98    bool inAutoSize;
99
100    watch(background)     { if(editor) editor.background = background; };
101    watch(foreground)     { if(editor) editor.foreground = foreground; };
102    //watch(selectionColor) { if(editor) editor.selectionColor = selectionColor; };
103    //watch(selectionText)  { if(editor) editor.selectionText = selectionText; };
104    watch(opacity)        { if(editor) editor.opacity = opacity; };
105
106    bool OnPostCreate()
107    {
108       // Right now for read-only DataBoxes the only reason we'd want to create an editor is for autoSize purposes, when using the default EditBox editor that supports it.
109       // ( A tweak for enum classes is in typeEdit.ec, as the base class editor invokes it )
110       if(type/* && (!readOnly || (autoSize && type._vTbl[__ecereVMethodID_class_OnEdit] == class(Instance)._vTbl[__ecereVMethodID_class_OnEdit]))*/ &&
111          (type.type == normalClass || type.type == noHeadClass || data))
112       {
113          // IMPORTANT FIX: If keepEditor is true, we were passing editor rather than the editor's current master
114          editor = ((Window (*)(void *, void *, DataBox, void *, int, int, int, int, void*))(void *)type._vTbl[__ecereVMethodID_class_OnEdit])(type,
115             (type.type == normalClass || type.type == noHeadClass) ? (data ? (*(void **)data) : null) : data,
116             this, (keepEditor && editor) ? editor.master : this, 0, 0, clientSize.w, clientSize.h, fieldData);// null);
117          if(editor && readOnly && !eClass_IsDerived(editor._class, class(EditBox)) &&
118                !(autoSize && type._vTbl[__ecereVMethodID_class_OnEdit] == class(Instance)._vTbl[__ecereVMethodID_class_OnEdit]))
119          {
120             editor.Destroy(0);
121             editor = null;
122             return true;
123          }
124          if(editor)
125          {
126             // editor.anchor = { 0, 0, 0, 0 };
127             editor.background = background;
128             editor.foreground = foreground;
129             editor.opacity = opacity;
130             if(eClass_IsDerived(editor._class, class(EditBox)))
131             {
132                ((EditBox)editor).readOnly = readOnly;
133                if(autoSize)
134                   ((EditBox)editor).autoSize = autoSize;
135                ((EditBox)editor).clickThrough = true;
136             }
137             else if(eClass_IsDerived(editor._class, class(Button)) && autoSize)
138                size = editor.size;
139          }
140          else
141          {
142             // TODO: Should returning 0 from OnPostCreate cancel creation?
143             Destroy(0);
144             return false;
145          }
146       }
147       return true;
148    }
149
150    void OnRedraw(Surface surface)
151    {
152       if(type && data && (!editor || !editor.created || editor.anchor.left)) // ColorDropBox lets part of the DataBox show
153       {
154          char tempString[1024];
155          if(type._vTbl[__ecereVMethodID_class_OnDisplay] == class(Instance)._vTbl[__ecereVMethodID_class_OnDisplay])
156          {
157             if(needUpdate)
158             {
159                const String s;
160                if(type.type == noHeadClass || type.type == normalClass)
161                   s = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, *(void **)this.data, tempString, fieldData, null);
162                else
163                   s = ((const char *(*)(void *, void *, char *, void *, bool *))(void *)type._vTbl[__ecereVMethodID_class_OnGetString])(type, this.data, tempString, fieldData, null);
164                delete stringValue;
165                stringValue = CopyString(s);
166                needUpdate = false;
167             }
168             ((void (*)(void *, void *, void *, int, int, int, void *, uint, uint))(void *)class(String)._vTbl[__ecereVMethodID_class_OnDisplay])(class(String), stringValue, surface, 3, 1, clientSize.w, fieldData, type.defaultAlignment, 0);
169          }
170          else
171          {
172             if(type.type == noHeadClass || type.type == normalClass)
173                ((void (*)(void *, void *, void *, int, int, int, void *, uint, uint))(void *)type._vTbl[__ecereVMethodID_class_OnDisplay])(type, *(void **)this.data, surface, 3, 1, clientSize.w, fieldData, type.defaultAlignment, 0);
174             else
175                ((void (*)(void *, void *, void *, int, int, int, void *, uint, uint))(void *)type._vTbl[__ecereVMethodID_class_OnDisplay])(type, this.data, surface, 3, 1, clientSize.w, fieldData, type.defaultAlignment, 0);
176          }
177       }
178    }
179
180    void OnDestroy()
181    {
182       editor = null;
183       CommonControl::OnDestroy();
184    }
185
186    void OnPosition(int x, int y, int width, int height)
187    {
188       if(editor)
189          editor.OnPosition(editor.position.x, editor.position.y, editor.size.w, editor.size.h);
190    }
191
192    bool OnKeyHit(Key key, unichar ch)
193    {
194       return editor ? editor.OnKeyHit(key, ch) : true;
195    }
196
197    bool OnKeyDown(Key key, unichar ch)
198    {
199       if((SmartKey)key == enter)
200       {
201          SaveData();
202          return false;
203       }
204       else if((SmartKey)key == escape)
205       {
206          Refresh();
207          return true;
208       }
209       return editor ? editor.OnKeyDown(key, ch) : true;
210    }
211
212    void OnChildResized(Window child, int x, int y, int w, int h)
213    {
214       if(!inAutoSize && autoSize == true)
215       {
216          inAutoSize = true;
217          clientSize = { w, h };
218          inAutoSize = false;
219       }
220    }
221 };
222
223 public class SavingDataBox : DataBox
224 {
225    class_property(icon) = "<:ecere>controls/dataBox.png";
226    borderStyle = deep;
227    bool OnActivate(bool active, Window previous, bool * goOnWithActivation, bool direct)
228    {
229       if(!active && editor && !editor.modalSlave)
230       {
231          if(!SaveData())
232             Refresh();
233       }
234       return true;
235    }
236
237    bool OnKeyDown(Key key, unichar ch)
238    {
239       if((SmartKey)key == enter)
240       {
241          if(!SaveData())
242             // Force Refresh on Enter if SaveData didn't do it
243             Refresh();
244          return true;
245       }
246       return DataBox::OnKeyDown(key, ch);
247    }
248
249    bool OnResizing(int * w, int * h)
250    {
251       if(!*w || !*h)
252       {
253          int spaceH;
254          display.FontExtent(fontObject, " ", 1, null, &spaceH);
255          if(!*h)
256             *h = spaceH + 2;
257          if(!*w)
258             *w = spaceH * 80 / 14;
259       }
260       return true;
261    }
262 }