ecere/gui/controls/ToolTip: More null Virtual Methods fixes
[sdk] / ecere / src / gui / controls / ToolTip.ec
1 import "Window"
2
3 struct ToolTipTextLine
4 {
5    char * string;
6    int len;
7    int width;
8 };
9
10 public class ToolTip : Window
11 {
12    borderStyle = contour;
13    background = lightYellow;
14    autoCreate = false;
15    interim = true;
16    clickThrough = true;
17    creationActivation = doNothing;
18
19    Window tippedWindow;
20    Point pos;
21
22    bool (* OrigOnMouseOver)(Window window, int x, int y, Modifiers mods);
23    bool (* OrigOnMouseLeave)(Window window, Modifiers mods);
24    bool (* OrigOnMouseMove)(Window window, int x, int y, Modifiers mods);
25    bool (* OrigOnLeftButtonDown)(Window window, int x, int y, Modifiers mods);
26
27    int lh, maxW;
28    Array<ToolTipTextLine> lines { };
29    String tip;
30    int margin; margin = 2;
31    Point offset; offset = { 0, 20 };
32
33    public property const String tip
34    {
35       set
36       {
37          delete tip;
38          lines.Free();
39          if(value)
40          {
41             int c;
42             char ch, next;
43             int start = 0;
44
45             tip = CopyString(value);
46             next = tip[0];
47             for(c = 0; (ch = next); c++)
48             {
49                next = tip[c+1];
50                if(ch == '\n' || next == '\0')
51                {
52                   lines.Add({ tip + start, c - start + (next == '\0') });
53                   start = c+1;
54                }
55             }
56          }
57       }
58       get { return tip; }
59    }
60
61    bool OnLoadGraphics()
62    {
63       maxW = 0;
64       display.FontExtent(fontObject, " ", 1, null, &lh);
65       for(l : lines)
66       {
67          int w;
68          display.FontExtent(fontObject, l.string, l.len, &w, null);
69          l.width = w;
70          if(w > maxW) maxW = w;
71       }
72       clientSize = { maxW + 2*margin, lh * lines.count + 2*margin };
73       return true;
74    }
75
76    bool OnMouseLeave(Modifiers mods)
77    {
78       closeTimer.Start();
79       return true;
80    }
81
82    bool OnMouseOver(int x, int y, Modifiers mods)
83    {
84       closeTimer.Stop();
85       return true;
86    }
87
88    bool OnLeftButtonDown(int x, int y, Modifiers mods)
89    {
90       Destroy(0);
91       return true;
92    }
93
94    Timer timer
95    {
96       this, 0.5, userData = this;
97
98       bool DelayExpired()
99       {
100          timer.Stop();
101          position =
102          {
103             pos.x + offset.x + tippedWindow.clientStart.x +
104                tippedWindow.absPosition.x - parent.position.x;
105             pos.y + offset.y + tippedWindow.clientStart.y +
106                tippedWindow.absPosition.y - parent.position.y;
107          };
108          Create();
109          return true;
110       }
111    };
112
113    Timer closeTimer
114    {
115       this, 0.3, userData = this;
116
117       bool DelayExpired()
118       {
119          closeTimer.Stop();
120          Destroy(0);
121
122          return true;
123       }
124    };
125
126    watch(parent)
127    {
128       if(tippedWindow && tippedWindow == master)
129       {
130          tippedWindow.OnMouseOver = OrigOnMouseOver;
131          tippedWindow.OnMouseLeave = OrigOnMouseLeave;
132          tippedWindow.OnMouseMove = OrigOnMouseMove;
133          tippedWindow.OnLeftButtonDown = OrigOnLeftButtonDown;
134          master = null;
135          delete tippedWindow;
136       }
137       if(parent && parent.parent)
138       {
139          Window value = parent;
140          parent = null;
141          tippedWindow = value;
142          incref tippedWindow;
143          master = tippedWindow;
144          OrigOnMouseOver = value.OnMouseOver;
145          OrigOnMouseLeave = value.OnMouseLeave;
146          OrigOnMouseMove = value.OnMouseMove;
147          OrigOnLeftButtonDown = value.OnLeftButtonDown;
148          tippedWindow.OnMouseOver = OnMouseOverHandler;
149          tippedWindow.OnMouseLeave = OnMouseLeaveHandler;
150          tippedWindow.OnMouseMove = OnMouseMoveHandler;
151          tippedWindow.OnLeftButtonDown = OnLeftButtonDownHandler;
152       }
153    };
154    ~ToolTip()
155    {
156       timer.Stop();
157       closeTimer.Stop();
158       if(tippedWindow)
159       {
160          tippedWindow.OnMouseOver = OrigOnMouseOver;
161          tippedWindow.OnMouseLeave = OrigOnMouseLeave;
162          tippedWindow.OnMouseMove = OrigOnMouseMove;
163          tippedWindow.OnLeftButtonDown = OrigOnLeftButtonDown;
164          delete tippedWindow;
165       }
166       delete tip;
167    }
168
169    ToolTip ::Find(Window window)
170    {
171       Window w;
172       for(w = window.firstSlave; w; w = w.nextSlave)
173       {
174          if(eClass_IsDerived(w._class, class(ToolTip)))
175             break;
176       }
177       return (ToolTip)w;
178    }
179
180    bool Window::OnMouseOverHandler(int x, int y, Modifiers mods)
181    {
182       ToolTip toolTip = ToolTip::Find(this);
183       if(toolTip)
184       {
185          toolTip.pos = { x, y };
186          toolTip.closeTimer.Stop();
187          if(!mods.isSideEffect && !toolTip.created && rootWindow.active)
188             toolTip.timer.Start();
189          return toolTip.OrigOnMouseOver ? toolTip.OrigOnMouseOver(this, x, y, mods) : true;
190       }
191       return true;
192    }
193
194    bool Window::OnMouseLeaveHandler(Modifiers mods)
195    {
196       ToolTip toolTip = ToolTip::Find(this);
197       if(toolTip)
198       {
199          toolTip.timer.Stop();
200          toolTip.closeTimer.Start();
201          return toolTip.OrigOnMouseLeave ? toolTip.OrigOnMouseLeave(this, mods) : true;
202       }
203       return true;
204    }
205
206    bool Window::OnLeftButtonDownHandler(int x, int y, Modifiers mods)
207    {
208       ToolTip toolTip = ToolTip::Find(this);
209       if(toolTip)
210       {
211          toolTip.timer.Stop();
212          toolTip.Destroy(0);
213          return toolTip.OrigOnLeftButtonDown ? toolTip.OrigOnLeftButtonDown(this, x, y, mods) : true;
214       }
215       return true;
216    }
217
218    bool Window::OnMouseMoveHandler(int x, int y, Modifiers mods)
219    {
220       ToolTip toolTip = ToolTip::Find(this);
221       if(toolTip)
222       {
223          toolTip.pos = { x, y };
224          toolTip.closeTimer.Stop();
225          if(!mods.isSideEffect && !toolTip.created && rootWindow.active)
226          {
227             toolTip.timer.Stop();
228             toolTip.timer.Start();
229          }
230          return toolTip.OrigOnMouseMove ? toolTip.OrigOnMouseMove(this, x, y, mods) : true;
231       }
232       return true;
233    }
234
235    void OnRedraw(Surface surface)
236    {
237       int y = margin;
238       for(l : lines)
239       {
240          surface.WriteText(margin, y, l.string, l.len);
241          y += lh;
242       }
243    }
244 }
245
246 // NOTE: I'm putting this here for now because I don't have time to update all the makefiles right now
247 import "Stacker"
248
249 public class ToolBar : Stacker
250 {
251    direction = horizontal;
252    background = formColor;
253    gap = 0;
254    inactive = true;
255
256    anchor = Anchor { left = 0, right = 0 };
257    clientSize = { h = 32 };
258    borderStyle = bevel;
259
260    watch(master)
261    {
262       Window w;
263       for(w = firstChild; w; w = w.next)
264          w.master = master;
265    };
266 }
267
268 public class ToolButton : Button
269 {
270    bevelOver = true;
271    size = Size { 24, 24 };
272    opacity = 0;
273    bitmapAlignment = center;
274    MenuItem * menuItemPtr;
275
276    watch(master)
277    {
278       if(menuItemPtr)
279       {
280          Window master = this.master;
281          if(master && master.parent && !eClass_IsDerived(master._class, class(Stacker)))
282          {
283             MenuItem menuItem = this.menuItem;
284             BitmapResource bmp;
285
286             if(menuItem && (bmp = menuItem.bitmap))
287             {
288                //bitmap = bmp;
289                // We cannot reuse the same BitmapResource object here, as a BitmapResource is meant to be added with AddResource to a single Window
290                bitmap = { fileName = bmp.fileName, grayed = bmp.grayed, monochrome = bmp.monochrome, transparent = bmp.transparent, alphaBlend = bmp.alphaBlend, keepData = bmp.keepData };
291             }
292          }
293       }
294    };
295
296    NotifyClicked = SelectMenuItem;
297
298    bool Window::SelectMenuItem(Button button, int x, int y, Modifiers mods)
299    {
300       ToolButton toolButton = (ToolButton)button;
301       MenuItem menuItem = toolButton.menuItem;
302       return menuItem ? menuItem.NotifySelect(this, menuItem, 0) : 0;
303    }
304
305 public:
306    property MenuItem * menuItemPtr { set { menuItemPtr = value; } }
307    property MenuItem menuItem
308    {
309       get
310       {
311          MenuItem menuItem = menuItemPtr ? *(MenuItem *)((byte *)master + (uintptr)menuItemPtr) : null;
312          return menuItem;
313       }
314    }
315 }
316
317 public class ToolSeparator : Window
318 {
319    size = Size { 4, 24 };
320    opacity = 0;
321
322    void OnRedraw(Surface surface)
323    {
324       surface.foreground = Color { 85, 85, 85 };
325       surface.VLine(0, 23, 1);
326       surface.foreground = white;
327       surface.VLine(0, 23, 2);
328    }
329 }