listWidgetTooltip.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. #include "main.h"
  2. #include "./listWidgetInternal.h"
  3. #include <strsafe.h>
  4. #define TOOLTIP_MARGIN_LEFT_DLU 3
  5. #define TOOLTIP_MARGIN_TOP_DLU 1
  6. #define TOOLTIP_MARGIN_RIGHT_DLU 3
  7. #define TOOLTIP_MARGIN_BOTTOM_DLU 1
  8. #define TOOLTIP_DELAY_INITIAL 1000
  9. #define TOOLTIP_DELAY_RESHOW 400
  10. static ATOM LISTWIDGETTOOLTIP_PROP = 0;
  11. static LRESULT WINAPI
  12. ListWidgetTooltip_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  13. typedef struct ListWidgetTooltip
  14. {
  15. HWND window;
  16. HWND owner;
  17. BOOL active;
  18. POINT position;
  19. wchar_t *buffer;
  20. ListWidgetItem *item;
  21. ListWidgetItemPart part;
  22. RECT partRect;
  23. BOOL blockLocationChange;
  24. WNDPROC originalProc;
  25. DWORD showTime;
  26. } ListWidgetTooltip;
  27. static HWND
  28. ListWidget_TooltipCreateWindow(HWND owner, WNDPROC *originalProc, HANDLE windowProperty)
  29. {
  30. HWND hwnd;
  31. TOOLINFO ti;
  32. hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_NOPARENTNOTIFY | WS_EX_TRANSPARENT | WS_EX_LAYERED,
  33. TOOLTIPS_CLASS, NULL, WS_CLIPSIBLINGS | WS_POPUP | TTS_NOANIMATE | TTS_ALWAYSTIP,
  34. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  35. owner, NULL, NULL, NULL);
  36. if ( hwnd == NULL )
  37. return NULL;
  38. MLSkinWindow2(Plugin_GetLibraryWindow(), hwnd, SKINNEDWND_TYPE_TOOLTIP,
  39. SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS);
  40. if (NULL != originalProc)
  41. {
  42. if (0 == LISTWIDGETTOOLTIP_PROP)
  43. LISTWIDGETTOOLTIP_PROP = GlobalAddAtom(TEXT("ListWidgetTooltipProp"));
  44. if (0 != LISTWIDGETTOOLTIP_PROP)
  45. {
  46. *originalProc = (WNDPROC)(LONG_PTR)SetWindowLongPtr(hwnd, GWLP_WNDPROC,
  47. (LONGX86)(LONG_PTR)ListWidgetTooltip_WindowProc);
  48. if (NULL != *originalProc &&
  49. FALSE == SetProp(hwnd, MAKEINTATOM(LISTWIDGETTOOLTIP_PROP), windowProperty))
  50. {
  51. SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)*originalProc);
  52. *originalProc = NULL;
  53. }
  54. }
  55. else
  56. *originalProc = NULL;
  57. }
  58. SendMessage(hwnd, CCM_SETVERSION, 6, 0L);
  59. SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
  60. ZeroMemory(&ti, sizeof(ti));
  61. ti.cbSize = sizeof(ti);
  62. ti.hwnd = owner;
  63. ti.lpszText = LPSTR_TEXTCALLBACK;
  64. ti.uFlags = 0/*TTF_TRACK | TTF_ABSOLUTE*/;
  65. SendMessage(hwnd, TTM_ADDTOOL, 0, (LPARAM)&ti);
  66. return hwnd;
  67. }
  68. ListWidgetTooltip*
  69. ListWidget_TooltipCreate(HWND hwnd)
  70. {
  71. ListWidgetTooltip *tooltip;
  72. tooltip = (ListWidgetTooltip*)malloc(sizeof(ListWidgetTooltip));
  73. if (NULL == tooltip)
  74. return FALSE;
  75. ZeroMemory(tooltip, sizeof(ListWidgetTooltip));
  76. tooltip->window = ListWidget_TooltipCreateWindow(hwnd, &tooltip->originalProc, tooltip);
  77. if (NULL == tooltip->window)
  78. {
  79. ListWidget_TooltipDestroy(tooltip);
  80. return NULL;
  81. }
  82. tooltip->owner = hwnd;
  83. ListWidget_TooltipFontChanged(tooltip);
  84. return tooltip;
  85. }
  86. void
  87. ListWidget_TooltipDestroy(ListWidgetTooltip *tooltip)
  88. {
  89. if (NULL != tooltip)
  90. {
  91. if (NULL != tooltip->window)
  92. DestroyWindow(tooltip->window);
  93. String_Free(tooltip->buffer);
  94. free(tooltip);
  95. }
  96. }
  97. void
  98. ListWidget_TooltipFontChanged(ListWidgetTooltip *tooltip)
  99. {
  100. WidgetStyle *style;
  101. RECT marginsRect;
  102. HDC hdc;
  103. long maxWidth;
  104. if (NULL == tooltip)
  105. return;
  106. if (NULL == tooltip->window)
  107. {
  108. // attempt to recover...
  109. tooltip->window = ListWidget_TooltipCreateWindow(tooltip->owner, &tooltip->originalProc, tooltip);
  110. if (NULL == tooltip->window)
  111. return;
  112. tooltip->active = FALSE;
  113. tooltip->part = ListWidgetItemPart_None;
  114. SetRectEmpty(&tooltip->partRect);
  115. }
  116. style = WIDGET_GET_STYLE(tooltip->owner);
  117. if (NULL == style)
  118. return;
  119. hdc = GetDCEx(tooltip->window, NULL, DCX_CACHE | DCX_NORESETATTRS);
  120. if (NULL != hdc)
  121. {
  122. HFONT font = (HFONT)SendMessage(tooltip->window, WM_GETFONT, 0, 0L);
  123. HFONT prevFont = SelectFont(hdc, font);
  124. maxWidth = Graphics_GetAveStrWidth(hdc, 36);
  125. SelectFont(hdc, prevFont);
  126. ReleaseDC(tooltip->window, hdc);
  127. }
  128. else
  129. maxWidth = 100;
  130. SendMessage(tooltip->window, TTM_SETMAXTIPWIDTH, 0, (LPARAM)maxWidth);
  131. WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(marginsRect.left, style, TOOLTIP_MARGIN_LEFT_DLU, 3);
  132. WIDGETSTYLE_DLU_TO_VERT_PX_MIN(marginsRect.top, style, TOOLTIP_MARGIN_TOP_DLU, 2);
  133. WIDGETSTYLE_DLU_TO_HORZ_PX_MIN(marginsRect.right, style, TOOLTIP_MARGIN_RIGHT_DLU, 3);
  134. WIDGETSTYLE_DLU_TO_VERT_PX_MIN(marginsRect.bottom, style, TOOLTIP_MARGIN_BOTTOM_DLU, 2);
  135. SendMessage(tooltip->window, TTM_SETMARGIN, 0, (LPARAM)&marginsRect);
  136. }
  137. void
  138. ListWidget_TooltipRelayMouseMessage(ListWidgetTooltip *tooltip, unsigned int message, unsigned int vKeys, const POINT *cursor)
  139. {
  140. if (NULL != tooltip &&
  141. NULL != tooltip->window &&
  142. FALSE != tooltip->active)
  143. {
  144. MSG msg;
  145. msg.hwnd = tooltip->owner;
  146. msg.message = message;
  147. msg.wParam = (WPARAM)vKeys;
  148. msg.lParam = MAKELPARAM(cursor->x, cursor->y);
  149. SendMessage(tooltip->window, TTM_RELAYEVENT, 0, (LPARAM)&msg);
  150. }
  151. }
  152. void
  153. ListWidget_TooltipHide(ListWidgetTooltip *tooltip)
  154. {
  155. if (NULL == tooltip ||
  156. NULL == tooltip->window ||
  157. FALSE == tooltip->active)
  158. {
  159. return;
  160. }
  161. tooltip->active = FALSE;
  162. tooltip->part = ListWidgetItemPart_None;
  163. SetRectEmpty(&tooltip->partRect);
  164. SendMessage(tooltip->window, TTM_ACTIVATE, FALSE, 0);
  165. }
  166. BOOL
  167. ListWidget_TooltipActivate(ListWidgetTooltip *tooltip, const RECT *rect)
  168. {
  169. TOOLINFO ti;
  170. POINT origin;
  171. if (NULL == tooltip ||
  172. NULL == tooltip->window)
  173. {
  174. return FALSE;
  175. }
  176. ZeroMemory(&ti, sizeof(ti));
  177. ti.cbSize = sizeof(ti);
  178. ti.hwnd = tooltip->owner;
  179. ti.uId = 0;
  180. if (FALSE == SendMessage(tooltip->window, TTM_GETTOOLINFO, 0, (LPARAM)&ti))
  181. return FALSE;
  182. if (FALSE != tooltip->active)
  183. {
  184. tooltip->active = FALSE;
  185. SendMessage(tooltip->window, TTM_ACTIVATE, FALSE, 0);
  186. }
  187. else
  188. SendMessage(tooltip->window, TTM_SETDELAYTIME, TTDT_INITIAL, MAKELPARAM(TOOLTIP_DELAY_INITIAL, 0));
  189. CopyRect(&ti.rect, rect);
  190. if (FALSE != ListWidget_GetViewOrigin(tooltip->owner, &origin))
  191. OffsetRect(&ti.rect, origin.x, origin.y);
  192. ti.lParam = NULL;
  193. ti.lpszText = LPSTR_TEXTCALLBACK;
  194. SendMessage(tooltip->window, TTM_SETTOOLINFO, 0, (LPARAM)&ti);
  195. if (FALSE == tooltip->active)
  196. {
  197. KillTimer(tooltip->window, 4);
  198. SendMessage(tooltip->window, TTM_ACTIVATE, TRUE, 0);
  199. tooltip->active = TRUE;
  200. }
  201. return TRUE;
  202. }
  203. BOOL
  204. ListWidget_TooltipUpdate(ListWidgetTooltip *tooltip, ListWidgetItem *item,
  205. ListWidgetItemPart part, const RECT *partRect)
  206. {
  207. BOOL tooltipValid;
  208. if (NULL == tooltip)
  209. return FALSE;
  210. if(FALSE == tooltip->active && NULL == item)
  211. return FALSE;
  212. if (0 != (ListWidgetItemPart_Activity & part))
  213. {
  214. part &= ~ListWidgetItemPart_Activity;
  215. part |= ListWidgetItemPart_Frame;
  216. }
  217. tooltipValid = (NULL != item && ListWidgetItemPart_None != part);
  218. if (tooltip->item == item &&
  219. tooltip->part == part &&
  220. (FALSE != (FALSE != tooltipValid) ?
  221. EqualRect(&tooltip->partRect, partRect) :
  222. IsRectEmpty(&tooltip->partRect)))
  223. {
  224. return FALSE;
  225. }
  226. tooltip->item = item;
  227. tooltip->part = part;
  228. if (FALSE != tooltipValid)
  229. CopyRect(&tooltip->partRect, partRect);
  230. else
  231. SetRectEmpty(&tooltip->partRect);
  232. if (FALSE == tooltipValid)
  233. {
  234. ListWidget_TooltipHide(tooltip);
  235. return FALSE;
  236. }
  237. return ListWidget_TooltipActivate(tooltip, &tooltip->partRect);
  238. }
  239. static BOOL
  240. ListWidget_TooltipFormatTip(ListWidget *self, HWND hwnd,
  241. const RECT *rect, wchar_t *buffer, size_t bufferMax)
  242. {
  243. WidgetStyle *style;
  244. ListWidgetItem *item;
  245. ListWidgetItemPart part;
  246. ListWidgetItemMetric metrics;
  247. RECT partRect;
  248. POINT pt, origin;
  249. if (NULL == self || NULL == rect)
  250. return FALSE;
  251. style = WIDGET_GET_STYLE(hwnd);
  252. if (NULL == style)
  253. return FALSE;
  254. if (FALSE == ListWidget_GetItemMetrics(style, &metrics))
  255. return FALSE;
  256. if (FALSE == ListWidget_GetViewOrigin(hwnd, &origin))
  257. {
  258. origin.x = 0;
  259. origin.y = 0;
  260. }
  261. pt.x = (rect->left + (rect->right - rect->left)/2) - origin.x;
  262. pt.y = (rect->top + (rect->bottom - rect->top)/2) - origin.y;
  263. item = ListWidget_GetItemFromPoint(self, pt);
  264. if (NULL == item)
  265. return FALSE;
  266. part = ListWidgetItemPart_Frame |
  267. ListWidgetItemPart_Command |
  268. ListWidgetItemPart_Spacebar |
  269. ListWidgetItemPart_Title;
  270. part = ListWidget_GetItemPartFromPoint(self, item, &metrics, pt, part, &partRect);
  271. switch(part)
  272. {
  273. case ListWidgetItemPart_Command:
  274. CopyRect(&partRect, rect);
  275. OffsetRect(&partRect, -origin.x - item->rect.left, -origin.y - item->rect.top);
  276. return ListWidget_FormatItemCommandTip(self, item, &partRect, buffer, bufferMax);
  277. case ListWidgetItemPart_Activity:
  278. case ListWidgetItemPart_Frame:
  279. return ListWidget_FormatItemTip(self, item, buffer, bufferMax);
  280. case ListWidgetItemPart_Spacebar:
  281. return ListWidget_FormatItemSpaceTip(self, item, buffer, bufferMax);
  282. case ListWidgetItemPart_Title:
  283. return ListWidget_FormatItemTitleTip(self, item, buffer, bufferMax);
  284. }
  285. return FALSE;
  286. }
  287. static void
  288. ListWidget_TooltipGetDispInfo(ListWidget *self, ListWidgetTooltip *tooltip, NMTTDISPINFO *dispInfo)
  289. {
  290. TOOLINFO ti;
  291. if (NULL == dispInfo)
  292. return;
  293. ZeroMemory(&ti, sizeof(ti));
  294. ti.cbSize = sizeof(ti);
  295. ti.hwnd = tooltip->owner;
  296. ti.uId = dispInfo->hdr.idFrom;
  297. String_Free(tooltip->buffer);
  298. tooltip->buffer = NULL;
  299. if (FALSE != SendMessage(dispInfo->hdr.hwndFrom, TTM_GETTOOLINFO, 0, (LPARAM)&ti))
  300. {
  301. wchar_t buffer[4096] = {0};
  302. if (FALSE != ListWidget_TooltipFormatTip(self, tooltip->owner, &ti.rect, buffer, ARRAYSIZE(buffer)))
  303. tooltip->buffer = String_Duplicate(buffer);
  304. }
  305. dispInfo->lpszText = tooltip->buffer;
  306. dispInfo->szText[0] = L'\0';
  307. dispInfo->hinst = NULL;
  308. dispInfo->uFlags = TTF_DI_SETITEM;
  309. }
  310. BOOL
  311. ListWidget_TooltipProcessNotification(ListWidget *self, ListWidgetTooltip *tooltip, NMHDR *pnmh, LRESULT *result)
  312. {
  313. if (NULL == tooltip ||
  314. NULL == pnmh ||
  315. pnmh->hwndFrom != tooltip->window)
  316. {
  317. return FALSE;
  318. }
  319. switch(pnmh->code)
  320. {
  321. case TTN_GETDISPINFO:
  322. ListWidget_TooltipGetDispInfo(self, tooltip, (NMTTDISPINFO*)pnmh);
  323. break;
  324. case TTN_SHOW:
  325. if (0 == (WS_VISIBLE & GetWindowStyle(tooltip->window)))
  326. tooltip->showTime = GetTickCount();
  327. if (FALSE != tooltip->active)
  328. {
  329. SendMessage(tooltip->window, TTM_SETDELAYTIME, TTDT_INITIAL, MAKELPARAM(TOOLTIP_DELAY_RESHOW, 0));
  330. }
  331. break;
  332. }
  333. return TRUE;
  334. }
  335. ListWidgetItem *
  336. ListWidget_TooltipGetCurrent(ListWidgetTooltip *tooltip, ListWidgetItemPart *part, RECT *partRect)
  337. {
  338. if (NULL == tooltip ||
  339. FALSE == tooltip->active ||
  340. NULL == tooltip->item)
  341. {
  342. return NULL;
  343. }
  344. if (NULL != part)
  345. *part = tooltip->part;
  346. if (NULL != partRect)
  347. CopyRect(partRect, &tooltip->partRect);
  348. return tooltip->item;
  349. }
  350. BOOL
  351. ListWidget_TooltipGetChanged(ListWidgetTooltip *tooltip, ListWidgetItem *item,
  352. ListWidgetItemPart part, const RECT *partRect)
  353. {
  354. if (NULL == tooltip)
  355. return FALSE;
  356. if (tooltip->item != item)
  357. return TRUE;
  358. if (NULL == item)
  359. return FALSE;
  360. if (tooltip->part != part)
  361. return TRUE;
  362. if (FALSE == EqualRect(&tooltip->partRect, partRect))
  363. return TRUE;
  364. return FALSE;
  365. }
  366. BOOL
  367. ListWidget_TooltipUpdateText(ListWidget *self, ListWidgetTooltip *tooltip, ListWidgetItem *item, TooltipUpdateReason reason)
  368. {
  369. TOOLINFO ti;
  370. ListWidgetItemPart mask;
  371. unsigned int windowStyle;
  372. unsigned long showTimeout, currentTime;
  373. if (NULL == self ||
  374. NULL == tooltip ||
  375. NULL == tooltip->active ||
  376. NULL == tooltip->window)
  377. {
  378. return FALSE;
  379. }
  380. if (NULL != item && tooltip->item != item)
  381. return FALSE;
  382. windowStyle = GetWindowStyle(tooltip->window);
  383. if (0 == (WS_VISIBLE & windowStyle))
  384. return FALSE;
  385. showTimeout = (unsigned long)SendMessage(tooltip->window, TTM_GETDELAYTIME, (WPARAM)TTDT_AUTOPOP, 0L);
  386. currentTime = GetTickCount();
  387. if (currentTime < tooltip->showTime)
  388. return FALSE;
  389. currentTime -= tooltip->showTime;
  390. if (showTimeout > (currentTime + 10))
  391. showTimeout -= currentTime;
  392. else
  393. return FALSE;
  394. KillTimer(tooltip->window, 4);
  395. switch(reason)
  396. {
  397. case Tooltip_DeviceTitleChanged:
  398. case Tooltip_DeviceModelChanged:
  399. case Tooltip_DeviceStatusChanged:
  400. mask = ListWidgetItemPart_Frame |
  401. ListWidgetItemPart_Activity |
  402. ListWidgetItemPart_Title;
  403. if (0 == (mask & tooltip->part))
  404. return FALSE;
  405. break;
  406. case Tooltip_DeviceSpaceChanged:
  407. mask = ListWidgetItemPart_Frame |
  408. ListWidgetItemPart_Activity |
  409. ListWidgetItemPart_Spacebar;
  410. if (0 == (mask & tooltip->part))
  411. return FALSE;
  412. break;
  413. /*case Tooltip_DeviceActivityChanged:
  414. mask = ListWidgetItemPart_Frame |
  415. ListWidgetItemPart_Activity;
  416. if (0 == (mask & tooltip->part))
  417. return FALSE;
  418. break;*/
  419. }
  420. ZeroMemory(&ti, sizeof(ti));
  421. ti.cbSize = sizeof(ti);
  422. ti.hwnd = tooltip->owner;
  423. ti.uId = 0;
  424. ti.lpszText = LPSTR_TEXTCALLBACK;
  425. tooltip->blockLocationChange = TRUE;
  426. SendMessage(tooltip->window, TTM_UPDATETIPTEXT, 0, (LPARAM)&ti);
  427. tooltip->blockLocationChange = FALSE;
  428. SetTimer(tooltip->window, 4, showTimeout, 0);
  429. return TRUE;
  430. }
  431. static LRESULT WINAPI
  432. ListWidgetTooltip_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  433. {
  434. ListWidgetTooltip *tooltip;
  435. tooltip = (ListWidgetTooltip*)GetProp(hwnd, MAKEINTATOM(LISTWIDGETTOOLTIP_PROP));
  436. if (NULL == tooltip ||
  437. NULL == tooltip->originalProc)
  438. {
  439. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  440. }
  441. switch(uMsg)
  442. {
  443. case WM_DESTROY:
  444. RemoveProp(hwnd, MAKEINTATOM(LISTWIDGETTOOLTIP_PROP));
  445. SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)tooltip->originalProc);
  446. CallWindowProc(tooltip->originalProc, hwnd, uMsg, wParam, lParam);
  447. tooltip->originalProc = NULL;
  448. tooltip->window = NULL;
  449. break;
  450. case WM_WINDOWPOSCHANGING:
  451. if (FALSE != tooltip->blockLocationChange)
  452. {
  453. WINDOWPOS *pwp;
  454. pwp = (WINDOWPOS*)lParam;
  455. if (NULL != pwp)
  456. {
  457. pwp->flags |= SWP_NOMOVE;
  458. }
  459. }
  460. break;
  461. }
  462. return CallWindowProc(tooltip->originalProc, hwnd, uMsg, wParam, lParam);
  463. }