Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

353 lines
12 KiB

  1. #include "stdafx.h"
  2. #include "common.h"
  3. #include "bidi.h"
  4. #ifdef _DEBUG
  5. #undef THIS_FILE
  6. static char BASED_CODE THIS_FILE[] = __FILE__;
  7. #endif
  8. #define new DEBUG_NEW
  9. #define ID_MY_EDITTIMER 10007
  10. #define EDIT_TIPTIMEOUT 10000
  11. // 2.0 seconds
  12. #define EDIT_TIPTIMEOUT_LOSTFOCUS 20000000
  13. inline UINT64 FILETIMEToUINT64( const FILETIME & FileTime )
  14. {
  15. ULARGE_INTEGER LargeInteger;
  16. LargeInteger.HighPart = FileTime.dwHighDateTime;
  17. LargeInteger.LowPart = FileTime.dwLowDateTime;
  18. return LargeInteger.QuadPart;
  19. }
  20. inline FILETIME UINT64ToFILETIME( UINT64 Int64Value )
  21. {
  22. ULARGE_INTEGER LargeInteger;
  23. LargeInteger.QuadPart = Int64Value;
  24. FILETIME FileTime;
  25. FileTime.dwHighDateTime = LargeInteger.HighPart;
  26. FileTime.dwLowDateTime = LargeInteger.LowPart;
  27. return FileTime;
  28. }
  29. typedef struct tagBALLOONCONTROLINFO
  30. {
  31. HWND hwndControl;
  32. HWND hwndBalloon;
  33. FILETIME ftStart;
  34. } BALLOONCONTROLINFO, *PBALLOONCONTROLINFO;
  35. // global
  36. BALLOONCONTROLINFO g_MyBalloonInfo;
  37. // forwards
  38. LRESULT CALLBACK Edit_BalloonTipParentSubclassProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam, UINT_PTR uID, ULONG_PTR dwRefData);
  39. VOID CALLBACK MyBalloonTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime);
  40. BOOL IsSupportTooltips(void)
  41. {
  42. BOOL bReturn = FALSE;
  43. HINSTANCE hComCtl = NULL;
  44. //
  45. // Comctl32.dll must be 5.80 or greater to use balloon tips. We check the dll version
  46. // by calling DllGetVersion in comctl32.dll.
  47. //
  48. hComCtl = LoadLibraryExA("comctl32.dll", NULL, 0);
  49. if (hComCtl != NULL)
  50. {
  51. typedef HRESULT (*DLLGETVERSIONPROC)(DLLVERSIONINFO* lpdvi);
  52. DLLGETVERSIONPROC fnDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hComCtl,"DllGetVersion");
  53. if (NULL == fnDllGetVersion)
  54. {
  55. //
  56. // DllGetVersion does not exist in Comctl32.dll. This mean the version is too old so we need to fail.
  57. //
  58. goto IsSupportTooltips_Exit;
  59. }
  60. else
  61. {
  62. DLLVERSIONINFO dvi;
  63. ZeroMemory(&dvi, sizeof(dvi));
  64. dvi.cbSize = sizeof(dvi);
  65. HRESULT hResult = (*fnDllGetVersion)(&dvi);
  66. if (SUCCEEDED(hResult))
  67. {
  68. //
  69. // Take the version returned and compare it to 5.80.
  70. //
  71. if (MAKELONG(dvi.dwMinorVersion,dvi.dwMajorVersion) < MAKELONG(80,5))
  72. {
  73. //CMTRACE2(TEXT("COMCTL32.DLL version - %d.%d"),dvi.dwMajorVersion,dvi.dwMinorVersion);
  74. //CMTRACE1(TEXT("COMCTL32.DLL MAKELONG - %li"),MAKELONG(dvi.dwMinorVersion,dvi.dwMajorVersion));
  75. //CMTRACE1(TEXT("Required minimum MAKELONG - %li"),MAKELONG(80,5));
  76. // Wrong DLL version
  77. bReturn = FALSE;
  78. goto IsSupportTooltips_Exit;
  79. }
  80. // version is larger than 5.80
  81. bReturn = TRUE;
  82. }
  83. }
  84. }
  85. IsSupportTooltips_Exit:
  86. if (hComCtl)
  87. {
  88. FreeLibrary(hComCtl);hComCtl=NULL;
  89. }
  90. return bReturn;
  91. }
  92. LRESULT Edit_BalloonTipSubclassParents(PBALLOONCONTROLINFO pMyBalloonInfo)
  93. {
  94. if (pMyBalloonInfo)
  95. {
  96. // Subclass all windows along the parent chain from the edit control
  97. // and in the same thread (can only subclass windows with same thread affinity)
  98. HWND hwndParent = GetAncestor(pMyBalloonInfo->hwndControl, GA_PARENT);
  99. DWORD dwTid = GetWindowThreadProcessId(pMyBalloonInfo->hwndControl, NULL);
  100. while (hwndParent && (dwTid == GetWindowThreadProcessId(hwndParent, NULL)))
  101. {
  102. SetWindowSubclass(hwndParent, Edit_BalloonTipParentSubclassProc, (UINT_PTR)pMyBalloonInfo->hwndControl, (DWORD_PTR) pMyBalloonInfo);
  103. hwndParent = GetAncestor(hwndParent, GA_PARENT);
  104. }
  105. }
  106. return TRUE;
  107. }
  108. HWND Edit_BalloonTipRemoveSubclasses(HWND hwndControl)
  109. {
  110. HWND hwndParent = GetAncestor(hwndControl, GA_PARENT);
  111. HWND hwndTopMost = NULL;
  112. DWORD dwTid = GetWindowThreadProcessId(hwndControl, NULL);
  113. while (hwndParent && (dwTid == GetWindowThreadProcessId(hwndParent, NULL)))
  114. {
  115. RemoveWindowSubclass(hwndParent, Edit_BalloonTipParentSubclassProc, (UINT_PTR) NULL);
  116. hwndTopMost = hwndParent;
  117. hwndParent = GetAncestor(hwndParent, GA_PARENT);
  118. }
  119. return hwndTopMost;
  120. }
  121. LRESULT Edit_HideBalloonTipHandler(PBALLOONCONTROLINFO pMyBalloonControl)
  122. {
  123. HWND hwndParent = 0;
  124. if (pMyBalloonControl)
  125. {
  126. KillTimer(pMyBalloonControl->hwndControl, ID_MY_EDITTIMER);
  127. if (SendMessage(pMyBalloonControl->hwndBalloon, TTM_ENUMTOOLS, 0, (LPARAM)0))
  128. {
  129. SendMessage(pMyBalloonControl->hwndBalloon, TTM_DELTOOL, 0, (LPARAM)0);
  130. }
  131. SendMessage(pMyBalloonControl->hwndBalloon, TTM_TRACKACTIVATE, FALSE, 0);
  132. DestroyWindow(pMyBalloonControl->hwndBalloon);
  133. pMyBalloonControl->hwndBalloon = NULL;
  134. hwndParent = Edit_BalloonTipRemoveSubclasses(pMyBalloonControl->hwndControl);
  135. if (hwndParent && IsWindow(hwndParent))
  136. {
  137. InvalidateRect(hwndParent, NULL, TRUE);
  138. UpdateWindow(hwndParent);
  139. }
  140. if (hwndParent != pMyBalloonControl->hwndControl)
  141. {
  142. RedrawWindow(pMyBalloonControl->hwndControl, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
  143. }
  144. }
  145. return TRUE;
  146. }
  147. void Edit_HideBalloonTipHandler(void)
  148. {
  149. if (g_MyBalloonInfo.hwndBalloon)
  150. {
  151. Edit_HideBalloonTipHandler(&g_MyBalloonInfo);
  152. }
  153. }
  154. VOID CALLBACK MyBalloonTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)
  155. {
  156. Edit_HideBalloonTipHandler(&g_MyBalloonInfo);
  157. }
  158. LRESULT CALLBACK Edit_BalloonTipParentSubclassProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam, UINT_PTR uID, ULONG_PTR dwRefData)
  159. {
  160. PBALLOONCONTROLINFO pMyBalloonControl = (PBALLOONCONTROLINFO) dwRefData;
  161. if (pMyBalloonControl)
  162. {
  163. switch (uMessage)
  164. {
  165. case WM_MOVE:
  166. case WM_SIZING:
  167. case WM_TIMER:
  168. //
  169. // dismiss any showing tips
  170. //
  171. if (pMyBalloonControl->hwndBalloon)
  172. {
  173. Edit_HideBalloonTipHandler(pMyBalloonControl);
  174. }
  175. break;
  176. case WM_KILLFOCUS: //for some reason we never get this notification that's why we need to use mousemove
  177. case WM_MOUSEMOVE:
  178. if (pMyBalloonControl->hwndBalloon)
  179. {
  180. FILETIME ftNow;
  181. ::GetSystemTimeAsFileTime(&ftNow);
  182. // Check if at least 2 seconds have gone by
  183. // if they have not then show for at least that long
  184. if ((FILETIMEToUINT64(ftNow) - FILETIMEToUINT64(g_MyBalloonInfo.ftStart)) > EDIT_TIPTIMEOUT_LOSTFOCUS)
  185. {
  186. // Displayed for longer than 2 seconds
  187. // that's long enough
  188. Edit_HideBalloonTipHandler(pMyBalloonControl);
  189. }
  190. else
  191. {
  192. // special case here
  193. // set timeout to kill the tip in 2 seconds
  194. KillTimer(g_MyBalloonInfo.hwndControl, ID_MY_EDITTIMER);
  195. SetTimer(g_MyBalloonInfo.hwndControl, ID_MY_EDITTIMER, EDIT_TIPTIMEOUT_LOSTFOCUS / 10000, (TIMERPROC) MyBalloonTimerProc);
  196. //Edit_HideBalloonTipHandler(pMyBalloonControl);
  197. }
  198. }
  199. break;
  200. case WM_DESTROY:
  201. // Clean up subclass
  202. RemoveWindowSubclass(hDlg, Edit_BalloonTipParentSubclassProc, (UINT_PTR) pMyBalloonControl->hwndControl);
  203. break;
  204. default:
  205. break;
  206. }
  207. }
  208. return DefSubclassProc(hDlg, uMessage, wParam, lParam);
  209. }
  210. LRESULT Edit_TrackBalloonTip(PBALLOONCONTROLINFO pMyBalloonControl)
  211. {
  212. if (pMyBalloonControl)
  213. {
  214. DWORD dwPackedCoords;
  215. HDC hdc = GetDC(pMyBalloonControl->hwndControl);
  216. RECT rcWindowCaret;
  217. RECT rcWindowControl;
  218. POINT ptBalloonSpear;
  219. ptBalloonSpear.x = 0;
  220. ptBalloonSpear.y = 0;
  221. POINT ptCaret;
  222. ptCaret.x = 0;
  223. ptCaret.y = 0;
  224. //
  225. // get the average size of one character
  226. //
  227. int cxCharOffset = 0;
  228. //cxCharOffset = TESTFLAG(GET_EXSTYLE(ped), WS_EX_RTLREADING) ? -ped->aveCharWidth : ped->aveCharWidth;
  229. TEXTMETRIC tm;
  230. GetTextMetrics(hdc, &tm);
  231. cxCharOffset = tm.tmAveCharWidth / 2;
  232. //
  233. // Get current caret position.
  234. //
  235. GetCaretPos( (POINT FAR*)& ptCaret);
  236. GetClientRect(pMyBalloonControl->hwndControl,&rcWindowCaret);
  237. ptBalloonSpear.x = ptCaret.x + cxCharOffset;
  238. ptBalloonSpear.y = rcWindowCaret.top + (rcWindowCaret.bottom - rcWindowCaret.top) / 2 ;
  239. //
  240. // Translate to window coords
  241. //
  242. GetWindowRect(pMyBalloonControl->hwndControl, &rcWindowControl);
  243. ptBalloonSpear.x += rcWindowControl.left;
  244. ptBalloonSpear.y += rcWindowControl.top;
  245. //
  246. // Position the tip stem at the caret position
  247. //
  248. dwPackedCoords = (DWORD) MAKELONG(ptBalloonSpear.x, ptBalloonSpear.y);
  249. SendMessage(pMyBalloonControl->hwndBalloon, TTM_TRACKPOSITION, 0, (LPARAM) dwPackedCoords);
  250. ReleaseDC(pMyBalloonControl->hwndBalloon,hdc);
  251. }
  252. return 1;
  253. }
  254. LRESULT Edit_ShowBalloonTipHandler(HWND hwndControl,LPCTSTR szText)
  255. {
  256. LRESULT lResult = FALSE;
  257. if (g_MyBalloonInfo.hwndBalloon)
  258. {
  259. Edit_HideBalloonTipHandler(&g_MyBalloonInfo);
  260. }
  261. g_MyBalloonInfo.hwndControl = hwndControl;
  262. KillTimer(g_MyBalloonInfo.hwndControl , ID_MY_EDITTIMER);
  263. g_MyBalloonInfo.hwndBalloon = CreateWindowEx(
  264. (IsBiDiLocalizedSystem() ? WS_EX_LAYOUTRTL : 0),
  265. TOOLTIPS_CLASS, NULL,
  266. WS_POPUP | TTS_NOPREFIX | TTS_BALLOON,
  267. CW_USEDEFAULT, CW_USEDEFAULT,
  268. CW_USEDEFAULT, CW_USEDEFAULT,
  269. hwndControl, NULL, _Module.GetResourceInstance(),
  270. NULL);
  271. if (NULL != g_MyBalloonInfo.hwndBalloon)
  272. {
  273. TOOLINFO ti = {0};
  274. ti.cbSize = TTTOOLINFOW_V2_SIZE;
  275. ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_SUBCLASS; // not sure if we need TTF_SUBCLASS
  276. ti.hwnd = hwndControl;
  277. ti.uId = (WPARAM) g_MyBalloonInfo.hwndBalloon;
  278. ti.lpszText = (LPTSTR) szText;
  279. // set the version so we can have non buggy mouse event forwarding
  280. SendMessage(g_MyBalloonInfo.hwndBalloon, CCM_SETVERSION, COMCTL32_VERSION, 0);
  281. SendMessage(g_MyBalloonInfo.hwndBalloon, TTM_ADDTOOL, 0, (LPARAM)&ti);
  282. SendMessage(g_MyBalloonInfo.hwndBalloon, TTM_SETMAXTIPWIDTH, 0, 300);
  283. //SendMessage(g_MyBalloonInfo.hwndBalloon, TTM_SETTITLE, (WPARAM) 0, (LPARAM) "");
  284. Edit_TrackBalloonTip(&g_MyBalloonInfo);
  285. SendMessage(g_MyBalloonInfo.hwndBalloon, TTM_TRACKACTIVATE, (WPARAM) TRUE, (LPARAM)&ti);
  286. SetFocus(g_MyBalloonInfo.hwndControl);
  287. Edit_BalloonTipSubclassParents(&g_MyBalloonInfo);
  288. //
  289. // set timeout to kill the tip
  290. //
  291. KillTimer(g_MyBalloonInfo.hwndControl, ID_MY_EDITTIMER);
  292. ::GetSystemTimeAsFileTime(&g_MyBalloonInfo.ftStart);
  293. SetTimer(g_MyBalloonInfo.hwndControl, ID_MY_EDITTIMER, EDIT_TIPTIMEOUT, (TIMERPROC) MyBalloonTimerProc);
  294. lResult = TRUE;
  295. }
  296. return lResult;
  297. }