Source code of Windows XP (NT5)
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.

1861 lines
58 KiB

  1. /*
  2. ** STATUS.C
  3. **
  4. ** Status bar code
  5. **
  6. */
  7. #include "ctlspriv.h"
  8. #define MAX_TOOLTIP_STRING 80
  9. #define SB_HITTEST_NOITEM -2
  10. typedef struct {
  11. ULONG_PTR dwString;
  12. UINT uType;
  13. int right;
  14. HICON hIcon;
  15. SIZE sizeIcon;
  16. LPTSTR pszToolTip;
  17. BOOL fNeedToTip;
  18. } STRINGINFO, *PSTRINGINFO;
  19. typedef struct {
  20. CCONTROLINFO ci;
  21. HWND hwndToolTips;
  22. HFONT hStatFont;
  23. BOOL bDefFont;
  24. int nFontHeight;
  25. int nMinHeight;
  26. int nBorderX, nBorderY, nBorderPart;
  27. int nLastX; // for invalidating unclipped right side
  28. int dxGripper; // 0 if no gripper
  29. int dyGripper;
  30. UINT uiCodePage; // code page
  31. STRINGINFO sSimple;
  32. int nParts;
  33. COLORREF _clrBk;
  34. HTHEME hTheme;
  35. PSTRINGINFO sInfo;
  36. } STATUSINFO, *PSTATUSINFO;
  37. #define SBT_NORMAL 0xf000
  38. #define SBT_NULL 0x0000 /* Some code depends on this being 0 */
  39. #define SBT_ALLTYPES 0xf000 /* this does NOT include rtlred */
  40. #define SBT_NOSIMPLE 0x00ff /* Flags to indicate normal status bar */
  41. #define MAXPARTS 256
  42. // Bug#94368 raymondc v6: This limit isn't big enough on large res screens
  43. #define MAX_STATUS_TEXT_LEN 128
  44. #define CharNextEx(cp, sz, f) ((sz)+1)
  45. BOOL SBSetText(PSTATUSINFO pStatusInfo, WPARAM wParam, LPCTSTR lpsz);
  46. void SBSetBorders(PSTATUSINFO pStatusInfo, LPINT lpInt);
  47. void SBSetFont(PSTATUSINFO pStatusInfo, HFONT hFont, BOOL bInvalidate);
  48. void WINAPI DrawStatusTextEx(PSTATUSINFO pStatusInfo, HDC hDC, LPRECT lprc, LPCTSTR pszText, STRINGINFO * psi, UINT uFlags, BOOL fClipText);
  49. void RecalcTooltipRects(PSTATUSINFO pStatusinfo);
  50. PSTRINGINFO GetStringInfo(PSTATUSINFO pStatusInfo, int nIndex);
  51. int IndexFromPt(PSTATUSINFO pStatusInfo, POINT pt);
  52. void StatusUpdateToolTips(PSTATUSINFO psi);
  53. void GetNewMetrics(PSTATUSINFO pStatusInfo, HDC hDC, HFONT hNewFont)
  54. {
  55. HFONT hOldFont;
  56. /* HACK! Pass in -1 to just delete the old font
  57. */
  58. if (hNewFont != (HFONT)-1)
  59. {
  60. HRESULT hr = E_FAIL;
  61. TEXTMETRIC tm;
  62. if (pStatusInfo->hTheme)
  63. {
  64. hr = GetThemeTextMetrics(pStatusInfo->hTheme, hDC, 0, 0, &tm);
  65. }
  66. if (FAILED(hr))
  67. {
  68. hOldFont = 0;
  69. if (hNewFont)
  70. hOldFont = SelectObject(hDC, hNewFont);
  71. GetTextMetrics(hDC, &tm);
  72. if (hOldFont)
  73. SelectObject(hDC, hOldFont);
  74. }
  75. pStatusInfo->nFontHeight = tm.tmHeight + tm.tmInternalLeading;
  76. // For far east font which has no internal leading
  77. if ( !tm.tmInternalLeading )
  78. pStatusInfo->nFontHeight += g_cyBorder * 2;
  79. }
  80. }
  81. void NewFont(PSTATUSINFO pStatusInfo, HFONT hNewFont, BOOL fResize)
  82. {
  83. HFONT hOldFont;
  84. BOOL bDelFont;
  85. HDC hDC;
  86. hOldFont = pStatusInfo->hStatFont;
  87. bDelFont = pStatusInfo->bDefFont;
  88. hDC = GetDC(pStatusInfo->ci.hwnd);
  89. if (hNewFont) {
  90. pStatusInfo->hStatFont = hNewFont;
  91. pStatusInfo->bDefFont = FALSE;
  92. pStatusInfo->uiCodePage = GetCodePageForFont(hNewFont);
  93. } else {
  94. if (bDelFont) {
  95. /* I will reuse the default font, so don't delete it later
  96. */
  97. hNewFont = pStatusInfo->hStatFont;
  98. bDelFont = FALSE;
  99. } else {
  100. #ifndef DBCS_FONT
  101. hNewFont = CCCreateStatusFont();
  102. if (!hNewFont)
  103. #endif // DBCS_FONT
  104. hNewFont = g_hfontSystem;
  105. pStatusInfo->hStatFont = hNewFont;
  106. pStatusInfo->bDefFont = BOOLFROMPTR(hNewFont);
  107. }
  108. }
  109. #ifndef DBCS_FONT
  110. /* We delete the old font after creating the new one in case they are
  111. * the same; this should help GDI a little
  112. */
  113. if (bDelFont)
  114. DeleteObject(hOldFont);
  115. #endif
  116. GetNewMetrics(pStatusInfo, hDC, hNewFont);
  117. ReleaseDC(pStatusInfo->ci.hwnd, hDC);
  118. // My font changed, so maybe I should resize to match
  119. if (fResize)
  120. SendMessage(pStatusInfo->ci.hwnd, WM_SIZE, 0, 0L);
  121. }
  122. /* We should send messages instead of calling things directly so we can
  123. * be subclassed more easily.
  124. */
  125. LRESULT InitStatusWnd(HWND hWnd, LPCREATESTRUCT lpCreate)
  126. {
  127. int nBorders[3];
  128. PSTATUSINFO pStatusInfo = (PSTATUSINFO)LocalAlloc(LPTR, sizeof(STATUSINFO));
  129. if (!pStatusInfo)
  130. return -1; // fail the window create
  131. // Start out with one part
  132. pStatusInfo->sInfo = (PSTRINGINFO)LocalAlloc(LPTR, sizeof(STRINGINFO));
  133. if (!pStatusInfo->sInfo)
  134. {
  135. LocalFree(pStatusInfo);
  136. return -1; // fail the window create
  137. }
  138. SetWindowPtr(hWnd, 0, pStatusInfo);
  139. CIInitialize(&pStatusInfo->ci, hWnd, lpCreate);
  140. pStatusInfo->sSimple.uType = SBT_NOSIMPLE | SBT_NULL;
  141. pStatusInfo->sSimple.right = -1;
  142. pStatusInfo->uiCodePage = CP_ACP;
  143. pStatusInfo->nParts = 1;
  144. pStatusInfo->sInfo[0].uType = SBT_NULL;
  145. pStatusInfo->sInfo[0].right = -1;
  146. pStatusInfo->_clrBk = CLR_DEFAULT;
  147. pStatusInfo->hTheme = OpenThemeData(pStatusInfo->ci.hwnd, L"Status");
  148. // Save the window text in our struct, and let USER store the NULL string
  149. SBSetText(pStatusInfo, 0, lpCreate->lpszName);
  150. lpCreate->lpszName = c_szNULL;
  151. // Don't resize because MFC doesn't like getting funky
  152. // messages before the window is fully created. USER will send
  153. // us a WM_SIZE message after the WM_CREATE returns, so we'll
  154. // get it sooner or later.
  155. NewFont(pStatusInfo, 0, FALSE);
  156. nBorders[0] = -1; // use default border widths
  157. nBorders[1] = -1;
  158. nBorders[2] = -1;
  159. SBSetBorders(pStatusInfo, nBorders);
  160. #define GRIPSIZE (g_cxVScroll + g_cxBorder) // make the default look good
  161. if ((lpCreate->style & SBARS_SIZEGRIP) ||
  162. ((GetWindowStyle(lpCreate->hwndParent) & WS_THICKFRAME) &&
  163. !(lpCreate->style & (CCS_NOPARENTALIGN | CCS_TOP | CCS_NOMOVEY))))
  164. if (pStatusInfo->hTheme)
  165. {
  166. RECT rcContent = {0, 0, GRIPSIZE, GRIPSIZE};
  167. GetThemeBackgroundExtent(pStatusInfo->hTheme, NULL, SP_GRIPPER, 0, &rcContent, &rcContent);
  168. pStatusInfo->dxGripper = RECTWIDTH(rcContent);
  169. pStatusInfo->dyGripper = RECTHEIGHT(rcContent);
  170. }
  171. else
  172. {
  173. pStatusInfo->dxGripper = GRIPSIZE;
  174. }
  175. return 0; // success
  176. }
  177. // lprc is left unchanged, but used as scratch
  178. void WINAPI DrawStatusText(HDC hDC, LPRECT lprc, LPCTSTR pszText, UINT uFlags)
  179. {
  180. DrawStatusTextEx(NULL, hDC, lprc, pszText, NULL, uFlags, FALSE);
  181. }
  182. void WINAPI DrawStatusTextA(HDC hDC, LPRECT lprc, LPCSTR pszText, UINT uFlags)
  183. {
  184. INT cch;
  185. LPWSTR lpw;
  186. cch = lstrlenA(pszText);
  187. lpw = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, ((cch + 1) * sizeof(TCHAR)));
  188. if (!lpw)
  189. {
  190. return;
  191. }
  192. MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszText, cch, lpw, cch);
  193. DrawStatusTextW(hDC, lprc, lpw, uFlags);
  194. LocalFree((LPVOID)lpw);
  195. }
  196. BOOL Status_GetRect(PSTATUSINFO pStatusInfo, int nthPart, LPRECT lprc)
  197. {
  198. PSTRINGINFO pStringInfo = pStatusInfo->sInfo;
  199. if (lprc == NULL)
  200. return FALSE;
  201. if (pStatusInfo->sSimple.uType & SBT_NOSIMPLE)
  202. {
  203. RECT rc;
  204. int nRightMargin, i;
  205. /* Get the client rect and inset the top and bottom. Then set
  206. * up the right side for entry into the loop
  207. */
  208. GetClientRect(pStatusInfo->ci.hwnd, &rc);
  209. if (pStatusInfo->dxGripper && !IsZoomed(pStatusInfo->ci.hwndParent))
  210. {
  211. rc.right = rc.right - pStatusInfo->dxGripper + pStatusInfo->nBorderX;
  212. }
  213. rc.top += pStatusInfo->nBorderY;
  214. nRightMargin = rc.right - pStatusInfo->nBorderX;
  215. rc.right = pStatusInfo->nBorderX - pStatusInfo->nBorderPart;
  216. for (i = 0; i < pStatusInfo->nParts; ++i, ++pStringInfo)
  217. {
  218. // WARNING! This pixel computation is also in PaintStatusWnd,
  219. // so make sure the two algorithms are in sync.
  220. if (pStringInfo->right == 0)
  221. continue;
  222. rc.left = rc.right + pStatusInfo->nBorderPart;
  223. rc.right = pStringInfo->right;
  224. // size the right-most one to the end with room for border
  225. if (rc.right < 0 || rc.right > nRightMargin)
  226. rc.right = nRightMargin;
  227. // if the part is real small, don't show it
  228. // Bug keep the rc.left valid in case this item happens to
  229. // be the nthPart.
  230. if ((rc.right - rc.left) < pStatusInfo->nBorderPart)
  231. rc.left = rc.right;
  232. if (i == nthPart)
  233. {
  234. if (nthPart == pStatusInfo->nParts - 1)
  235. {
  236. rc.right -= pStatusInfo->dxGripper;
  237. }
  238. *lprc = rc;
  239. return TRUE;
  240. }
  241. }
  242. }
  243. return FALSE;
  244. }
  245. void PaintStatusWnd(PSTATUSINFO pStatusInfo, HDC hdcIn, PSTRINGINFO pStringInfo, int nParts, int nBorderX)
  246. {
  247. PAINTSTRUCT ps;
  248. RECT rc, rcGripper, rcClient;
  249. int nRightMargin, i;
  250. HFONT hOldFont = NULL;
  251. UINT uType;
  252. BOOL bDrawGrip;
  253. // paint the whole client area
  254. GetClientRect(pStatusInfo->ci.hwnd, &rcClient);
  255. rc = rcClient;
  256. if (hdcIn)
  257. {
  258. ps.rcPaint = rc;
  259. ps.hdc = hdcIn;
  260. }
  261. else
  262. BeginPaint(pStatusInfo->ci.hwnd, &ps);
  263. rc.top += pStatusInfo->nBorderY;
  264. bDrawGrip = pStatusInfo->dxGripper && !IsZoomed(pStatusInfo->ci.hwndParent);
  265. if (bDrawGrip)
  266. rcGripper = rc;
  267. nRightMargin = rc.right - nBorderX;
  268. rc.right = nBorderX - pStatusInfo->nBorderPart;
  269. if (pStatusInfo->hStatFont)
  270. hOldFont = SelectObject(ps.hdc, pStatusInfo->hStatFont);
  271. for (i=0; i<nParts; ++i, ++pStringInfo)
  272. {
  273. BOOL fClipRight = FALSE;
  274. // WARNING! This pixel computation is also in Status_GetRect,
  275. // so make sure the two algorithms are in sync.
  276. if (pStringInfo->right == 0)
  277. continue;
  278. rc.left = rc.right + pStatusInfo->nBorderPart;
  279. rc.right = pStringInfo->right;
  280. // size the right-most one to the end with room for border
  281. if (rc.right < 0 || rc.right > nRightMargin)
  282. rc.right = nRightMargin;
  283. if(g_fMEEnabled && (rc.right > (nRightMargin-pStatusInfo->dxGripper)))
  284. {
  285. //
  286. // for MidEast we DONT overpaint the rhs with the grip, this will
  287. // lose the begining of the text.
  288. //
  289. rc.right = nRightMargin-pStatusInfo->dxGripper;
  290. }
  291. if (pStatusInfo->dxGripper && !IsZoomed(pStatusInfo->ci.hwndParent)
  292. && rc.right > rcClient.right - pStatusInfo->dxGripper + 1)
  293. {
  294. fClipRight = TRUE;
  295. }
  296. DebugMsg(TF_STATUS, TEXT("SBPaint: part=%d, x/y=%d/%d"), i, rc.left, rc.right);
  297. // if the part is real small, don't show it
  298. if (((rc.right - rc.left) < pStatusInfo->nBorderPart) || !RectVisible(ps.hdc, &rc))
  299. continue;
  300. uType = pStringInfo->uType;
  301. if ((uType&SBT_ALLTYPES) == SBT_NORMAL)
  302. {
  303. DrawStatusTextEx(pStatusInfo, ps.hdc, &rc, (LPTSTR)OFFSETOF(pStringInfo->dwString), pStringInfo, uType, fClipRight);
  304. }
  305. else
  306. {
  307. DrawStatusTextEx(pStatusInfo, ps.hdc, &rc, c_szNULL, pStringInfo, uType, fClipRight);
  308. if (uType & SBT_OWNERDRAW)
  309. {
  310. DRAWITEMSTRUCT di;
  311. di.CtlID = GetWindowID(pStatusInfo->ci.hwnd);
  312. di.itemID = i;
  313. di.hwndItem = pStatusInfo->ci.hwnd;
  314. di.hDC = ps.hdc;
  315. di.rcItem = rc;
  316. InflateRect(&di.rcItem, -g_cxBorder, -g_cyBorder);
  317. di.itemData = pStringInfo->dwString;
  318. SaveDC(ps.hdc);
  319. IntersectClipRect(ps.hdc, di.rcItem.left, di.rcItem.top,
  320. di.rcItem.right, di.rcItem.bottom);
  321. SendMessage(pStatusInfo->ci.hwndParent, WM_DRAWITEM, di.CtlID,
  322. (LPARAM)(LPTSTR)&di);
  323. RestoreDC(ps.hdc, -1);
  324. }
  325. }
  326. }
  327. if (bDrawGrip)
  328. {
  329. RECT rcTemp;
  330. COLORREF crBkColorOld;
  331. COLORREF crBkColor;
  332. pStatusInfo->dxGripper = min(pStatusInfo->dxGripper, pStatusInfo->nFontHeight);
  333. if (pStatusInfo->hTheme)
  334. {
  335. rcGripper.left = rcGripper.right - pStatusInfo->dxGripper;
  336. rcGripper.top = rcGripper.bottom - pStatusInfo->dyGripper;
  337. DrawThemeBackground(pStatusInfo->hTheme,
  338. ps.hdc,
  339. SP_GRIPPER,
  340. 1,
  341. &rcGripper, 0);
  342. }
  343. else
  344. {
  345. // draw the grip
  346. rcGripper.right -= g_cxBorder; // inside the borders
  347. rcGripper.bottom -= g_cyBorder;
  348. rcGripper.left = rcGripper.right - pStatusInfo->dxGripper; // make it square
  349. rcGripper.top += g_cyBorder;
  350. // rcGripper.top = rcGripper.bottom - pStatusInfo->dxGripper;
  351. crBkColor = g_clrBtnFace;
  352. if ((pStatusInfo->_clrBk != CLR_DEFAULT))
  353. crBkColor = pStatusInfo->_clrBk;
  354. crBkColorOld = SetBkColor(ps.hdc, crBkColor);
  355. DrawFrameControl(ps.hdc, &rcGripper, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
  356. // clear out the border edges to make this appear on the same level
  357. // NOTE: these values line up right only for the default scroll bar
  358. // width. for others this is close enough...
  359. // right border
  360. rcTemp.top = rcGripper.bottom - pStatusInfo->dxGripper + g_cyBorder + g_cyEdge;
  361. rcTemp.left = rcGripper.right;
  362. rcTemp.bottom = rcGripper.bottom;
  363. rcTemp.right = rcGripper.right + g_cxBorder;
  364. FillRectClr(ps.hdc, &rcTemp, crBkColor);
  365. // bottom border
  366. rcTemp.top = rcGripper.bottom;
  367. rcTemp.left = rcGripper.left + g_cyBorder + g_cxEdge;
  368. rcTemp.bottom = rcGripper.bottom + g_cyBorder;
  369. rcTemp.right = rcGripper.right + g_cxBorder;
  370. FillRectClr(ps.hdc, &rcTemp, crBkColor);
  371. SetBkColor(ps.hdc, crBkColorOld);
  372. }
  373. }
  374. if (hOldFont)
  375. SelectObject(ps.hdc, hOldFont);
  376. if (hdcIn == NULL)
  377. EndPaint(pStatusInfo->ci.hwnd, &ps);
  378. }
  379. BOOL SetStatusText(PSTATUSINFO pStatusInfo, PSTRINGINFO pStringInfo, UINT uPart, LPCTSTR lpStr)
  380. {
  381. PTSTR pString;
  382. UINT wLen;
  383. int nPart;
  384. RECT rc;
  385. nPart = LOBYTE(uPart);
  386. /* Note it is up to the app the dispose of the previous itemData for
  387. * SBT_OWNERDRAW
  388. */
  389. if ((pStringInfo->uType&SBT_ALLTYPES) == SBT_NORMAL)
  390. LocalFree((HLOCAL)OFFSETOF(pStringInfo->dwString));
  391. /* Set to the NULL string in case anything goes wrong
  392. *
  393. * But be careful to preserve simple-ness if this is the simple
  394. * pane being updated.
  395. */
  396. if (nPart == 0xFF)
  397. {
  398. pStringInfo->uType = (uPart & 0xff00) | (pStringInfo->uType & 0x00ff);
  399. nPart = 0; // There is only one simple part, so we are part 0
  400. }
  401. else
  402. {
  403. pStringInfo->uType = uPart & 0xff00;
  404. }
  405. pStringInfo->uType &= ~SBT_ALLTYPES;
  406. pStringInfo->uType |= SBT_NULL;
  407. /* Invalidate the rect of this pane.
  408. *
  409. * Note that we don't check whether the pane is actually visible
  410. * in the current status bar mode. The result is some gratuitous
  411. * invalidates and updates. Oh well.
  412. */
  413. GetClientRect(pStatusInfo->ci.hwnd, &rc);
  414. if (nPart)
  415. rc.left = pStringInfo[-1].right;
  416. if (pStringInfo->right > 0)
  417. rc.right = pStringInfo->right;
  418. InvalidateRect(pStatusInfo->ci.hwnd, &rc, TRUE);
  419. switch (uPart&SBT_ALLTYPES)
  420. {
  421. case 0:
  422. /* If lpStr==NULL, we have the NULL string
  423. */
  424. if (HIWORD64(lpStr))
  425. {
  426. wLen = lstrlen(lpStr);
  427. if (wLen)
  428. {
  429. pString = (PTSTR)LocalAlloc(LPTR, (wLen+1)*sizeof(TCHAR));
  430. pStringInfo->dwString = (ULONG_PTR)(LPTSTR)pString;
  431. if (pString)
  432. {
  433. pStringInfo->uType |= SBT_NORMAL;
  434. /* Copy the string
  435. */
  436. lstrcpy(pString, lpStr);
  437. /* Replace unprintable characters (like CR/LF) with spaces
  438. */
  439. for ( ; *pString;
  440. pString=(PTSTR)OFFSETOF(CharNextEx((WORD)pStatusInfo->uiCodePage, pString, 0)))
  441. if ((unsigned)(*pString)<(unsigned)TEXT(' ') && *pString!= TEXT('\t'))
  442. *pString = TEXT(' ');
  443. }
  444. else
  445. {
  446. /* We return FALSE to indicate there was an error setting
  447. * the string
  448. */
  449. return(FALSE);
  450. }
  451. }
  452. }
  453. else if (LOWORD(lpStr))
  454. {
  455. /* We don't allow this anymore; the app needs to set the ownerdraw
  456. * bit for ownerdraw.
  457. */
  458. return(FALSE);
  459. }
  460. break;
  461. case SBT_OWNERDRAW:
  462. pStringInfo->uType |= SBT_OWNERDRAW;
  463. pStringInfo->dwString = (ULONG_PTR)lpStr;
  464. break;
  465. default:
  466. return(FALSE);
  467. }
  468. UpdateWindow(pStatusInfo->ci.hwnd);
  469. return(TRUE);
  470. }
  471. BOOL SetStatusParts(PSTATUSINFO pStatusInfo, int nParts, LPINT lpInt)
  472. {
  473. int i;
  474. int prev;
  475. PSTRINGINFO pStringInfo, pStringInfoTemp;
  476. BOOL bRedraw = FALSE;
  477. if (nParts != pStatusInfo->nParts)
  478. {
  479. TOOLINFO ti = {0};
  480. int n;
  481. if (pStatusInfo->hwndToolTips)
  482. {
  483. ti.cbSize = sizeof(ti);
  484. ti.hwnd = pStatusInfo->ci.hwnd;
  485. ti.lpszText = LPSTR_TEXTCALLBACK;
  486. for(n = 0; n < pStatusInfo->nParts; n++)
  487. {
  488. ti.uId = n;
  489. SendMessage(pStatusInfo->hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
  490. }
  491. }
  492. bRedraw = TRUE;
  493. /* Note that if nParts > pStatusInfo->nParts, this loop
  494. * does nothing
  495. */
  496. for (i=pStatusInfo->nParts-nParts,
  497. pStringInfo=&pStatusInfo->sInfo[nParts]; i>0;
  498. --i, ++pStringInfo)
  499. {
  500. if ((pStringInfo->uType&SBT_ALLTYPES) == SBT_NORMAL)
  501. LocalFree((HLOCAL)OFFSETOF(pStringInfo->dwString));
  502. pStringInfo->uType = SBT_NULL;
  503. }
  504. /* Realloc to the new size and store the new pointer
  505. */
  506. pStringInfoTemp = (PSTRINGINFO)CCLocalReAlloc(pStatusInfo->sInfo,
  507. nParts * sizeof(STRINGINFO));
  508. if (!pStringInfoTemp)
  509. return(FALSE);
  510. pStatusInfo->sInfo = pStringInfoTemp;
  511. /* Note that if nParts < pStatusInfo->nParts, this loop
  512. * does nothing
  513. */
  514. for (i=nParts-pStatusInfo->nParts,
  515. pStringInfo=&pStatusInfo->sInfo[pStatusInfo->nParts]; i>0;
  516. --i, ++pStringInfo)
  517. {
  518. pStringInfo->uType = SBT_NULL;
  519. pStringInfo->right = 0;
  520. }
  521. pStatusInfo->nParts = nParts;
  522. StatusUpdateToolTips(pStatusInfo);
  523. }
  524. //
  525. // Under stress, apps such as Explorer might pass coordinates that
  526. // result in status bar panes with negative width, so make sure
  527. // each edge is at least as far to the right as the previous.
  528. //
  529. prev = 0;
  530. for (i=0, pStringInfo=pStatusInfo->sInfo; i<nParts;
  531. ++i, ++pStringInfo, ++lpInt)
  532. {
  533. int right = *lpInt;
  534. // The last component is allowed to have *lpInt = -1.
  535. // Otherwise, make sure the widths are nondecreasing.
  536. if (!(right == -1 && i == nParts - 1) && right < prev)
  537. right = prev;
  538. DebugMsg(TF_STATUS, TEXT("SBSetParts: part=%d, rlimit=%d (%d)"), i, right, *lpInt);
  539. if (pStringInfo->right != right)
  540. {
  541. bRedraw = TRUE;
  542. pStringInfo->right = right;
  543. }
  544. prev = right;
  545. }
  546. /* Only redraw if necesary (if the number of parts has changed or
  547. * a border has changed)
  548. */
  549. if (bRedraw)
  550. InvalidateRect(pStatusInfo->ci.hwnd, NULL, TRUE);
  551. RecalcTooltipRects(pStatusInfo);
  552. return TRUE;
  553. }
  554. void SBSetFont(PSTATUSINFO pStatusInfo, HFONT hFont, BOOL bInvalidate)
  555. {
  556. NewFont(pStatusInfo, hFont, TRUE);
  557. if (bInvalidate)
  558. {
  559. RedrawWindow(pStatusInfo->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
  560. }
  561. }
  562. BOOL SBSetText(PSTATUSINFO pStatusInfo, WPARAM wParam, LPCTSTR lpsz)
  563. {
  564. BOOL bRet;
  565. UINT idChild;
  566. DebugMsg(TF_STATUS, TEXT("SBSetText(%04x, [%s])"), wParam, lpsz);
  567. /* This is the "simple" status bar pane
  568. */
  569. if (LOBYTE(wParam) == 0xff)
  570. {
  571. UINT uSimple;
  572. // Note that we do not allow OWNERDRAW for a "simple" status bar
  573. if (wParam & SBT_OWNERDRAW)
  574. return FALSE;
  575. //
  576. // IE4 BUG-FOR-BUG COMPATIBILITY: In IE4, changing the SIMPLE
  577. // status bar text while you were in complex mode caused the simple
  578. // text to be painted briefly. It would get cleaned up the next time
  579. // the window got invalidated.
  580. //
  581. // Corel Gallery actually RELIES ON THIS BUG!
  582. //
  583. // Since the bad text got cleaned up on every invalidate, they
  584. // "worked around" their bug by doing SB_SETTEXT in their idle loop,
  585. // so the "correct" text gets repainted no matter what.
  586. //
  587. // So if we have an old status bar, emulate the bug by temporarily
  588. // setting the status bar into SIMPLE mode for the duration of the
  589. // SetStatusText call.
  590. uSimple = pStatusInfo->sSimple.uType;
  591. if (pStatusInfo->ci.iVersion < 5)
  592. pStatusInfo->sSimple.uType = (uSimple & 0xFF00);
  593. bRet = SetStatusText(pStatusInfo, &pStatusInfo->sSimple,
  594. (UINT) wParam, lpsz);
  595. if (pStatusInfo->ci.iVersion < 5)
  596. pStatusInfo->sSimple.uType |= LOBYTE(uSimple);
  597. idChild = 0;
  598. }
  599. else
  600. {
  601. if ((UINT)pStatusInfo->nParts <= (UINT)LOBYTE(wParam))
  602. bRet = FALSE;
  603. else
  604. bRet = SetStatusText(pStatusInfo, &pStatusInfo->sInfo[LOBYTE(wParam)],
  605. (UINT) wParam, lpsz);
  606. idChild = LOBYTE(wParam);
  607. }
  608. if (bRet)
  609. NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, pStatusInfo->ci.hwnd,
  610. OBJID_CLIENT, idChild+1);
  611. return bRet;
  612. }
  613. //
  614. // iPart - which part we are querying
  615. // lpOutBuf - output buffer, NULL if no output desired
  616. // cchOutBuf - size of output buffer in characters
  617. // flags - zero or more of the following flags
  618. //
  619. // SBGT_ANSI - Output buffer is ANSI
  620. // SBGT_UNICODE - Output buffer is unicode
  621. // SBGT_TCHAR - Output buffer is TCHAR
  622. // SBGT_OWNERDRAWOK- Return refdata for owner-draw
  623. //
  624. // If item is a string, and output buffer is provided, then returns
  625. // output string length (not including null) in low word, flags in
  626. // high word.
  627. //
  628. // If item is a string, and no output buffer is provided, then returns
  629. // source string length (not including null) in low word, flags in
  630. // high word.
  631. //
  632. // If item is owner-draw and SBGT_OWNERDRAWOK is set, then return the
  633. // refdata for the owner-draw item.
  634. //
  635. // If item is owner-draw and SBGT_OWNERDRAWOK is clear, then treats
  636. // string as if it were empty.
  637. //
  638. #define SBGT_ANSI 0
  639. #define SBGT_UNICODE 1
  640. #define SBGT_OWNERDRAWOK 2
  641. #define SBGT_TCHAR SBGT_UNICODE
  642. // Value for cchOutBuf to indicate largest possible output buffer size
  643. // We cannot use -1 because StrCpyNW thinks -1 means a negative-size buffer.
  644. // Since the maximum value we return is 0xFFFF (LOWORD), and the return value
  645. // doesn't include the trailing null, the largest incoming buffer is one
  646. // greater.
  647. #define SBGT_INFINITE 0x00010000
  648. LRESULT SBGetText(PSTATUSINFO pStatusInfo, WPARAM iPart, LPVOID lpOutBuf, int cchOutBuf, UINT flags)
  649. {
  650. UINT uType;
  651. PTSTR pString;
  652. ULONG_PTR dwString;
  653. UINT wLen;
  654. if (!pStatusInfo || (UINT)pStatusInfo->nParts<=iPart)
  655. return(0);
  656. if (pStatusInfo->sSimple.uType & SBT_NOSIMPLE)
  657. {
  658. uType = pStatusInfo->sInfo[iPart].uType;
  659. dwString = pStatusInfo->sInfo[iPart].dwString;
  660. } else {
  661. uType = pStatusInfo->sSimple.uType;
  662. dwString = pStatusInfo->sSimple.dwString;
  663. }
  664. // Catch the boundary condition early so we only have to check lpOutBuf
  665. if (cchOutBuf == 0)
  666. lpOutBuf = NULL;
  667. if ((uType&SBT_ALLTYPES) == SBT_NORMAL)
  668. {
  669. pString = (PTSTR)dwString;
  670. if (flags & SBGT_UNICODE)
  671. {
  672. if (lpOutBuf)
  673. {
  674. StrCpyNW(lpOutBuf, pString, cchOutBuf);
  675. wLen = lstrlenW(lpOutBuf);
  676. }
  677. else
  678. wLen = lstrlen(pString);
  679. }
  680. else
  681. {
  682. // We have to use ProduceAFromW because WideCharToMultiByte
  683. // will simply fail if the output buffer isn't big enough,
  684. // but we want to copy as many as will fit.
  685. LPSTR pStringA = ProduceAFromW(pStatusInfo->ci.uiCodePage, pString);
  686. if (pStringA)
  687. {
  688. if (lpOutBuf)
  689. {
  690. lstrcpynA(lpOutBuf, pStringA, cchOutBuf);
  691. wLen = lstrlenA(lpOutBuf);
  692. }
  693. else
  694. wLen = lstrlenA(pStringA); // Return required ANSI buf size
  695. FreeProducedString(pStringA);
  696. }
  697. else
  698. {
  699. if (lpOutBuf)
  700. *(LPSTR)lpOutBuf = '\0';
  701. wLen = 0; // Eek, horrible memory problem
  702. }
  703. }
  704. /* Set this back to 0 to return to the app
  705. */
  706. uType &= ~SBT_ALLTYPES;
  707. }
  708. else
  709. {
  710. if (lpOutBuf)
  711. {
  712. if (flags & SBGT_UNICODE)
  713. *(LPWSTR)lpOutBuf = L'\0';
  714. else
  715. *(LPSTR)lpOutBuf = '\0';
  716. }
  717. wLen = 0;
  718. // Only SB_GETTEXT[AW] returns the raw owner-draw refdata
  719. if ((uType&SBT_ALLTYPES)==SBT_OWNERDRAW && (flags & SBGT_OWNERDRAWOK))
  720. return(dwString);
  721. }
  722. return(MAKELONG(wLen, uType));
  723. }
  724. void SBSetBorders(PSTATUSINFO pStatusInfo, LPINT lpInt)
  725. {
  726. // pStatusInfo->nBorderX = lpInt[0] < 0 ? 0 : lpInt[0];
  727. pStatusInfo->nBorderX = 0;
  728. // pStatusInfo->nBorderY = lpInt[1] < 0 ? 2 * g_cyBorder : lpInt[1];
  729. pStatusInfo->nBorderY = g_cyEdge;
  730. // pStatusInfo->nBorderPart = lpInt[2] < 0 ? 2 * g_cxBorder : lpInt[2];
  731. pStatusInfo->nBorderPart = g_cxEdge;
  732. }
  733. void StatusUpdateToolTips(PSTATUSINFO psi)
  734. {
  735. if (psi->hwndToolTips)
  736. {
  737. TOOLINFO ti = {0};
  738. int n;
  739. ti.cbSize = sizeof(ti);
  740. ti.hwnd = psi->ci.hwnd;
  741. ti.lpszText = LPSTR_TEXTCALLBACK;
  742. for(n = 0; n < psi->nParts; n++)
  743. {
  744. ti.uId = n;
  745. SendMessage(psi->hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)&ti);
  746. }
  747. }
  748. }
  749. void StatusForceCreateTooltips(PSTATUSINFO psi)
  750. {
  751. if (psi->ci.style & SBT_TOOLTIPS && !psi->hwndToolTips)
  752. {
  753. TOOLINFO ti = {0};
  754. psi->hwndToolTips = CreateWindowEx(WS_EX_TRANSPARENT, c_szSToolTipsClass, NULL, WS_POPUP | TTS_ALWAYSTIP,
  755. CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  756. psi->ci.hwnd, NULL, HINST_THISDLL, NULL);
  757. ti.cbSize = sizeof(ti);
  758. ti.hwnd = psi->ci.hwnd;
  759. ti.lpszText = LPSTR_TEXTCALLBACK;
  760. ti.uId = SB_SIMPLEID;
  761. SendMessage(psi->hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)&ti);
  762. StatusUpdateToolTips(psi);
  763. RecalcTooltipRects(psi);
  764. }
  765. }
  766. LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  767. {
  768. PSTATUSINFO pStatusInfo = GetWindowPtr(hWnd, 0);
  769. NMCLICK nm;
  770. int nNotification;
  771. if (!pStatusInfo && uMsg != WM_CREATE)
  772. goto DoDefault;
  773. switch (uMsg)
  774. {
  775. case WM_CREATE:
  776. return InitStatusWnd(hWnd, (LPCREATESTRUCT)lParam);
  777. case WM_SYSCOLORCHANGE:
  778. if (pStatusInfo->hwndToolTips)
  779. SendMessage(pStatusInfo->hwndToolTips, uMsg, wParam, lParam);
  780. break;
  781. case WM_MOUSEMOVE:
  782. case WM_LBUTTONDOWN:
  783. StatusForceCreateTooltips(pStatusInfo);
  784. RelayToToolTips(pStatusInfo->hwndToolTips, hWnd, uMsg, wParam, lParam);
  785. break;
  786. case WM_STYLECHANGED:
  787. {
  788. if (wParam == GWL_EXSTYLE)
  789. {
  790. //
  791. // If the RTL_MIRROR extended style bit had changed, let's
  792. // repaint the control window
  793. //
  794. if ((pStatusInfo->ci.dwExStyle&RTL_MIRRORED_WINDOW) !=
  795. (((LPSTYLESTRUCT)lParam)->styleNew&RTL_MIRRORED_WINDOW))
  796. InvalidateRect(pStatusInfo->ci.hwnd, NULL, TRUE);
  797. //
  798. // Save the new ex-style bits
  799. //
  800. pStatusInfo->ci.dwExStyle = ((LPSTYLESTRUCT)lParam)->styleNew;
  801. }
  802. }
  803. return 0;
  804. case WM_SETTINGCHANGE:
  805. InitGlobalColors();
  806. InitGlobalMetrics(wParam);
  807. if (pStatusInfo->hwndToolTips)
  808. SendMessage(pStatusInfo->hwndToolTips, uMsg, wParam, lParam);
  809. if (pStatusInfo->dxGripper)
  810. pStatusInfo->dxGripper = GRIPSIZE;
  811. if (wParam == SPI_SETNONCLIENTMETRICS)
  812. {
  813. if (pStatusInfo->bDefFont)
  814. {
  815. if (pStatusInfo->hStatFont)
  816. {
  817. DeleteObject(pStatusInfo->hStatFont);
  818. pStatusInfo->hStatFont = NULL;
  819. pStatusInfo->bDefFont = FALSE;
  820. SBSetFont(pStatusInfo, 0, TRUE);
  821. }
  822. }
  823. }
  824. break;
  825. case WM_DESTROY:
  826. if (pStatusInfo)
  827. {
  828. int i;
  829. PSTRINGINFO pStringInfo;
  830. // FALSE = Don't resize while being destroyed...
  831. NewFont(pStatusInfo, (HFONT)-1, FALSE);
  832. for (i=pStatusInfo->nParts-1, pStringInfo=pStatusInfo->sInfo;
  833. i>=0; --i, ++pStringInfo)
  834. {
  835. if ((pStringInfo->uType&SBT_ALLTYPES) == SBT_NORMAL)
  836. LocalFree((HLOCAL)OFFSETOF(pStringInfo->dwString));
  837. Str_Set(&pStringInfo->pszToolTip, NULL);
  838. }
  839. if ((pStatusInfo->sSimple.uType&SBT_ALLTYPES) == SBT_NORMAL)
  840. LocalFree((HLOCAL)OFFSETOF(pStatusInfo->sSimple.dwString));
  841. if (IsWindow(pStatusInfo->hwndToolTips))
  842. DestroyWindow(pStatusInfo->hwndToolTips);
  843. Str_Set(&pStatusInfo->sSimple.pszToolTip, NULL);
  844. if (pStatusInfo->sInfo)
  845. LocalFree(pStatusInfo->sInfo);
  846. if (pStatusInfo->hTheme)
  847. CloseThemeData(pStatusInfo->hTheme);
  848. LocalFree((HLOCAL)pStatusInfo);
  849. SetWindowInt(hWnd, 0, 0);
  850. }
  851. break;
  852. case WM_NCHITTEST:
  853. if (pStatusInfo->dxGripper && !IsZoomed(pStatusInfo->ci.hwndParent))
  854. {
  855. RECT rc;
  856. // already know height is valid. if the width is in the grip,
  857. // show the sizing cursor
  858. GetWindowRect(pStatusInfo->ci.hwnd, &rc);
  859. //
  860. // If this is a RTL mirrored status window, then measure
  861. // from the near edge (screen coordinates) since Screen
  862. // Coordinates are not RTL mirrored.
  863. // [samera]
  864. //
  865. if (pStatusInfo->ci.dwExStyle&RTL_MIRRORED_WINDOW) {
  866. if (GET_X_LPARAM(lParam) < (rc.left + pStatusInfo->dxGripper))
  867. return HTBOTTOMLEFT;
  868. } else if (GET_X_LPARAM(lParam) > (rc.right - pStatusInfo->dxGripper)) {
  869. return HTBOTTOMRIGHT;
  870. }
  871. }
  872. goto DoDefault;
  873. case WM_SETTEXT:
  874. {
  875. wParam = 0;
  876. uMsg = SB_SETTEXT;
  877. }
  878. /* Fall through */
  879. case SB_SETTEXT:
  880. return SBSetText(pStatusInfo, wParam, (LPCTSTR)lParam);
  881. case SB_SETTEXTA:
  882. {
  883. BOOL bRet, bAlloced = FALSE;
  884. LPTSTR lpsz;
  885. if (!(wParam & SBT_OWNERDRAW))
  886. {
  887. lpsz = ProduceWFromA(pStatusInfo->uiCodePage, (LPSTR)lParam);
  888. bAlloced = TRUE;
  889. }
  890. else
  891. {
  892. lpsz = (LPTSTR)lParam;
  893. }
  894. if (!pStatusInfo)
  895. bRet = FALSE;
  896. else
  897. {
  898. bRet = SBSetText(pStatusInfo, wParam, (LPCTSTR)lpsz);
  899. }
  900. if (bAlloced)
  901. {
  902. FreeProducedString(lpsz);
  903. }
  904. return bRet;
  905. }
  906. // The WM_GETTEXT and WM_GETTEXTLENGTH messages must return a
  907. // character count, no flags. (Otherwise USER gets mad at us.)
  908. // So we throw away the flags by returning only the LOWORD().
  909. case WM_GETTEXT:
  910. return LOWORD(SBGetText(pStatusInfo, 0, (LPVOID)lParam, (int)wParam, SBGT_TCHAR));
  911. case WM_GETTEXTLENGTH:
  912. return LOWORD(SBGetText(pStatusInfo, 0, NULL, 0, SBGT_TCHAR));
  913. case SB_GETTEXT:
  914. /* We assume the buffer is large enough to hold the string, just
  915. * as listboxes do; the app should call SB_GETTEXTLEN first
  916. */
  917. return SBGetText(pStatusInfo, wParam, (LPVOID)lParam, SBGT_INFINITE, SBGT_TCHAR | SBGT_OWNERDRAWOK);
  918. case SB_GETTEXTLENGTH:
  919. return SBGetText(pStatusInfo, wParam, NULL, 0, SBGT_TCHAR);
  920. case SB_GETTEXTA:
  921. /* We assume the buffer is large enough to hold the string, just
  922. * as listboxes do; the app should call SB_GETTEXTLEN first
  923. */
  924. return SBGetText(pStatusInfo, wParam, (LPVOID)lParam, SBGT_INFINITE, SBGT_ANSI | SBGT_OWNERDRAWOK);
  925. case SB_GETTEXTLENGTHA:
  926. return SBGetText(pStatusInfo, wParam, NULL, 0, SBGT_ANSI);
  927. case SB_SETBKCOLOR:
  928. {
  929. COLORREF clr = pStatusInfo->_clrBk;
  930. pStatusInfo->_clrBk = (COLORREF)lParam;
  931. InvalidateRect(hWnd, NULL, TRUE);
  932. return clr;
  933. }
  934. case SB_SETPARTS:
  935. if (!wParam || wParam>MAXPARTS)
  936. return FALSE;
  937. return SetStatusParts(pStatusInfo, (int) wParam, (LPINT)lParam);
  938. case SB_GETPARTS:
  939. if (lParam)
  940. {
  941. PSTRINGINFO pStringInfo;
  942. LPINT lpInt;
  943. /* Fill in the lesser of the number of entries asked for or
  944. * the number of entries there are
  945. */
  946. if (wParam > (WPARAM)pStatusInfo->nParts)
  947. wParam = pStatusInfo->nParts;
  948. for (pStringInfo=pStatusInfo->sInfo, lpInt=(LPINT)lParam;
  949. wParam>0; --wParam, ++pStringInfo, ++lpInt)
  950. *lpInt = pStringInfo->right;
  951. }
  952. /* Always return the number of actual entries
  953. */
  954. return(pStatusInfo->nParts);
  955. case SB_GETBORDERS:
  956. // Can't validate any more than this....
  957. if ((LPINT)lParam != NULL)
  958. {
  959. ((LPINT)lParam)[0] = pStatusInfo->nBorderX;
  960. ((LPINT)lParam)[1] = pStatusInfo->nBorderY;
  961. ((LPINT)lParam)[2] = pStatusInfo->nBorderPart;
  962. return TRUE;
  963. }
  964. else
  965. return FALSE;
  966. case SB_ISSIMPLE:
  967. return !(pStatusInfo->sSimple.uType & SBT_NOSIMPLE);
  968. case SB_GETRECT:
  969. return Status_GetRect(pStatusInfo, (int)wParam, (LPRECT)lParam);
  970. case SB_SETMINHEIGHT: // this is a substitute for WM_MEASUREITEM
  971. pStatusInfo->nMinHeight = (int) wParam;
  972. RecalcTooltipRects(pStatusInfo);
  973. break;
  974. case SB_SIMPLE:
  975. {
  976. BOOL bInvalidate = FALSE;
  977. if (wParam)
  978. {
  979. if (pStatusInfo->sSimple.uType & SBT_NOSIMPLE)
  980. {
  981. pStatusInfo->sSimple.uType &= ~SBT_NOSIMPLE;
  982. bInvalidate = TRUE;
  983. }
  984. }
  985. else
  986. {
  987. if ((pStatusInfo->sSimple.uType & SBT_NOSIMPLE) == 0)
  988. {
  989. pStatusInfo->sSimple.uType |= SBT_NOSIMPLE;
  990. bInvalidate = TRUE;
  991. }
  992. }
  993. if (bInvalidate) {
  994. DebugMsg(TF_STATUS, TEXT("SB_SIMPLE: %d"), wParam);
  995. RecalcTooltipRects(pStatusInfo);
  996. SendNotifyEx(pStatusInfo->ci.hwndParent, pStatusInfo->ci.hwnd, SBN_SIMPLEMODECHANGE, NULL, FALSE);
  997. InvalidateRect(pStatusInfo->ci.hwnd, NULL, TRUE);
  998. }
  999. break;
  1000. }
  1001. case SB_SETICON:
  1002. case SB_GETICON:
  1003. {
  1004. PSTRINGINFO pStringInfo = NULL;
  1005. // -1 implies we are setting the icon for sSimple
  1006. if ((UINT_PTR)-1 == wParam)
  1007. pStringInfo = &pStatusInfo->sSimple;
  1008. else if(wParam < (UINT)pStatusInfo->nParts)
  1009. pStringInfo = &pStatusInfo->sInfo[wParam];
  1010. if (uMsg == SB_GETICON)
  1011. return (LRESULT)(pStringInfo ? pStringInfo->hIcon : NULL);
  1012. if (pStringInfo && (pStringInfo->hIcon != (HICON)lParam))
  1013. {
  1014. BITMAP bm = {0};
  1015. RECT rc;
  1016. if (lParam)
  1017. {
  1018. ICONINFO ii;
  1019. // Save the dimensions of the icon
  1020. GetIconInfo((HICON)lParam, &ii);
  1021. GetObject(ii.hbmColor, sizeof(BITMAP), &bm);
  1022. DeleteObject(ii.hbmColor);
  1023. DeleteObject(ii.hbmMask);
  1024. }
  1025. pStringInfo->sizeIcon.cx = bm.bmWidth;
  1026. pStringInfo->sizeIcon.cy = bm.bmHeight;
  1027. pStringInfo->hIcon = (HICON)lParam;
  1028. Status_GetRect(pStatusInfo, (int)wParam, &rc);
  1029. InvalidateRect(pStatusInfo->ci.hwnd, &rc, TRUE);
  1030. UpdateWindow(pStatusInfo->ci.hwnd);
  1031. }
  1032. return TRUE;
  1033. }
  1034. // HIWORD(wParam) is the cbChar
  1035. // LOWORD(wParam) is the nPart
  1036. case SB_GETTIPTEXT:
  1037. {
  1038. PSTRINGINFO pStringInfo = GetStringInfo(pStatusInfo, LOWORD(wParam));
  1039. if (pStringInfo && pStringInfo->pszToolTip)
  1040. lstrcpyn((LPTSTR)lParam, pStringInfo->pszToolTip, HIWORD(wParam));
  1041. break;
  1042. }
  1043. case SB_SETTIPTEXT:
  1044. {
  1045. PSTRINGINFO pStringInfo = GetStringInfo(pStatusInfo, (int) wParam);
  1046. if (pStringInfo)
  1047. Str_Set(&pStringInfo->pszToolTip, (LPCTSTR)lParam);
  1048. break;
  1049. }
  1050. case SB_GETTIPTEXTA:
  1051. {
  1052. PSTRINGINFO pStringInfo = GetStringInfo(pStatusInfo, LOWORD(wParam));
  1053. if (pStringInfo)
  1054. WideCharToMultiByte(CP_ACP, 0, pStringInfo->pszToolTip, -1, (LPSTR)lParam,
  1055. HIWORD(wParam), NULL, NULL);
  1056. break;
  1057. }
  1058. case SB_SETTIPTEXTA:
  1059. {
  1060. PSTRINGINFO pStringInfo = GetStringInfo(pStatusInfo, (int) wParam);
  1061. LPTSTR lpsz;
  1062. lpsz = ProduceWFromA(pStatusInfo->uiCodePage, (LPSTR)lParam);
  1063. if (pStringInfo)
  1064. Str_Set(&pStringInfo->pszToolTip, (LPCTSTR)lpsz);
  1065. LocalFree(lpsz);
  1066. break;
  1067. }
  1068. #define lpNmhdr ((LPNMHDR)(lParam))
  1069. #define lpnmTT ((LPTOOLTIPTEXT) lParam)
  1070. #define IsTextPtr(lpszText) (((lpszText) != LPSTR_TEXTCALLBACK) && (HIWORD64(lpszText)))
  1071. case WM_NOTIFY:
  1072. {
  1073. PSTRINGINFO pStringInfo = NULL;
  1074. if (lpNmhdr->code == TTN_NEEDTEXT)
  1075. {
  1076. pStringInfo = GetStringInfo(pStatusInfo, (int) lpNmhdr->idFrom);
  1077. if (!pStringInfo || !pStringInfo->fNeedToTip)
  1078. break;
  1079. }
  1080. //
  1081. // We are just going to pass this on to the
  1082. // real parent. Note that -1 is used as
  1083. // the hwndFrom. This prevents SendNotifyEx
  1084. // from updating the NMHDR structure.
  1085. //
  1086. SendNotifyEx(pStatusInfo->ci.hwndParent, (HWND) -1,
  1087. lpNmhdr->code, lpNmhdr, pStatusInfo->ci.bUnicode);
  1088. if ((lpNmhdr->code == TTN_NEEDTEXT) && lpnmTT->lpszText
  1089. && IsTextPtr(lpnmTT->lpszText) && !lpnmTT->lpszText[0])
  1090. {
  1091. if (pStringInfo)
  1092. lpnmTT->lpszText = pStringInfo->pszToolTip;
  1093. }
  1094. break;
  1095. }
  1096. case WM_NOTIFYFORMAT:
  1097. return CIHandleNotifyFormat(&pStatusInfo->ci, lParam);
  1098. case WM_SETFONT:
  1099. if (!pStatusInfo)
  1100. return FALSE;
  1101. SBSetFont(pStatusInfo, (HFONT)wParam, (BOOL)lParam);
  1102. return TRUE;
  1103. case WM_LBUTTONUP:
  1104. nNotification = NM_CLICK;
  1105. StatusForceCreateTooltips(pStatusInfo);
  1106. RelayToToolTips(pStatusInfo->hwndToolTips, hWnd, uMsg, wParam, lParam);
  1107. goto SendNotify;
  1108. case WM_LBUTTONDBLCLK:
  1109. nNotification = NM_DBLCLK;
  1110. goto SendNotify;
  1111. case WM_RBUTTONDBLCLK:
  1112. nNotification = NM_RDBLCLK;
  1113. goto SendNotify;
  1114. case WM_RBUTTONUP:
  1115. nNotification = NM_RCLICK;
  1116. SendNotify:
  1117. LPARAM_TO_POINT(lParam, nm.pt);
  1118. nm.dwItemSpec = IndexFromPt(pStatusInfo, nm.pt);
  1119. if (!SendNotifyEx(pStatusInfo->ci.hwndParent, pStatusInfo->ci.hwnd, nNotification, (LPNMHDR)&nm,FALSE))
  1120. goto DoDefault;
  1121. return 0;
  1122. case WM_GETFONT:
  1123. if (!pStatusInfo)
  1124. return 0;
  1125. return (LRESULT)pStatusInfo->hStatFont;
  1126. case WM_SIZE:
  1127. {
  1128. int nHeight;
  1129. RECT rc;
  1130. LPTSTR lpStr;
  1131. PSTRINGINFO pStringInfo;
  1132. int i, nTabs;
  1133. if (!pStatusInfo)
  1134. return 0;
  1135. GetWindowRect(pStatusInfo->ci.hwnd, &rc);
  1136. rc.right -= rc.left; // -> dx
  1137. rc.bottom -= rc.top; // -> dy
  1138. // If there is no parent, then this is a top level window
  1139. if (pStatusInfo->ci.hwndParent)
  1140. {
  1141. ScreenToClient(pStatusInfo->ci.hwndParent, (LPPOINT)&rc);
  1142. //
  1143. // Places the status bar properly
  1144. //
  1145. if (pStatusInfo->ci.dwExStyle&RTL_MIRRORED_WINDOW)
  1146. rc.left -= rc.right;
  1147. }
  1148. // need room for text, 3d border, and extra edge
  1149. nHeight =
  1150. max(pStatusInfo->nFontHeight, g_cySmIcon) + 2 * g_cyBorder ;
  1151. if (nHeight < pStatusInfo->nMinHeight)
  1152. nHeight = pStatusInfo->nMinHeight;
  1153. nHeight += pStatusInfo->nBorderY;
  1154. // we don't have a divider thing -> force CCS_NODIVIDER
  1155. NewSize(pStatusInfo->ci.hwnd, nHeight, GetWindowStyle(pStatusInfo->ci.hwnd) | CCS_NODIVIDER,
  1156. rc.left, rc.top, rc.right, rc.bottom);
  1157. // If the pane is right aligned then we need to invalidate all the pane
  1158. // to force paint the entire pane. because the system will invalidate none if
  1159. // the status bar get shrieked or only the new added part if the status bar
  1160. // get grow and this does not work with the right justified text.
  1161. pStringInfo = pStatusInfo->sInfo;
  1162. for (i = 0; i < pStatusInfo->nParts; ++i, ++pStringInfo)
  1163. {
  1164. if ((pStringInfo->uType&SBT_ALLTYPES) == SBT_NORMAL &&
  1165. (lpStr = (LPTSTR)(pStringInfo->dwString)) != NULL)
  1166. {
  1167. for ( nTabs = 0; (lpStr = StrChr(lpStr, TEXT('\t'))) != NULL; lpStr++)
  1168. {
  1169. nTabs++;
  1170. }
  1171. if ( nTabs >= 2)
  1172. {
  1173. Status_GetRect(pStatusInfo, i, &rc);
  1174. InvalidateRect(pStatusInfo->ci.hwnd, &rc, FALSE);
  1175. }
  1176. }
  1177. }
  1178. // need to invalidate the right end of the status bar
  1179. // to maintain the finished edge look.
  1180. GetClientRect(pStatusInfo->ci.hwnd, &rc);
  1181. if (rc.right > pStatusInfo->nLastX)
  1182. rc.left = pStatusInfo->nLastX;
  1183. else
  1184. rc.left = rc.right;
  1185. rc.left -= (g_cxBorder + pStatusInfo->nBorderX);
  1186. if (pStatusInfo->dxGripper)
  1187. rc.left -= pStatusInfo->dxGripper;
  1188. else
  1189. rc.left -= pStatusInfo->nBorderPart;
  1190. if (pStatusInfo->hTheme)
  1191. {
  1192. MARGINS m = {0};
  1193. GetThemeMargins(pStatusInfo->hTheme, NULL, SP_PANE, 0, TMT_SIZINGMARGINS, &rc, &m);
  1194. rc.left -= m.cxRightWidth;
  1195. }
  1196. InvalidateRect(pStatusInfo->ci.hwnd, &rc, TRUE);
  1197. RecalcTooltipRects(pStatusInfo);
  1198. pStatusInfo->nLastX = rc.right;
  1199. break;
  1200. }
  1201. case WM_PRINTCLIENT:
  1202. case WM_PAINT:
  1203. if (!pStatusInfo)
  1204. break;
  1205. if (pStatusInfo->sSimple.uType & SBT_NOSIMPLE)
  1206. PaintStatusWnd(pStatusInfo, (HDC)wParam, pStatusInfo->sInfo, pStatusInfo->nParts, pStatusInfo->nBorderX);
  1207. else
  1208. PaintStatusWnd(pStatusInfo, (HDC)wParam, &pStatusInfo->sSimple, 1, 0);
  1209. return 0;
  1210. case WM_ERASEBKGND:
  1211. if (pStatusInfo)
  1212. {
  1213. RECT rc;
  1214. GetClientRect(hWnd, &rc);
  1215. if (pStatusInfo->hTheme)
  1216. {
  1217. DrawThemeBackground(pStatusInfo->hTheme, (HDC)wParam, 0, 0, &rc, 0);
  1218. return 1;
  1219. }
  1220. else if (pStatusInfo->_clrBk != CLR_DEFAULT)
  1221. {
  1222. FillRectClr((HDC)wParam, &rc, pStatusInfo->_clrBk);
  1223. return 1;
  1224. }
  1225. }
  1226. goto DoDefault;
  1227. case WM_GETOBJECT:
  1228. if( lParam == OBJID_QUERYCLASSNAMEIDX )
  1229. return MSAA_CLASSNAMEIDX_STATUS;
  1230. goto DoDefault;
  1231. case WM_THEMECHANGED:
  1232. if (pStatusInfo->hTheme)
  1233. CloseThemeData(pStatusInfo->hTheme);
  1234. pStatusInfo->hTheme = OpenThemeData(pStatusInfo->ci.hwnd, L"Status");
  1235. InvalidateRect(pStatusInfo->ci.hwnd, NULL, TRUE);
  1236. return 0;
  1237. default:
  1238. {
  1239. LRESULT lres;
  1240. if (CCWndProc(&pStatusInfo->ci, uMsg, wParam, lParam, &lres))
  1241. return lres;
  1242. }
  1243. break;
  1244. }
  1245. DoDefault:
  1246. return DefWindowProc(hWnd, uMsg, wParam, lParam);
  1247. }
  1248. BOOL InitStatusClass(HINSTANCE hInstance)
  1249. {
  1250. WNDCLASS rClass;
  1251. rClass.lpfnWndProc = StatusWndProc;
  1252. rClass.style = CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW;
  1253. rClass.cbClsExtra = 0;
  1254. rClass.cbWndExtra = sizeof(PSTATUSINFO);
  1255. rClass.hInstance = hInstance;
  1256. rClass.hIcon = NULL;
  1257. rClass.hCursor = LoadCursor(NULL, IDC_ARROW);
  1258. rClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
  1259. rClass.lpszMenuName = NULL;
  1260. rClass.lpszClassName = c_szStatusClass;
  1261. if (!RegisterClass(&rClass) && !GetClassInfo(hInstance, c_szStatusClass, &rClass))
  1262. return FALSE;
  1263. return TRUE;
  1264. }
  1265. HWND WINAPI CreateStatusWindow(LONG style, LPCTSTR pszText, HWND hwndParent, UINT uID)
  1266. {
  1267. // remove border styles to fix capone and other apps
  1268. return CreateWindowEx(0, c_szStatusClass, pszText, style & ~(WS_BORDER | CCS_NODIVIDER),
  1269. -100, -100, 10, 10, hwndParent, IntToPtr_(HMENU, uID), HINST_THISDLL, NULL);
  1270. }
  1271. HWND WINAPI CreateStatusWindowA(LONG style, LPCSTR pszText, HWND hwndParent,
  1272. UINT uID)
  1273. {
  1274. // remove border styles to fix capone and other apps
  1275. return CreateWindowExA(0, STATUSCLASSNAMEA, pszText, style & ~(WS_BORDER | CCS_NODIVIDER),
  1276. -100, -100, 10, 10, hwndParent, IntToPtr_(HMENU, uID), HINST_THISDLL, NULL);
  1277. }
  1278. void WINAPI DrawStatusTextEx(PSTATUSINFO pStatusInfo, HDC hDC, LPRECT lprc, LPCTSTR pszText, STRINGINFO * psi, UINT uFlags, BOOL fClipText)
  1279. {
  1280. int len, nWidth = 0, nHeight = 0;
  1281. HBRUSH hFaceBrush=NULL;
  1282. COLORREF crTextColor, crBkColor;
  1283. UINT uOpts = 0;
  1284. BOOL bNull;
  1285. int nOldMode;
  1286. int i = 0, left = 0;
  1287. LPTSTR lpTab, lpNext;
  1288. TCHAR szBuf[MAX_STATUS_TEXT_LEN];
  1289. int oldAlign;
  1290. BOOL fDrawnIcon = FALSE;
  1291. RECT rc = * lprc;
  1292. RECT rcItem = *lprc;
  1293. //
  1294. // IMPORTANT NOTE:
  1295. // pStatusInfo can be NULL, please check before reference.
  1296. //
  1297. if (uFlags & SBT_RTLREADING)
  1298. {
  1299. oldAlign = GetTextAlign(hDC);
  1300. SetTextAlign(hDC, oldAlign | TA_RTLREADING);
  1301. }
  1302. if (pszText)
  1303. lstrcpyn(szBuf, pszText, ARRAYSIZE(szBuf));
  1304. else
  1305. szBuf[0] = TEXT('\0');
  1306. if (pStatusInfo && pStatusInfo->hTheme)
  1307. {
  1308. if (!(uFlags & SBT_NOBORDERS))
  1309. DrawThemeBackground(pStatusInfo->hTheme, hDC, fClipText?SP_GRIPPERPANE:SP_PANE, 0, &rc, 0);
  1310. InflateRect(&rc, -g_cxBorder, -g_cyBorder);
  1311. crTextColor = SetTextColor(hDC, g_clrBtnText);
  1312. crBkColor = SetBkColor(hDC, g_clrBtnFace);
  1313. nOldMode = SetBkMode(hDC, TRANSPARENT);
  1314. }
  1315. else
  1316. {
  1317. //
  1318. // Create the three brushes we need. If the button face is a solid
  1319. // color, then we will just draw in opaque, instead of using a
  1320. // brush to avoid the flash
  1321. //
  1322. if (GetNearestColor(hDC, g_clrBtnFace) == g_clrBtnFace ||
  1323. !(hFaceBrush = CreateSolidBrush(g_clrBtnFace)))
  1324. {
  1325. uOpts = ETO_CLIPPED | ETO_OPAQUE;
  1326. nOldMode = SetBkMode(hDC, OPAQUE);
  1327. }
  1328. else
  1329. {
  1330. uOpts = ETO_CLIPPED;
  1331. nOldMode = SetBkMode(hDC, TRANSPARENT);
  1332. }
  1333. crTextColor = SetTextColor(hDC, g_clrBtnText);
  1334. if (pStatusInfo && (pStatusInfo->_clrBk != CLR_DEFAULT))
  1335. crBkColor = SetBkColor(hDC, pStatusInfo->_clrBk);
  1336. else
  1337. crBkColor = SetBkColor(hDC, g_clrBtnFace);
  1338. // Draw the hilites
  1339. if (!(uFlags & SBT_NOBORDERS))
  1340. // BF_ADJUST does the InflateRect stuff
  1341. DrawEdge(hDC, &rc, (uFlags & SBT_POPOUT) ? BDR_RAISEDINNER : BDR_SUNKENOUTER, BF_RECT | BF_ADJUST);
  1342. else
  1343. InflateRect(&rc, -g_cxBorder, -g_cyBorder);
  1344. if (hFaceBrush)
  1345. {
  1346. HBRUSH hOldBrush = SelectObject(hDC, hFaceBrush);
  1347. if (hOldBrush)
  1348. {
  1349. PatBlt(hDC, rc.left, rc.top,
  1350. rc.right-rc.left, rc.bottom-rc.top, PATCOPY);
  1351. SelectObject(hDC, hOldBrush);
  1352. }
  1353. }
  1354. }
  1355. for (i=0, lpNext=szBuf, bNull=FALSE; i<3; ++i)
  1356. {
  1357. HRESULT hr = E_FAIL;
  1358. int cxIcon = 0;
  1359. int leftIcon;
  1360. UINT uiCodePage = pStatusInfo? pStatusInfo->uiCodePage: CP_ACP;
  1361. /* Optimize for NULL left or center strings
  1362. */
  1363. if (!(uFlags & SBT_NOTABPARSING))
  1364. {
  1365. if (*lpNext==TEXT('\t') && i<=1)
  1366. {
  1367. ++lpNext;
  1368. continue;
  1369. }
  1370. }
  1371. /* Determine the end of the current string
  1372. */
  1373. for (lpTab=lpNext; ; lpTab=CharNextEx((WORD)uiCodePage, lpTab, 0))
  1374. {
  1375. if (!*lpTab) {
  1376. bNull = TRUE;
  1377. break;
  1378. } else if (!(uFlags & SBT_NOTABPARSING))
  1379. {
  1380. if (*lpTab == TEXT('\t'))
  1381. break;
  1382. }
  1383. }
  1384. *lpTab = TEXT('\0');
  1385. len = lstrlen(lpNext);
  1386. if (pStatusInfo && pStatusInfo->hTheme)
  1387. {
  1388. RECT rc = {0};
  1389. hr = GetThemeTextExtent(pStatusInfo->hTheme, hDC, 0, 0, lpNext, -1, DT_CALCRECT | DT_SINGLELINE, &rc, &rc);
  1390. nWidth = RECTWIDTH(rc);
  1391. nHeight = RECTHEIGHT(rc);
  1392. }
  1393. if (FAILED(hr))
  1394. MGetTextExtent(hDC, lpNext, len, &nWidth, &nHeight);
  1395. if (psi)
  1396. {
  1397. if (psi->hIcon && !fDrawnIcon)
  1398. {
  1399. cxIcon = psi->sizeIcon.cx + g_cxEdge * 2;
  1400. fDrawnIcon = TRUE;
  1401. }
  1402. }
  1403. /* i=0 means left, 1 means center, and 2 means right justified text
  1404. */
  1405. switch (i) {
  1406. case 0:
  1407. leftIcon = rcItem.left + g_cxEdge;
  1408. break;
  1409. case 1:
  1410. leftIcon = (rcItem.left + rcItem.right - (nWidth + cxIcon)) / 2;
  1411. break;
  1412. default:
  1413. leftIcon = rcItem.right - g_cxEdge - (nWidth + cxIcon);
  1414. break;
  1415. }
  1416. left = leftIcon + cxIcon;
  1417. if (psi)
  1418. {
  1419. if (cxIcon)
  1420. {
  1421. int nTop = rc.top + ((rc.bottom - rc.top) - (psi->sizeIcon.cy )) / 2 ;
  1422. if (leftIcon > rcItem.left)
  1423. {
  1424. if (psi->hIcon)
  1425. {
  1426. DrawIconEx(hDC, leftIcon, nTop, psi->hIcon,
  1427. psi->sizeIcon.cx, psi->sizeIcon.cy,
  1428. 0, NULL, DI_NORMAL);
  1429. }
  1430. }
  1431. rc.left = leftIcon + cxIcon;
  1432. }
  1433. if (!*lpNext && cxIcon)
  1434. psi->fNeedToTip = TRUE;
  1435. else
  1436. psi->fNeedToTip = (BOOL)(nWidth >= (rc.right - rc.left));
  1437. }
  1438. if (pStatusInfo && pStatusInfo->hTheme)
  1439. {
  1440. RECT rcText = rc;
  1441. rcText.left = left;
  1442. rcText.top = (rc.bottom - nHeight + rc.top) / 2;
  1443. if (fClipText)
  1444. {
  1445. rcText.right -= pStatusInfo->dxGripper;
  1446. }
  1447. hr = DrawThemeText(pStatusInfo->hTheme, hDC, 0, 0, lpNext, -1, DT_SINGLELINE | DT_NOPREFIX, 0, &rcText);
  1448. }
  1449. if (FAILED(hr))
  1450. ExtTextOut(hDC, left, (rc.bottom - nHeight + rc.top) / 2, uOpts, &rc, lpNext, len, NULL);
  1451. /* Now that we have drawn text once, take off the OPAQUE flag
  1452. */
  1453. uOpts = ETO_CLIPPED;
  1454. if (bNull)
  1455. break;
  1456. *lpTab = TEXT('\t');
  1457. lpNext = lpTab + 1;
  1458. }
  1459. if (uFlags & SBT_RTLREADING)
  1460. SetTextAlign(hDC, oldAlign);
  1461. SetTextColor(hDC, crTextColor);
  1462. SetBkColor(hDC, crBkColor);
  1463. SetBkMode(hDC, nOldMode);
  1464. if (hFaceBrush)
  1465. DeleteObject(hFaceBrush);
  1466. }
  1467. void RecalcTooltipRects(PSTATUSINFO pStatusInfo)
  1468. {
  1469. if(pStatusInfo->hwndToolTips)
  1470. {
  1471. UINT i;
  1472. TOOLINFO ti;
  1473. STRINGINFO * psi;
  1474. ti.cbSize = sizeof(ti);
  1475. ti.hwnd = pStatusInfo->ci.hwnd;
  1476. ti.lpszText = LPSTR_TEXTCALLBACK;
  1477. if (pStatusInfo->sSimple.uType & SBT_NOSIMPLE)
  1478. {
  1479. for ( i = 0, psi = pStatusInfo->sInfo; i < (UINT)pStatusInfo->nParts; i++, psi++)
  1480. {
  1481. ti.uId = i;
  1482. Status_GetRect(pStatusInfo, i, &ti.rect);
  1483. SendMessage(pStatusInfo->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
  1484. }
  1485. SetRect(&ti.rect, 0,0,0,0);
  1486. ti.uId = SB_SIMPLEID;
  1487. SendMessage(pStatusInfo->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
  1488. }
  1489. else
  1490. {
  1491. GetClientRect(pStatusInfo->ci.hwnd, &ti.rect);
  1492. InflateRect(&ti.rect, -g_cxBorder, -g_cyBorder);
  1493. ti.uId = SB_SIMPLEID;
  1494. SendMessage(pStatusInfo->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
  1495. SetRect(&ti.rect, 0,0,0,0);
  1496. for ( i = 0, psi = pStatusInfo->sInfo; i < (UINT)pStatusInfo->nParts; i++, psi++)
  1497. {
  1498. ti.uId = i;
  1499. SendMessage(pStatusInfo->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
  1500. }
  1501. }
  1502. }
  1503. return;
  1504. }
  1505. PSTRINGINFO GetStringInfo(PSTATUSINFO pStatusInfo, int nIndex)
  1506. {
  1507. PSTRINGINFO pRet = NULL;
  1508. if (nIndex == SB_SIMPLEID)
  1509. pRet = &pStatusInfo->sSimple;
  1510. else if (nIndex < pStatusInfo->nParts)
  1511. pRet = &pStatusInfo->sInfo[nIndex];
  1512. return pRet;
  1513. }
  1514. int IndexFromPt(PSTATUSINFO pStatusInfo, POINT pt)
  1515. {
  1516. RECT rc;
  1517. int nPart = 0;
  1518. //
  1519. // More IE4 bug-for-bug compatibility. IE4 tested for simple mode
  1520. // incorrectly.
  1521. //
  1522. if (pStatusInfo->ci.iVersion < 5)
  1523. {
  1524. // This is not a typo! Well, actually, it *is* a typo, but it's
  1525. // a typo we have to preserve for compatibility. I don't know if
  1526. // anybody relied on the typo, but I'm playing it safe.
  1527. //
  1528. // The bug was that in IE4, a click on a simple status bar usually
  1529. // came back as SB_HITTEST_NOITEM instead of SB_SIMPLEID.
  1530. //
  1531. // I re-parenthesized the test so typo.pl won't trigger. The original
  1532. // IE4 code lacked the parentheses.
  1533. if ((!pStatusInfo->sSimple.uType) & SBT_NOSIMPLE)
  1534. return SB_SIMPLEID;
  1535. }
  1536. else
  1537. {
  1538. if (!(pStatusInfo->sSimple.uType & SBT_NOSIMPLE))
  1539. return SB_SIMPLEID;
  1540. }
  1541. for(nPart = 0; nPart < pStatusInfo->nParts; nPart++)
  1542. {
  1543. Status_GetRect(pStatusInfo, nPart, &rc);
  1544. if (PtInRect(&rc, pt))
  1545. return nPart;
  1546. }
  1547. return SB_HITTEST_NOITEM;
  1548. }