1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3
4
5 ///
6 module dfl.tooltip;
7
8
9 private import dfl.internal.dlib, dfl.internal.clib;
10
11 private import dfl.control, dfl.base, dfl.application, dfl.internal.winapi,
12 dfl.internal.utf;
13
14
15 ///
16 class ToolTip // docmain
17 {
18 package this(DWORD style)
19 {
20 _initCommonControls(ICC_TREEVIEW_CLASSES); // Includes tooltip.
21
22 hwtt = CreateWindowExA(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, _TOOLTIPS_CLASSA.ptr,
23 "", style, 0, 0, 50, 50, null, null, null, null);
24 if(!hwtt)
25 throw new DflException("Unable to create tooltip");
26 }
27
28
29 this()
30 {
31 this(cast(DWORD)WS_POPUP);
32 }
33
34
35 ~this()
36 {
37 removeAll(); // Fixes ref count.
38 DestroyWindow(hwtt);
39 }
40
41
42 ///
43 final @property HWND handle() // getter
44 {
45 return hwtt;
46 }
47
48
49 ///
50 final @property void active(bool byes) // setter
51 {
52 SendMessageA(hwtt, TTM_ACTIVATE, byes, 0); // ?
53 _active = byes;
54 }
55
56 /// ditto
57 final @property bool active() // getter
58 {
59 return _active;
60 }
61
62
63 ///
64 // Sets autoPopDelay, initialDelay and reshowDelay.
65 final @property void automaticDelay(DWORD ms) // setter
66 {
67 SendMessageA(hwtt, TTM_SETDELAYTIME, TTDT_AUTOMATIC, ms);
68 }
69
70 /+
71 /// ditto
72 final @property DWORD automaticDelay() // getter
73 {
74 }
75 +/
76
77
78 ///
79 final @property void autoPopDelay(DWORD ms) // setter
80 {
81 SendMessageA(hwtt, TTM_SETDELAYTIME, TTDT_AUTOPOP, ms);
82 }
83
84 /+
85 /// ditto
86 final @property DWORD autoPopDelay() // getter
87 {
88 }
89 +/
90
91
92 ///
93 final @property void initialDelay(DWORD ms) // setter
94 {
95 SendMessageA(hwtt, TTM_SETDELAYTIME, TTDT_INITIAL, ms);
96 }
97
98 /+
99 /// ditto
100 final @property DWORD initialDelay() // getter
101 {
102 }
103 +/
104
105
106 ///
107 final @property void reshowDelay(DWORD ms) // setter
108 {
109 SendMessageA(hwtt, TTM_SETDELAYTIME, TTDT_RESHOW, ms);
110 }
111
112 /+
113 /// ditto
114 final @property DWORD reshowDelay() // getter
115 {
116 }
117 +/
118
119
120 ///
121 final @property void showAlways(bool byes) // setter
122 {
123 LONG wl;
124 wl = GetWindowLongA(hwtt, GWL_STYLE);
125 if(byes)
126 {
127 if(wl & TTS_ALWAYSTIP)
128 return;
129 wl |= TTS_ALWAYSTIP;
130 }
131 else
132 {
133 if(!(wl & TTS_ALWAYSTIP))
134 return;
135 wl &= ~TTS_ALWAYSTIP;
136 }
137 SetWindowLongA(hwtt, GWL_STYLE, wl);
138 }
139
140 /// ditto
141 final @property bool showAlways() // getter
142 {
143 return (GetWindowLongA(hwtt, GWL_STYLE) & TTS_ALWAYSTIP) != 0;
144 }
145
146
147 ///
148 // Remove all tooltip text associated with this instance.
149 final void removeAll()
150 {
151 TOOLINFOA tool;
152 tool.cbSize = TOOLINFOA.sizeof;
153 while(SendMessageA(hwtt, TTM_ENUMTOOLSA, 0, cast(LPARAM)&tool))
154 {
155 SendMessageA(hwtt, TTM_DELTOOLA, 0, cast(LPARAM)&tool);
156 Application.refCountDec(cast(void*)this);
157 }
158 }
159
160
161 ///
162 // WARNING: possible buffer overflow.
163 final Dstring getToolTip(Control ctrl)
164 {
165 Dstring result;
166 TOOLINFOA tool;
167 tool.cbSize = TOOLINFOA.sizeof;
168 tool.uFlags = TTF_IDISHWND;
169 tool.hwnd = ctrl.handle;
170 tool.uId = cast(UINT)ctrl.handle;
171
172 if(dfl.internal.utf.useUnicode)
173 {
174 tool.lpszText = cast(typeof(tool.lpszText))dfl.internal.clib.malloc((MAX_TIP_TEXT_LENGTH + 1) * wchar.sizeof);
175 if(!tool.lpszText)
176 throw new OomException;
177 scope(exit)
178 dfl.internal.clib.free(tool.lpszText);
179 tool.lpszText[0 .. 2] = 0;
180 SendMessageA(hwtt, TTM_GETTEXTW, 0, cast(LPARAM)&tool);
181 if(!(cast(wchar*)tool.lpszText)[0])
182 result = null;
183 else
184 result = fromUnicodez(cast(wchar*)tool.lpszText);
185 }
186 else
187 {
188 tool.lpszText = cast(typeof(tool.lpszText))dfl.internal.clib.malloc(MAX_TIP_TEXT_LENGTH + 1);
189 if(!tool.lpszText)
190 throw new OomException;
191 scope(exit)
192 dfl.internal.clib.free(tool.lpszText);
193 tool.lpszText[0] = 0;
194 SendMessageA(hwtt, TTM_GETTEXTA, 0, cast(LPARAM)&tool);
195 if(!tool.lpszText[0])
196 result = null;
197 else
198 result = fromAnsiz(tool.lpszText); // Assumes fromAnsiz() copies.
199 }
200 return result;
201 }
202
203 /// ditto
204 final void setToolTip(Control ctrl, Dstring text)
205 in
206 {
207 try
208 {
209 ctrl.createControl();
210 }
211 catch(DThrowable o)
212 {
213 assert(0); // If -ctrl- is a child, make sure the parent is set before setting tool tip text.
214 //throw o;
215 }
216 }
217 body
218 {
219 TOOLINFOA tool;
220 tool.cbSize = TOOLINFOA.sizeof;
221 tool.uFlags = TTF_IDISHWND;
222 tool.hwnd = ctrl.handle;
223 tool.uId = cast(UINT)ctrl.handle;
224
225 if(!text.length)
226 {
227 if(SendMessageA(hwtt, TTM_GETTOOLINFOA, 0, cast(LPARAM)&tool))
228 {
229 // Remove.
230
231 SendMessageA(hwtt, TTM_DELTOOLA, 0, cast(LPARAM)&tool);
232
233 Application.refCountDec(cast(void*)this);
234 }
235 return;
236 }
237
238 // Hack to help prevent getToolTip() overflow.
239 if(text.length > MAX_TIP_TEXT_LENGTH)
240 text = text[0 .. MAX_TIP_TEXT_LENGTH];
241
242 if(SendMessageA(hwtt, TTM_GETTOOLINFOA, 0, cast(LPARAM)&tool))
243 {
244 // Update.
245
246 if(dfl.internal.utf.useUnicode)
247 {
248 tool.lpszText = cast(typeof(tool.lpszText))toUnicodez(text);
249 SendMessageA(hwtt, TTM_UPDATETIPTEXTW, 0, cast(LPARAM)&tool);
250 }
251 else
252 {
253 tool.lpszText = cast(typeof(tool.lpszText))unsafeAnsiz(text);
254 SendMessageA(hwtt, TTM_UPDATETIPTEXTA, 0, cast(LPARAM)&tool);
255 }
256 }
257 else
258 {
259 // Add.
260
261 /+
262 // TOOLINFOA.rect is ignored if TTF_IDISHWND.
263 tool.rect.left = 0;
264 tool.rect.top = 0;
265 tool.rect.right = ctrl.clientSize.width;
266 tool.rect.bottom = ctrl.clientSize.height;
267 +/
268 tool.uFlags |= TTF_SUBCLASS; // Not a good idea ?
269 LRESULT lr;
270 if(dfl.internal.utf.useUnicode)
271 {
272 tool.lpszText = cast(typeof(tool.lpszText))toUnicodez(text);
273 lr = SendMessageA(hwtt, TTM_ADDTOOLW, 0, cast(LPARAM)&tool);
274 }
275 else
276 {
277 tool.lpszText = cast(typeof(tool.lpszText))unsafeAnsiz(text);
278 lr = SendMessageA(hwtt, TTM_ADDTOOLA, 0, cast(LPARAM)&tool);
279 }
280
281 if(lr)
282 Application.refCountInc(cast(void*)this);
283 }
284 }
285
286
287 private:
288 enum _TOOLTIPS_CLASSA = "tooltips_class32";
289 enum size_t MAX_TIP_TEXT_LENGTH = 2045;
290
291 HWND hwtt; // Tooltip control handle.
292 bool _active = true;
293 }
294