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.

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