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.

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