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.

1907 lines
58 KiB

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