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.

392 lines
13 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. extern HINSTANCE hDLLInstance;
  10. #define ID_MY_EDITTIMER 10007
  11. #define EDIT_TIPTIMEOUT 10000
  12. // 2.0 seconds
  13. #define EDIT_TIPTIMEOUT_LOSTFOCUS 20000000
  14. inline UINT64 FILETIMEToUINT64( const FILETIME & FileTime )
  15. {
  16. ULARGE_INTEGER LargeInteger;
  17. LargeInteger.HighPart = FileTime.dwHighDateTime;
  18. LargeInteger.LowPart = FileTime.dwLowDateTime;
  19. return LargeInteger.QuadPart;
  20. }
  21. inline FILETIME UINT64ToFILETIME( UINT64 Int64Value )
  22. {
  23. ULARGE_INTEGER LargeInteger;
  24. LargeInteger.QuadPart = Int64Value;
  25. FILETIME FileTime;
  26. FileTime.dwHighDateTime = LargeInteger.HighPart;
  27. FileTime.dwLowDateTime = LargeInteger.LowPart;
  28. return FileTime;
  29. }
  30. typedef struct tagBALLOONCONTROLINFO
  31. {
  32. HWND hwndControl;
  33. HWND hwndBalloon;
  34. FILETIME ftStart;
  35. } BALLOONCONTROLINFO, *PBALLOONCONTROLINFO;
  36. // global
  37. BALLOONCONTROLINFO g_MyBalloonInfo;
  38. // forwards
  39. LRESULT CALLBACK Edit_BalloonTipParentSubclassProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam, UINT_PTR uID, ULONG_PTR dwRefData);
  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 pi)
  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. return SetWindowSubclass(pi->hwndControl,
  108. Edit_BalloonTipParentSubclassProc, (UINT_PTR)pi->hwndControl, (DWORD_PTR)pi);
  109. }
  110. HWND Edit_BalloonTipRemoveSubclasses(HWND hwndControl)
  111. {
  112. //HWND hwndParent = GetAncestor(hwndControl, GA_PARENT);
  113. //HWND hwndTopMost = NULL;
  114. //DWORD dwTid = GetWindowThreadProcessId(hwndControl, NULL);
  115. //while (hwndParent && (dwTid == GetWindowThreadProcessId(hwndParent, NULL)))
  116. //{
  117. // RemoveWindowSubclass(hwndParent, Edit_BalloonTipParentSubclassProc, (UINT_PTR) NULL);
  118. // hwndTopMost = hwndParent;
  119. // hwndParent = GetAncestor(hwndParent, GA_PARENT);
  120. //}
  121. //return hwndTopMost;
  122. RemoveWindowSubclass(hwndControl, Edit_BalloonTipParentSubclassProc, (UINT_PTR)hwndControl);
  123. return NULL;
  124. }
  125. LRESULT Edit_HideBalloonTipHandler(PBALLOONCONTROLINFO pi)
  126. {
  127. HWND hwndParent = 0;
  128. if (pi)
  129. {
  130. KillTimer(pi->hwndControl, ID_MY_EDITTIMER);
  131. if (SendMessage(pi->hwndBalloon, TTM_ENUMTOOLS, 0, (LPARAM)0))
  132. {
  133. SendMessage(pi->hwndBalloon, TTM_DELTOOL, 0, (LPARAM)0);
  134. }
  135. SendMessage(pi->hwndBalloon, TTM_TRACKACTIVATE, FALSE, 0);
  136. DestroyWindow(pi->hwndBalloon);
  137. pi->hwndBalloon = NULL;
  138. RemoveWindowSubclass(pi->hwndControl, Edit_BalloonTipParentSubclassProc, (UINT_PTR)pi->hwndControl);
  139. //hwndParent = Edit_BalloonTipRemoveSubclasses(pi->hwndControl);
  140. //if (hwndParent && IsWindow(hwndParent))
  141. //{
  142. // InvalidateRect(hwndParent, NULL, TRUE);
  143. // UpdateWindow(hwndParent);
  144. //}
  145. //if (hwndParent != pMyBalloonControl->hwndControl)
  146. //{
  147. // RedrawWindow(pMyBalloonControl->hwndControl, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
  148. //}
  149. }
  150. return TRUE;
  151. }
  152. void Edit_HideBalloonTipHandler(void)
  153. {
  154. if (g_MyBalloonInfo.hwndBalloon)
  155. {
  156. Edit_HideBalloonTipHandler(&g_MyBalloonInfo);
  157. }
  158. }
  159. LRESULT CALLBACK
  160. Edit_BalloonTipParentSubclassProc(
  161. HWND hDlg,
  162. UINT uMessage,
  163. WPARAM wParam,
  164. LPARAM lParam,
  165. UINT_PTR uID,
  166. ULONG_PTR dwRefData)
  167. {
  168. PBALLOONCONTROLINFO pi = (PBALLOONCONTROLINFO) dwRefData;
  169. switch (uMessage)
  170. {
  171. case WM_MOVE:
  172. case WM_SIZING:
  173. case WM_LBUTTONDOWN:
  174. case WM_LBUTTONDBLCLK:
  175. case WM_MBUTTONDOWN:
  176. case WM_MBUTTONDBLCLK:
  177. case WM_RBUTTONDOWN:
  178. case WM_RBUTTONDBLCLK:
  179. case WM_KEYDOWN:
  180. case WM_CHAR:
  181. if (pi->hwndBalloon)
  182. {
  183. Edit_HideBalloonTipHandler(pi);
  184. }
  185. break;
  186. case WM_KILLFOCUS:
  187. /*
  188. // dont do this for common build
  189. // just for common2 build
  190. if (pi->hwndBalloon)
  191. {
  192. FILETIME ftNow;
  193. ::GetSystemTimeAsFileTime(&ftNow);
  194. // Check if at least 2 seconds have gone by
  195. // if they have not then show for at least that long
  196. if ((FILETIMEToUINT64(ftNow) - FILETIMEToUINT64(g_MyBalloonInfo.ftStart)) > EDIT_TIPTIMEOUT_LOSTFOCUS)
  197. {
  198. // Displayed for longer than 2 seconds
  199. // that's long enough
  200. Edit_HideBalloonTipHandler(pi);
  201. }
  202. else
  203. {
  204. // special case here
  205. // set timeout to kill the tip in 2 seconds
  206. KillTimer(g_MyBalloonInfo.hwndControl, ID_MY_EDITTIMER);
  207. SetTimer(g_MyBalloonInfo.hwndControl, ID_MY_EDITTIMER, EDIT_TIPTIMEOUT_LOSTFOCUS / 10000, NULL);
  208. //Edit_HideBalloonTipHandler(pi);
  209. }
  210. }
  211. */
  212. if (pi->hwndBalloon)
  213. {
  214. Edit_HideBalloonTipHandler(pi);
  215. }
  216. break;
  217. case WM_TIMER:
  218. if (ID_MY_EDITTIMER == wParam)
  219. {
  220. Edit_HideBalloonTipHandler(pi);
  221. return 0;
  222. }
  223. break;
  224. case WM_DESTROY:
  225. // Clean up subclass
  226. RemoveWindowSubclass(hDlg, Edit_BalloonTipParentSubclassProc, uID);
  227. break;
  228. default:
  229. break;
  230. }
  231. return DefSubclassProc(hDlg, uMessage, wParam, lParam);
  232. }
  233. LRESULT Edit_TrackBalloonTip(PBALLOONCONTROLINFO pMyBalloonControl)
  234. {
  235. if (pMyBalloonControl)
  236. {
  237. DWORD dwPackedCoords;
  238. HDC hdc = GetDC(pMyBalloonControl->hwndControl);
  239. RECT rcWindowCaret;
  240. RECT rcWindowControl;
  241. POINT ptBalloonSpear;
  242. ptBalloonSpear.x = 0;
  243. ptBalloonSpear.y = 0;
  244. POINT ptCaret;
  245. ptCaret.x = 0;
  246. ptCaret.y = 0;
  247. //
  248. // get the average size of one character
  249. //
  250. int cxCharOffset = 0;
  251. //cxCharOffset = TESTFLAG(GET_EXSTYLE(ped), WS_EX_RTLREADING) ? -ped->aveCharWidth : ped->aveCharWidth;
  252. TEXTMETRIC tm;
  253. GetTextMetrics(hdc, &tm);
  254. cxCharOffset = tm.tmAveCharWidth / 2;
  255. //
  256. // Get current caret position.
  257. //
  258. GetCaretPos( (POINT FAR*)& ptCaret);
  259. GetClientRect(pMyBalloonControl->hwndControl,&rcWindowCaret);
  260. ptBalloonSpear.x = ptCaret.x + cxCharOffset;
  261. ptBalloonSpear.y = rcWindowCaret.top + (rcWindowCaret.bottom - rcWindowCaret.top) / 2 ;
  262. //
  263. // Translate to window coords
  264. //
  265. GetWindowRect(pMyBalloonControl->hwndControl, &rcWindowControl);
  266. ptBalloonSpear.x += rcWindowControl.left;
  267. ptBalloonSpear.y += rcWindowControl.top;
  268. //
  269. // Position the tip stem at the caret position
  270. //
  271. dwPackedCoords = (DWORD) MAKELONG(ptBalloonSpear.x, ptBalloonSpear.y);
  272. SendMessage(pMyBalloonControl->hwndBalloon, TTM_TRACKPOSITION, 0, (LPARAM) dwPackedCoords);
  273. ReleaseDC(pMyBalloonControl->hwndBalloon,hdc);
  274. }
  275. return 1;
  276. }
  277. VOID CALLBACK MyBalloonTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)
  278. {
  279. Edit_HideBalloonTipHandler(&g_MyBalloonInfo);
  280. }
  281. #define LIMITINPUTTIMERID 472
  282. LRESULT Edit_ShowBalloonTipHandler(HWND hwndControl, LPCTSTR szText)
  283. {
  284. LRESULT lResult = FALSE;
  285. // Close any other subclasses Balloon that could be poped up.
  286. // Kill any Tooltip that could have been there
  287. // from the SHLimitInputEditWithFlags call...
  288. // we don't want this here since we could
  289. // be poping up another Tooltip of our own...
  290. // And thus user will have two...
  291. ::SendMessage(hwndControl, WM_TIMER, LIMITINPUTTIMERID, 0);
  292. if (g_MyBalloonInfo.hwndBalloon)
  293. {
  294. Edit_HideBalloonTipHandler(&g_MyBalloonInfo);
  295. }
  296. g_MyBalloonInfo.hwndControl = hwndControl;
  297. KillTimer(g_MyBalloonInfo.hwndControl , ID_MY_EDITTIMER);
  298. g_MyBalloonInfo.hwndBalloon = CreateWindowEx(
  299. (IsBiDiLocalizedSystem() ? WS_EX_LAYOUTRTL : 0),
  300. TOOLTIPS_CLASS, NULL,
  301. WS_POPUP | TTS_NOPREFIX | TTS_BALLOON,
  302. CW_USEDEFAULT, CW_USEDEFAULT,
  303. CW_USEDEFAULT, CW_USEDEFAULT,
  304. hwndControl, NULL, hDLLInstance,
  305. NULL);
  306. if (NULL != g_MyBalloonInfo.hwndBalloon)
  307. {
  308. TOOLINFO ti = {0};
  309. ti.cbSize = TTTOOLINFOW_V2_SIZE;
  310. ti.uFlags = TTF_IDISHWND | TTF_TRACK | TTF_SUBCLASS; // not sure if we need TTF_SUBCLASS
  311. ti.hwnd = hwndControl;
  312. ti.uId = (WPARAM) g_MyBalloonInfo.hwndBalloon;
  313. ti.lpszText = (LPTSTR) szText;
  314. // set the version so we can have non buggy mouse event forwarding
  315. SendMessage(g_MyBalloonInfo.hwndBalloon, CCM_SETVERSION, COMCTL32_VERSION, 0);
  316. SendMessage(g_MyBalloonInfo.hwndBalloon, TTM_ADDTOOL, 0, (LPARAM)&ti);
  317. SendMessage(g_MyBalloonInfo.hwndBalloon, TTM_SETMAXTIPWIDTH, 0, 300);
  318. //SendMessage(g_MyBalloonInfo.hwndBalloon, TTM_SETTITLE, (WPARAM) 0, (LPARAM) "");
  319. // SetFocus must happen before Edit_TrackBalloonTip
  320. // for somereason, GetCaretPos() will return different value otherwise.
  321. SetFocus(g_MyBalloonInfo.hwndControl);
  322. Edit_TrackBalloonTip(&g_MyBalloonInfo);
  323. SendMessage(g_MyBalloonInfo.hwndBalloon, TTM_TRACKACTIVATE, (WPARAM) TRUE, (LPARAM)&ti);
  324. // Edit_BalloonTipSubclassParents(&g_MyBalloonInfo);
  325. if (SetWindowSubclass(g_MyBalloonInfo.hwndControl,
  326. Edit_BalloonTipParentSubclassProc, (UINT_PTR)g_MyBalloonInfo.hwndControl,
  327. (DWORD_PTR)&g_MyBalloonInfo)
  328. )
  329. {
  330. //
  331. // set timeout to kill the tip
  332. //
  333. KillTimer(g_MyBalloonInfo.hwndControl, ID_MY_EDITTIMER);
  334. ::GetSystemTimeAsFileTime(&g_MyBalloonInfo.ftStart);
  335. SetTimer(g_MyBalloonInfo.hwndControl, ID_MY_EDITTIMER, EDIT_TIPTIMEOUT, NULL/*(TIMERPROC) MyBalloonTimerProc*/);
  336. lResult = TRUE;
  337. }
  338. }
  339. return lResult;
  340. }