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