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