1862 lines
58 KiB
C
1862 lines
58 KiB
C
/*
|
|
** STATUS.C
|
|
**
|
|
** Status bar code
|
|
**
|
|
*/
|
|
|
|
#include "ctlspriv.h"
|
|
|
|
#define MAX_TOOLTIP_STRING 80
|
|
#define SB_HITTEST_NOITEM -2
|
|
|
|
typedef struct {
|
|
ULONG_PTR dwString;
|
|
UINT uType;
|
|
int right;
|
|
HICON hIcon;
|
|
SIZE sizeIcon;
|
|
LPTSTR pszToolTip;
|
|
BOOL fNeedToTip;
|
|
} STRINGINFO, *PSTRINGINFO;
|
|
|
|
typedef struct {
|
|
CCONTROLINFO ci;
|
|
HWND hwndToolTips;
|
|
HFONT hStatFont;
|
|
BOOL bDefFont;
|
|
|
|
int nFontHeight;
|
|
int nMinHeight;
|
|
int nBorderX, nBorderY, nBorderPart;
|
|
int nLastX; // for invalidating unclipped right side
|
|
int dxGripper; // 0 if no gripper
|
|
int dyGripper;
|
|
UINT uiCodePage; // code page
|
|
|
|
STRINGINFO sSimple;
|
|
|
|
int nParts;
|
|
COLORREF _clrBk;
|
|
|
|
HTHEME hTheme;
|
|
|
|
PSTRINGINFO sInfo;
|
|
} STATUSINFO, *PSTATUSINFO;
|
|
|
|
|
|
#define SBT_NORMAL 0xf000
|
|
#define SBT_NULL 0x0000 /* Some code depends on this being 0 */
|
|
#define SBT_ALLTYPES 0xf000 /* this does NOT include rtlred */
|
|
#define SBT_NOSIMPLE 0x00ff /* Flags to indicate normal status bar */
|
|
|
|
|
|
#define MAXPARTS 256
|
|
// Bug#94368 raymondc v6: This limit isn't big enough on large res screens
|
|
#define MAX_STATUS_TEXT_LEN 128
|
|
|
|
#define CharNextEx(cp, sz, f) ((sz)+1)
|
|
|
|
BOOL SBSetText(PSTATUSINFO pStatusInfo, WPARAM wParam, LPCTSTR lpsz);
|
|
void SBSetBorders(PSTATUSINFO pStatusInfo, LPINT lpInt);
|
|
void SBSetFont(PSTATUSINFO pStatusInfo, HFONT hFont, BOOL bInvalidate);
|
|
void WINAPI DrawStatusTextEx(PSTATUSINFO pStatusInfo, HDC hDC, LPRECT lprc, LPCTSTR pszText, STRINGINFO * psi, UINT uFlags, BOOL fClipText);
|
|
void RecalcTooltipRects(PSTATUSINFO pStatusinfo);
|
|
PSTRINGINFO GetStringInfo(PSTATUSINFO pStatusInfo, int nIndex);
|
|
int IndexFromPt(PSTATUSINFO pStatusInfo, POINT pt);
|
|
void StatusUpdateToolTips(PSTATUSINFO psi);
|
|
|
|
void GetNewMetrics(PSTATUSINFO pStatusInfo, HDC hDC, HFONT hNewFont)
|
|
{
|
|
HFONT hOldFont;
|
|
/* HACK! Pass in -1 to just delete the old font
|
|
*/
|
|
if (hNewFont != (HFONT)-1)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
TEXTMETRIC tm;
|
|
|
|
if (pStatusInfo->hTheme)
|
|
{
|
|
hr = GetThemeTextMetrics(pStatusInfo->hTheme, hDC, 0, 0, &tm);
|
|
|
|
}
|
|
if (FAILED(hr))
|
|
{
|
|
hOldFont = 0;
|
|
if (hNewFont)
|
|
hOldFont = SelectObject(hDC, hNewFont);
|
|
|
|
GetTextMetrics(hDC, &tm);
|
|
|
|
if (hOldFont)
|
|
SelectObject(hDC, hOldFont);
|
|
}
|
|
|
|
pStatusInfo->nFontHeight = tm.tmHeight + tm.tmInternalLeading;
|
|
|
|
// For far east font which has no internal leading
|
|
if ( !tm.tmInternalLeading )
|
|
pStatusInfo->nFontHeight += g_cyBorder * 2;
|
|
|
|
}
|
|
}
|
|
|
|
void NewFont(PSTATUSINFO pStatusInfo, HFONT hNewFont, BOOL fResize)
|
|
{
|
|
HFONT hOldFont;
|
|
BOOL bDelFont;
|
|
HDC hDC;
|
|
|
|
hOldFont = pStatusInfo->hStatFont;
|
|
bDelFont = pStatusInfo->bDefFont;
|
|
|
|
hDC = GetDC(pStatusInfo->ci.hwnd);
|
|
|
|
if (hNewFont) {
|
|
pStatusInfo->hStatFont = hNewFont;
|
|
pStatusInfo->bDefFont = FALSE;
|
|
pStatusInfo->uiCodePage = GetCodePageForFont(hNewFont);
|
|
} else {
|
|
if (bDelFont) {
|
|
/* I will reuse the default font, so don't delete it later
|
|
*/
|
|
hNewFont = pStatusInfo->hStatFont;
|
|
bDelFont = FALSE;
|
|
} else {
|
|
#ifndef DBCS_FONT
|
|
hNewFont = CCCreateStatusFont();
|
|
if (!hNewFont)
|
|
#endif // DBCS_FONT
|
|
hNewFont = g_hfontSystem;
|
|
|
|
|
|
pStatusInfo->hStatFont = hNewFont;
|
|
pStatusInfo->bDefFont = BOOLFROMPTR(hNewFont);
|
|
}
|
|
}
|
|
|
|
#ifndef DBCS_FONT
|
|
/* We delete the old font after creating the new one in case they are
|
|
* the same; this should help GDI a little
|
|
*/
|
|
if (bDelFont)
|
|
DeleteObject(hOldFont);
|
|
#endif
|
|
|
|
GetNewMetrics(pStatusInfo, hDC, hNewFont);
|
|
|
|
ReleaseDC(pStatusInfo->ci.hwnd, hDC);
|
|
|
|
// My font changed, so maybe I should resize to match
|
|
if (fResize)
|
|
SendMessage(pStatusInfo->ci.hwnd, WM_SIZE, 0, 0L);
|
|
}
|
|
|
|
/* We should send messages instead of calling things directly so we can
|
|
* be subclassed more easily.
|
|
*/
|
|
LRESULT InitStatusWnd(HWND hWnd, LPCREATESTRUCT lpCreate)
|
|
{
|
|
int nBorders[3];
|
|
PSTATUSINFO pStatusInfo = (PSTATUSINFO)LocalAlloc(LPTR, sizeof(STATUSINFO));
|
|
if (!pStatusInfo)
|
|
return -1; // fail the window create
|
|
|
|
// Start out with one part
|
|
pStatusInfo->sInfo = (PSTRINGINFO)LocalAlloc(LPTR, sizeof(STRINGINFO));
|
|
if (!pStatusInfo->sInfo)
|
|
{
|
|
LocalFree(pStatusInfo);
|
|
return -1; // fail the window create
|
|
}
|
|
|
|
SetWindowPtr(hWnd, 0, pStatusInfo);
|
|
CIInitialize(&pStatusInfo->ci, hWnd, lpCreate);
|
|
|
|
|
|
pStatusInfo->sSimple.uType = SBT_NOSIMPLE | SBT_NULL;
|
|
pStatusInfo->sSimple.right = -1;
|
|
pStatusInfo->uiCodePage = CP_ACP;
|
|
|
|
pStatusInfo->nParts = 1;
|
|
pStatusInfo->sInfo[0].uType = SBT_NULL;
|
|
pStatusInfo->sInfo[0].right = -1;
|
|
|
|
pStatusInfo->_clrBk = CLR_DEFAULT;
|
|
|
|
|
|
pStatusInfo->hTheme = OpenThemeData(pStatusInfo->ci.hwnd, L"Status");
|
|
|
|
// Save the window text in our struct, and let USER store the NULL string
|
|
SBSetText(pStatusInfo, 0, lpCreate->lpszName);
|
|
lpCreate->lpszName = c_szNULL;
|
|
|
|
// Don't resize because MFC doesn't like getting funky
|
|
// messages before the window is fully created. USER will send
|
|
// us a WM_SIZE message after the WM_CREATE returns, so we'll
|
|
// get it sooner or later.
|
|
NewFont(pStatusInfo, 0, FALSE);
|
|
|
|
nBorders[0] = -1; // use default border widths
|
|
nBorders[1] = -1;
|
|
nBorders[2] = -1;
|
|
|
|
SBSetBorders(pStatusInfo, nBorders);
|
|
|
|
#define GRIPSIZE (g_cxVScroll + g_cxBorder) // make the default look good
|
|
|
|
if ((lpCreate->style & SBARS_SIZEGRIP) ||
|
|
((GetWindowStyle(lpCreate->hwndParent) & WS_THICKFRAME) &&
|
|
!(lpCreate->style & (CCS_NOPARENTALIGN | CCS_TOP | CCS_NOMOVEY))))
|
|
if (pStatusInfo->hTheme)
|
|
{
|
|
RECT rcContent = {0, 0, GRIPSIZE, GRIPSIZE};
|
|
GetThemeBackgroundExtent(pStatusInfo->hTheme, NULL, SP_GRIPPER, 0, &rcContent, &rcContent);
|
|
|
|
pStatusInfo->dxGripper = RECTWIDTH(rcContent);
|
|
pStatusInfo->dyGripper = RECTHEIGHT(rcContent);
|
|
}
|
|
else
|
|
{
|
|
pStatusInfo->dxGripper = GRIPSIZE;
|
|
}
|
|
|
|
return 0; // success
|
|
}
|
|
|
|
// lprc is left unchanged, but used as scratch
|
|
void WINAPI DrawStatusText(HDC hDC, LPRECT lprc, LPCTSTR pszText, UINT uFlags)
|
|
{
|
|
DrawStatusTextEx(NULL, hDC, lprc, pszText, NULL, uFlags, FALSE);
|
|
}
|
|
|
|
void WINAPI DrawStatusTextA(HDC hDC, LPRECT lprc, LPCSTR pszText, UINT uFlags)
|
|
{
|
|
INT cch;
|
|
LPWSTR lpw;
|
|
|
|
cch = lstrlenA(pszText);
|
|
lpw = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, ((cch + 1) * sizeof(TCHAR)));
|
|
|
|
if (!lpw)
|
|
{
|
|
return;
|
|
}
|
|
|
|
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, pszText, cch, lpw, cch);
|
|
DrawStatusTextW(hDC, lprc, lpw, uFlags);
|
|
|
|
LocalFree((LPVOID)lpw);
|
|
}
|
|
|
|
BOOL Status_GetRect(PSTATUSINFO pStatusInfo, int nthPart, LPRECT lprc)
|
|
{
|
|
PSTRINGINFO pStringInfo = pStatusInfo->sInfo;
|
|
|
|
if (lprc == NULL)
|
|
return FALSE;
|
|
|
|
if (pStatusInfo->sSimple.uType & SBT_NOSIMPLE)
|
|
{
|
|
RECT rc;
|
|
int nRightMargin, i;
|
|
|
|
/* Get the client rect and inset the top and bottom. Then set
|
|
* up the right side for entry into the loop
|
|
*/
|
|
GetClientRect(pStatusInfo->ci.hwnd, &rc);
|
|
|
|
if (pStatusInfo->dxGripper && !IsZoomed(pStatusInfo->ci.hwndParent))
|
|
{
|
|
rc.right = rc.right - pStatusInfo->dxGripper + pStatusInfo->nBorderX;
|
|
}
|
|
|
|
rc.top += pStatusInfo->nBorderY;
|
|
|
|
nRightMargin = rc.right - pStatusInfo->nBorderX;
|
|
rc.right = pStatusInfo->nBorderX - pStatusInfo->nBorderPart;
|
|
|
|
for (i = 0; i < pStatusInfo->nParts; ++i, ++pStringInfo)
|
|
{
|
|
// WARNING! This pixel computation is also in PaintStatusWnd,
|
|
// so make sure the two algorithms are in sync.
|
|
|
|
if (pStringInfo->right == 0)
|
|
continue;
|
|
|
|
rc.left = rc.right + pStatusInfo->nBorderPart;
|
|
|
|
rc.right = pStringInfo->right;
|
|
|
|
// size the right-most one to the end with room for border
|
|
if (rc.right < 0 || rc.right > nRightMargin)
|
|
rc.right = nRightMargin;
|
|
|
|
// if the part is real small, don't show it
|
|
// Bug keep the rc.left valid in case this item happens to
|
|
// be the nthPart.
|
|
if ((rc.right - rc.left) < pStatusInfo->nBorderPart)
|
|
rc.left = rc.right;
|
|
|
|
if (i == nthPart)
|
|
{
|
|
if (nthPart == pStatusInfo->nParts - 1)
|
|
{
|
|
rc.right -= pStatusInfo->dxGripper;
|
|
}
|
|
|
|
*lprc = rc;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
void PaintStatusWnd(PSTATUSINFO pStatusInfo, HDC hdcIn, PSTRINGINFO pStringInfo, int nParts, int nBorderX)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
RECT rc, rcGripper, rcClient;
|
|
int nRightMargin, i;
|
|
HFONT hOldFont = NULL;
|
|
UINT uType;
|
|
BOOL bDrawGrip;
|
|
|
|
// paint the whole client area
|
|
GetClientRect(pStatusInfo->ci.hwnd, &rcClient);
|
|
rc = rcClient;
|
|
|
|
if (hdcIn)
|
|
{
|
|
ps.rcPaint = rc;
|
|
ps.hdc = hdcIn;
|
|
}
|
|
else
|
|
BeginPaint(pStatusInfo->ci.hwnd, &ps);
|
|
|
|
|
|
rc.top += pStatusInfo->nBorderY;
|
|
|
|
bDrawGrip = pStatusInfo->dxGripper && !IsZoomed(pStatusInfo->ci.hwndParent);
|
|
|
|
if (bDrawGrip)
|
|
rcGripper = rc;
|
|
|
|
nRightMargin = rc.right - nBorderX;
|
|
rc.right = nBorderX - pStatusInfo->nBorderPart;
|
|
|
|
if (pStatusInfo->hStatFont)
|
|
hOldFont = SelectObject(ps.hdc, pStatusInfo->hStatFont);
|
|
|
|
for (i=0; i<nParts; ++i, ++pStringInfo)
|
|
{
|
|
BOOL fClipRight = FALSE;
|
|
// WARNING! This pixel computation is also in Status_GetRect,
|
|
// so make sure the two algorithms are in sync.
|
|
if (pStringInfo->right == 0)
|
|
continue;
|
|
|
|
rc.left = rc.right + pStatusInfo->nBorderPart;
|
|
rc.right = pStringInfo->right;
|
|
|
|
// size the right-most one to the end with room for border
|
|
if (rc.right < 0 || rc.right > nRightMargin)
|
|
rc.right = nRightMargin;
|
|
|
|
if(g_fMEEnabled && (rc.right > (nRightMargin-pStatusInfo->dxGripper)))
|
|
{
|
|
//
|
|
// for MidEast we DONT overpaint the rhs with the grip, this will
|
|
// lose the begining of the text.
|
|
//
|
|
rc.right = nRightMargin-pStatusInfo->dxGripper;
|
|
}
|
|
|
|
if (pStatusInfo->dxGripper && !IsZoomed(pStatusInfo->ci.hwndParent)
|
|
&& rc.right > rcClient.right - pStatusInfo->dxGripper + 1)
|
|
{
|
|
fClipRight = TRUE;
|
|
}
|
|
|
|
DebugMsg(TF_STATUS, TEXT("SBPaint: part=%d, x/y=%d/%d"), i, rc.left, rc.right);
|
|
|
|
// if the part is real small, don't show it
|
|
if (((rc.right - rc.left) < pStatusInfo->nBorderPart) || !RectVisible(ps.hdc, &rc))
|
|
continue;
|
|
|
|
uType = pStringInfo->uType;
|
|
|
|
if ((uType&SBT_ALLTYPES) == SBT_NORMAL)
|
|
{
|
|
DrawStatusTextEx(pStatusInfo, ps.hdc, &rc, (LPTSTR)OFFSETOF(pStringInfo->dwString), pStringInfo, uType, fClipRight);
|
|
}
|
|
else
|
|
{
|
|
DrawStatusTextEx(pStatusInfo, ps.hdc, &rc, c_szNULL, pStringInfo, uType, fClipRight);
|
|
|
|
if (uType & SBT_OWNERDRAW)
|
|
{
|
|
DRAWITEMSTRUCT di;
|
|
|
|
di.CtlID = GetWindowID(pStatusInfo->ci.hwnd);
|
|
di.itemID = i;
|
|
di.hwndItem = pStatusInfo->ci.hwnd;
|
|
di.hDC = ps.hdc;
|
|
di.rcItem = rc;
|
|
InflateRect(&di.rcItem, -g_cxBorder, -g_cyBorder);
|
|
di.itemData = pStringInfo->dwString;
|
|
|
|
SaveDC(ps.hdc);
|
|
IntersectClipRect(ps.hdc, di.rcItem.left, di.rcItem.top,
|
|
di.rcItem.right, di.rcItem.bottom);
|
|
SendMessage(pStatusInfo->ci.hwndParent, WM_DRAWITEM, di.CtlID,
|
|
(LPARAM)(LPTSTR)&di);
|
|
RestoreDC(ps.hdc, -1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bDrawGrip)
|
|
{
|
|
RECT rcTemp;
|
|
COLORREF crBkColorOld;
|
|
COLORREF crBkColor;
|
|
|
|
pStatusInfo->dxGripper = min(pStatusInfo->dxGripper, pStatusInfo->nFontHeight);
|
|
|
|
if (pStatusInfo->hTheme)
|
|
{
|
|
rcGripper.left = rcGripper.right - pStatusInfo->dxGripper;
|
|
rcGripper.top = rcGripper.bottom - pStatusInfo->dyGripper;
|
|
|
|
DrawThemeBackground(pStatusInfo->hTheme,
|
|
ps.hdc,
|
|
SP_GRIPPER,
|
|
1,
|
|
&rcGripper, 0);
|
|
}
|
|
else
|
|
{
|
|
// draw the grip
|
|
rcGripper.right -= g_cxBorder; // inside the borders
|
|
rcGripper.bottom -= g_cyBorder;
|
|
|
|
rcGripper.left = rcGripper.right - pStatusInfo->dxGripper; // make it square
|
|
rcGripper.top += g_cyBorder;
|
|
// rcGripper.top = rcGripper.bottom - pStatusInfo->dxGripper;
|
|
|
|
crBkColor = g_clrBtnFace;
|
|
if ((pStatusInfo->_clrBk != CLR_DEFAULT))
|
|
crBkColor = pStatusInfo->_clrBk;
|
|
|
|
crBkColorOld = SetBkColor(ps.hdc, crBkColor);
|
|
DrawFrameControl(ps.hdc, &rcGripper, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
|
|
|
|
// clear out the border edges to make this appear on the same level
|
|
|
|
// NOTE: these values line up right only for the default scroll bar
|
|
// width. for others this is close enough...
|
|
|
|
// right border
|
|
rcTemp.top = rcGripper.bottom - pStatusInfo->dxGripper + g_cyBorder + g_cyEdge;
|
|
rcTemp.left = rcGripper.right;
|
|
rcTemp.bottom = rcGripper.bottom;
|
|
rcTemp.right = rcGripper.right + g_cxBorder;
|
|
FillRectClr(ps.hdc, &rcTemp, crBkColor);
|
|
|
|
// bottom border
|
|
rcTemp.top = rcGripper.bottom;
|
|
rcTemp.left = rcGripper.left + g_cyBorder + g_cxEdge;
|
|
rcTemp.bottom = rcGripper.bottom + g_cyBorder;
|
|
rcTemp.right = rcGripper.right + g_cxBorder;
|
|
FillRectClr(ps.hdc, &rcTemp, crBkColor);
|
|
|
|
SetBkColor(ps.hdc, crBkColorOld);
|
|
}
|
|
}
|
|
|
|
if (hOldFont)
|
|
SelectObject(ps.hdc, hOldFont);
|
|
|
|
if (hdcIn == NULL)
|
|
EndPaint(pStatusInfo->ci.hwnd, &ps);
|
|
}
|
|
|
|
|
|
BOOL SetStatusText(PSTATUSINFO pStatusInfo, PSTRINGINFO pStringInfo, UINT uPart, LPCTSTR lpStr)
|
|
{
|
|
PTSTR pString;
|
|
UINT wLen;
|
|
int nPart;
|
|
RECT rc;
|
|
|
|
nPart = LOBYTE(uPart);
|
|
|
|
/* Note it is up to the app the dispose of the previous itemData for
|
|
* SBT_OWNERDRAW
|
|
*/
|
|
if ((pStringInfo->uType&SBT_ALLTYPES) == SBT_NORMAL)
|
|
LocalFree((HLOCAL)OFFSETOF(pStringInfo->dwString));
|
|
|
|
/* Set to the NULL string in case anything goes wrong
|
|
*
|
|
* But be careful to preserve simple-ness if this is the simple
|
|
* pane being updated.
|
|
*/
|
|
if (nPart == 0xFF)
|
|
{
|
|
pStringInfo->uType = (uPart & 0xff00) | (pStringInfo->uType & 0x00ff);
|
|
nPart = 0; // There is only one simple part, so we are part 0
|
|
}
|
|
else
|
|
{
|
|
pStringInfo->uType = uPart & 0xff00;
|
|
}
|
|
pStringInfo->uType &= ~SBT_ALLTYPES;
|
|
pStringInfo->uType |= SBT_NULL;
|
|
|
|
/* Invalidate the rect of this pane.
|
|
*
|
|
* Note that we don't check whether the pane is actually visible
|
|
* in the current status bar mode. The result is some gratuitous
|
|
* invalidates and updates. Oh well.
|
|
*/
|
|
GetClientRect(pStatusInfo->ci.hwnd, &rc);
|
|
if (nPart)
|
|
rc.left = pStringInfo[-1].right;
|
|
if (pStringInfo->right > 0)
|
|
rc.right = pStringInfo->right;
|
|
InvalidateRect(pStatusInfo->ci.hwnd, &rc, TRUE);
|
|
|
|
switch (uPart&SBT_ALLTYPES)
|
|
{
|
|
case 0:
|
|
/* If lpStr==NULL, we have the NULL string
|
|
*/
|
|
if (HIWORD64(lpStr))
|
|
{
|
|
wLen = lstrlen(lpStr);
|
|
if (wLen)
|
|
{
|
|
pString = (PTSTR)LocalAlloc(LPTR, (wLen+1)*sizeof(TCHAR));
|
|
pStringInfo->dwString = (ULONG_PTR)(LPTSTR)pString;
|
|
if (pString)
|
|
{
|
|
pStringInfo->uType |= SBT_NORMAL;
|
|
|
|
/* Copy the string
|
|
*/
|
|
lstrcpy(pString, lpStr);
|
|
|
|
/* Replace unprintable characters (like CR/LF) with spaces
|
|
*/
|
|
for ( ; *pString;
|
|
pString=(PTSTR)OFFSETOF(CharNextEx((WORD)pStatusInfo->uiCodePage, pString, 0)))
|
|
if ((unsigned)(*pString)<(unsigned)TEXT(' ') && *pString!= TEXT('\t'))
|
|
*pString = TEXT(' ');
|
|
}
|
|
else
|
|
{
|
|
/* We return FALSE to indicate there was an error setting
|
|
* the string
|
|
*/
|
|
return(FALSE);
|
|
}
|
|
}
|
|
}
|
|
else if (LOWORD(lpStr))
|
|
{
|
|
/* We don't allow this anymore; the app needs to set the ownerdraw
|
|
* bit for ownerdraw.
|
|
*/
|
|
return(FALSE);
|
|
}
|
|
break;
|
|
|
|
case SBT_OWNERDRAW:
|
|
pStringInfo->uType |= SBT_OWNERDRAW;
|
|
pStringInfo->dwString = (ULONG_PTR)lpStr;
|
|
break;
|
|
|
|
default:
|
|
return(FALSE);
|
|
}
|
|
|
|
UpdateWindow(pStatusInfo->ci.hwnd);
|
|
return(TRUE);
|
|
}
|
|
|
|
BOOL SetStatusParts(PSTATUSINFO pStatusInfo, int nParts, LPINT lpInt)
|
|
{
|
|
int i;
|
|
int prev;
|
|
PSTRINGINFO pStringInfo, pStringInfoTemp;
|
|
BOOL bRedraw = FALSE;
|
|
|
|
if (nParts != pStatusInfo->nParts)
|
|
{
|
|
TOOLINFO ti = {0};
|
|
int n;
|
|
|
|
if (pStatusInfo->hwndToolTips)
|
|
{
|
|
ti.cbSize = sizeof(ti);
|
|
ti.hwnd = pStatusInfo->ci.hwnd;
|
|
ti.lpszText = LPSTR_TEXTCALLBACK;
|
|
|
|
for(n = 0; n < pStatusInfo->nParts; n++)
|
|
{
|
|
ti.uId = n;
|
|
SendMessage(pStatusInfo->hwndToolTips, TTM_DELTOOL, 0, (LPARAM)&ti);
|
|
}
|
|
}
|
|
|
|
bRedraw = TRUE;
|
|
|
|
/* Note that if nParts > pStatusInfo->nParts, this loop
|
|
* does nothing
|
|
*/
|
|
for (i=pStatusInfo->nParts-nParts,
|
|
pStringInfo=&pStatusInfo->sInfo[nParts]; i>0;
|
|
--i, ++pStringInfo)
|
|
{
|
|
if ((pStringInfo->uType&SBT_ALLTYPES) == SBT_NORMAL)
|
|
LocalFree((HLOCAL)OFFSETOF(pStringInfo->dwString));
|
|
pStringInfo->uType = SBT_NULL;
|
|
}
|
|
|
|
/* Realloc to the new size and store the new pointer
|
|
*/
|
|
pStringInfoTemp = (PSTRINGINFO)CCLocalReAlloc(pStatusInfo->sInfo,
|
|
nParts * sizeof(STRINGINFO));
|
|
if (!pStringInfoTemp)
|
|
return(FALSE);
|
|
pStatusInfo->sInfo = pStringInfoTemp;
|
|
|
|
/* Note that if nParts < pStatusInfo->nParts, this loop
|
|
* does nothing
|
|
*/
|
|
for (i=nParts-pStatusInfo->nParts,
|
|
pStringInfo=&pStatusInfo->sInfo[pStatusInfo->nParts]; i>0;
|
|
--i, ++pStringInfo)
|
|
{
|
|
pStringInfo->uType = SBT_NULL;
|
|
pStringInfo->right = 0;
|
|
}
|
|
pStatusInfo->nParts = nParts;
|
|
|
|
StatusUpdateToolTips(pStatusInfo);
|
|
}
|
|
|
|
//
|
|
// Under stress, apps such as Explorer might pass coordinates that
|
|
// result in status bar panes with negative width, so make sure
|
|
// each edge is at least as far to the right as the previous.
|
|
//
|
|
prev = 0;
|
|
for (i=0, pStringInfo=pStatusInfo->sInfo; i<nParts;
|
|
++i, ++pStringInfo, ++lpInt)
|
|
{
|
|
int right = *lpInt;
|
|
// The last component is allowed to have *lpInt = -1.
|
|
// Otherwise, make sure the widths are nondecreasing.
|
|
if (!(right == -1 && i == nParts - 1) && right < prev)
|
|
right = prev;
|
|
DebugMsg(TF_STATUS, TEXT("SBSetParts: part=%d, rlimit=%d (%d)"), i, right, *lpInt);
|
|
if (pStringInfo->right != right)
|
|
{
|
|
bRedraw = TRUE;
|
|
pStringInfo->right = right;
|
|
}
|
|
prev = right;
|
|
}
|
|
|
|
/* Only redraw if necesary (if the number of parts has changed or
|
|
* a border has changed)
|
|
*/
|
|
if (bRedraw)
|
|
InvalidateRect(pStatusInfo->ci.hwnd, NULL, TRUE);
|
|
|
|
RecalcTooltipRects(pStatusInfo);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void SBSetFont(PSTATUSINFO pStatusInfo, HFONT hFont, BOOL bInvalidate)
|
|
{
|
|
NewFont(pStatusInfo, hFont, TRUE);
|
|
if (bInvalidate)
|
|
{
|
|
RedrawWindow(pStatusInfo->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
|
|
}
|
|
}
|
|
|
|
BOOL SBSetText(PSTATUSINFO pStatusInfo, WPARAM wParam, LPCTSTR lpsz)
|
|
{
|
|
BOOL bRet;
|
|
UINT idChild;
|
|
|
|
DebugMsg(TF_STATUS, TEXT("SBSetText(%04x, [%s])"), wParam, lpsz);
|
|
|
|
/* This is the "simple" status bar pane
|
|
*/
|
|
if (LOBYTE(wParam) == 0xff)
|
|
{
|
|
UINT uSimple;
|
|
|
|
// Note that we do not allow OWNERDRAW for a "simple" status bar
|
|
if (wParam & SBT_OWNERDRAW)
|
|
return FALSE;
|
|
|
|
//
|
|
// IE4 BUG-FOR-BUG COMPATIBILITY: In IE4, changing the SIMPLE
|
|
// status bar text while you were in complex mode caused the simple
|
|
// text to be painted briefly. It would get cleaned up the next time
|
|
// the window got invalidated.
|
|
//
|
|
// Corel Gallery actually RELIES ON THIS BUG!
|
|
//
|
|
// Since the bad text got cleaned up on every invalidate, they
|
|
// "worked around" their bug by doing SB_SETTEXT in their idle loop,
|
|
// so the "correct" text gets repainted no matter what.
|
|
//
|
|
// So if we have an old status bar, emulate the bug by temporarily
|
|
// setting the status bar into SIMPLE mode for the duration of the
|
|
// SetStatusText call.
|
|
|
|
uSimple = pStatusInfo->sSimple.uType;
|
|
if (pStatusInfo->ci.iVersion < 5)
|
|
pStatusInfo->sSimple.uType = (uSimple & 0xFF00);
|
|
|
|
bRet = SetStatusText(pStatusInfo, &pStatusInfo->sSimple,
|
|
(UINT) wParam, lpsz);
|
|
|
|
if (pStatusInfo->ci.iVersion < 5)
|
|
pStatusInfo->sSimple.uType |= LOBYTE(uSimple);
|
|
|
|
idChild = 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
if ((UINT)pStatusInfo->nParts <= (UINT)LOBYTE(wParam))
|
|
bRet = FALSE;
|
|
else
|
|
bRet = SetStatusText(pStatusInfo, &pStatusInfo->sInfo[LOBYTE(wParam)],
|
|
(UINT) wParam, lpsz);
|
|
|
|
idChild = LOBYTE(wParam);
|
|
}
|
|
|
|
if (bRet)
|
|
NotifyWinEvent(EVENT_OBJECT_NAMECHANGE, pStatusInfo->ci.hwnd,
|
|
OBJID_CLIENT, idChild+1);
|
|
|
|
return bRet;
|
|
}
|
|
|
|
//
|
|
// iPart - which part we are querying
|
|
// lpOutBuf - output buffer, NULL if no output desired
|
|
// cchOutBuf - size of output buffer in characters
|
|
// flags - zero or more of the following flags
|
|
//
|
|
// SBGT_ANSI - Output buffer is ANSI
|
|
// SBGT_UNICODE - Output buffer is unicode
|
|
// SBGT_TCHAR - Output buffer is TCHAR
|
|
// SBGT_OWNERDRAWOK- Return refdata for owner-draw
|
|
//
|
|
// If item is a string, and output buffer is provided, then returns
|
|
// output string length (not including null) in low word, flags in
|
|
// high word.
|
|
//
|
|
// If item is a string, and no output buffer is provided, then returns
|
|
// source string length (not including null) in low word, flags in
|
|
// high word.
|
|
//
|
|
// If item is owner-draw and SBGT_OWNERDRAWOK is set, then return the
|
|
// refdata for the owner-draw item.
|
|
//
|
|
// If item is owner-draw and SBGT_OWNERDRAWOK is clear, then treats
|
|
// string as if it were empty.
|
|
//
|
|
|
|
#define SBGT_ANSI 0
|
|
#define SBGT_UNICODE 1
|
|
#define SBGT_OWNERDRAWOK 2
|
|
|
|
#define SBGT_TCHAR SBGT_UNICODE
|
|
|
|
// Value for cchOutBuf to indicate largest possible output buffer size
|
|
// We cannot use -1 because StrCpyNW thinks -1 means a negative-size buffer.
|
|
// Since the maximum value we return is 0xFFFF (LOWORD), and the return value
|
|
// doesn't include the trailing null, the largest incoming buffer is one
|
|
// greater.
|
|
#define SBGT_INFINITE 0x00010000
|
|
|
|
LRESULT SBGetText(PSTATUSINFO pStatusInfo, WPARAM iPart, LPVOID lpOutBuf, int cchOutBuf, UINT flags)
|
|
{
|
|
UINT uType;
|
|
PTSTR pString;
|
|
ULONG_PTR dwString;
|
|
UINT wLen;
|
|
|
|
if (!pStatusInfo || (UINT)pStatusInfo->nParts<=iPart)
|
|
return(0);
|
|
|
|
if (pStatusInfo->sSimple.uType & SBT_NOSIMPLE)
|
|
{
|
|
uType = pStatusInfo->sInfo[iPart].uType;
|
|
dwString = pStatusInfo->sInfo[iPart].dwString;
|
|
} else {
|
|
uType = pStatusInfo->sSimple.uType;
|
|
dwString = pStatusInfo->sSimple.dwString;
|
|
}
|
|
|
|
// Catch the boundary condition early so we only have to check lpOutBuf
|
|
if (cchOutBuf == 0)
|
|
lpOutBuf = NULL;
|
|
|
|
if ((uType&SBT_ALLTYPES) == SBT_NORMAL)
|
|
{
|
|
pString = (PTSTR)dwString;
|
|
if (flags & SBGT_UNICODE)
|
|
{
|
|
if (lpOutBuf)
|
|
{
|
|
StrCpyNW(lpOutBuf, pString, cchOutBuf);
|
|
wLen = lstrlenW(lpOutBuf);
|
|
}
|
|
else
|
|
wLen = lstrlen(pString);
|
|
}
|
|
else
|
|
{
|
|
// We have to use ProduceAFromW because WideCharToMultiByte
|
|
// will simply fail if the output buffer isn't big enough,
|
|
// but we want to copy as many as will fit.
|
|
LPSTR pStringA = ProduceAFromW(pStatusInfo->ci.uiCodePage, pString);
|
|
if (pStringA)
|
|
{
|
|
if (lpOutBuf)
|
|
{
|
|
lstrcpynA(lpOutBuf, pStringA, cchOutBuf);
|
|
wLen = lstrlenA(lpOutBuf);
|
|
}
|
|
else
|
|
wLen = lstrlenA(pStringA); // Return required ANSI buf size
|
|
FreeProducedString(pStringA);
|
|
}
|
|
else
|
|
{
|
|
if (lpOutBuf)
|
|
*(LPSTR)lpOutBuf = '\0';
|
|
wLen = 0; // Eek, horrible memory problem
|
|
}
|
|
}
|
|
/* Set this back to 0 to return to the app
|
|
*/
|
|
uType &= ~SBT_ALLTYPES;
|
|
}
|
|
else
|
|
{
|
|
if (lpOutBuf)
|
|
{
|
|
if (flags & SBGT_UNICODE)
|
|
*(LPWSTR)lpOutBuf = L'\0';
|
|
else
|
|
*(LPSTR)lpOutBuf = '\0';
|
|
}
|
|
wLen = 0;
|
|
|
|
// Only SB_GETTEXT[AW] returns the raw owner-draw refdata
|
|
if ((uType&SBT_ALLTYPES)==SBT_OWNERDRAW && (flags & SBGT_OWNERDRAWOK))
|
|
return(dwString);
|
|
}
|
|
|
|
return(MAKELONG(wLen, uType));
|
|
}
|
|
|
|
void SBSetBorders(PSTATUSINFO pStatusInfo, LPINT lpInt)
|
|
{
|
|
// pStatusInfo->nBorderX = lpInt[0] < 0 ? 0 : lpInt[0];
|
|
pStatusInfo->nBorderX = 0;
|
|
|
|
// pStatusInfo->nBorderY = lpInt[1] < 0 ? 2 * g_cyBorder : lpInt[1];
|
|
pStatusInfo->nBorderY = g_cyEdge;
|
|
|
|
// pStatusInfo->nBorderPart = lpInt[2] < 0 ? 2 * g_cxBorder : lpInt[2];
|
|
pStatusInfo->nBorderPart = g_cxEdge;
|
|
}
|
|
|
|
void StatusUpdateToolTips(PSTATUSINFO psi)
|
|
{
|
|
if (psi->hwndToolTips)
|
|
{
|
|
TOOLINFO ti = {0};
|
|
int n;
|
|
|
|
ti.cbSize = sizeof(ti);
|
|
ti.hwnd = psi->ci.hwnd;
|
|
ti.lpszText = LPSTR_TEXTCALLBACK;
|
|
for(n = 0; n < psi->nParts; n++)
|
|
{
|
|
ti.uId = n;
|
|
SendMessage(psi->hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)&ti);
|
|
}
|
|
|
|
}
|
|
}
|
|
void StatusForceCreateTooltips(PSTATUSINFO psi)
|
|
{
|
|
if (psi->ci.style & SBT_TOOLTIPS && !psi->hwndToolTips)
|
|
{
|
|
TOOLINFO ti = {0};
|
|
psi->hwndToolTips = CreateWindowEx(WS_EX_TRANSPARENT, c_szSToolTipsClass, NULL, WS_POPUP | TTS_ALWAYSTIP,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
psi->ci.hwnd, NULL, HINST_THISDLL, NULL);
|
|
|
|
|
|
ti.cbSize = sizeof(ti);
|
|
ti.hwnd = psi->ci.hwnd;
|
|
ti.lpszText = LPSTR_TEXTCALLBACK;
|
|
ti.uId = SB_SIMPLEID;
|
|
SendMessage(psi->hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)&ti);
|
|
StatusUpdateToolTips(psi);
|
|
RecalcTooltipRects(psi);
|
|
}
|
|
}
|
|
|
|
LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
PSTATUSINFO pStatusInfo = GetWindowPtr(hWnd, 0);
|
|
NMCLICK nm;
|
|
int nNotification;
|
|
|
|
if (!pStatusInfo && uMsg != WM_CREATE)
|
|
goto DoDefault;
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_CREATE:
|
|
return InitStatusWnd(hWnd, (LPCREATESTRUCT)lParam);
|
|
|
|
case WM_SYSCOLORCHANGE:
|
|
if (pStatusInfo->hwndToolTips)
|
|
SendMessage(pStatusInfo->hwndToolTips, uMsg, wParam, lParam);
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
case WM_LBUTTONDOWN:
|
|
StatusForceCreateTooltips(pStatusInfo);
|
|
RelayToToolTips(pStatusInfo->hwndToolTips, hWnd, uMsg, wParam, lParam);
|
|
break;
|
|
case WM_STYLECHANGED:
|
|
{
|
|
if (wParam == GWL_EXSTYLE)
|
|
{
|
|
//
|
|
// If the RTL_MIRROR extended style bit had changed, let's
|
|
// repaint the control window
|
|
//
|
|
if ((pStatusInfo->ci.dwExStyle&RTL_MIRRORED_WINDOW) !=
|
|
(((LPSTYLESTRUCT)lParam)->styleNew&RTL_MIRRORED_WINDOW))
|
|
InvalidateRect(pStatusInfo->ci.hwnd, NULL, TRUE);
|
|
|
|
//
|
|
// Save the new ex-style bits
|
|
//
|
|
pStatusInfo->ci.dwExStyle = ((LPSTYLESTRUCT)lParam)->styleNew;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
case WM_SETTINGCHANGE:
|
|
InitGlobalColors();
|
|
InitGlobalMetrics(wParam);
|
|
|
|
if (pStatusInfo->hwndToolTips)
|
|
SendMessage(pStatusInfo->hwndToolTips, uMsg, wParam, lParam);
|
|
|
|
if (pStatusInfo->dxGripper)
|
|
pStatusInfo->dxGripper = GRIPSIZE;
|
|
|
|
if (wParam == SPI_SETNONCLIENTMETRICS)
|
|
{
|
|
if (pStatusInfo->bDefFont)
|
|
{
|
|
if (pStatusInfo->hStatFont)
|
|
{
|
|
DeleteObject(pStatusInfo->hStatFont);
|
|
pStatusInfo->hStatFont = NULL;
|
|
pStatusInfo->bDefFont = FALSE;
|
|
SBSetFont(pStatusInfo, 0, TRUE);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (pStatusInfo)
|
|
{
|
|
int i;
|
|
PSTRINGINFO pStringInfo;
|
|
|
|
// FALSE = Don't resize while being destroyed...
|
|
NewFont(pStatusInfo, (HFONT)-1, FALSE);
|
|
for (i=pStatusInfo->nParts-1, pStringInfo=pStatusInfo->sInfo;
|
|
i>=0; --i, ++pStringInfo)
|
|
{
|
|
if ((pStringInfo->uType&SBT_ALLTYPES) == SBT_NORMAL)
|
|
LocalFree((HLOCAL)OFFSETOF(pStringInfo->dwString));
|
|
Str_Set(&pStringInfo->pszToolTip, NULL);
|
|
}
|
|
|
|
if ((pStatusInfo->sSimple.uType&SBT_ALLTYPES) == SBT_NORMAL)
|
|
LocalFree((HLOCAL)OFFSETOF(pStatusInfo->sSimple.dwString));
|
|
|
|
if (IsWindow(pStatusInfo->hwndToolTips))
|
|
DestroyWindow(pStatusInfo->hwndToolTips);
|
|
|
|
Str_Set(&pStatusInfo->sSimple.pszToolTip, NULL);
|
|
|
|
if (pStatusInfo->sInfo)
|
|
LocalFree(pStatusInfo->sInfo);
|
|
if (pStatusInfo->hTheme)
|
|
CloseThemeData(pStatusInfo->hTheme);
|
|
LocalFree((HLOCAL)pStatusInfo);
|
|
SetWindowInt(hWnd, 0, 0);
|
|
|
|
}
|
|
break;
|
|
|
|
case WM_NCHITTEST:
|
|
if (pStatusInfo->dxGripper && !IsZoomed(pStatusInfo->ci.hwndParent))
|
|
{
|
|
RECT rc;
|
|
|
|
// already know height is valid. if the width is in the grip,
|
|
// show the sizing cursor
|
|
GetWindowRect(pStatusInfo->ci.hwnd, &rc);
|
|
|
|
//
|
|
// If this is a RTL mirrored status window, then measure
|
|
// from the near edge (screen coordinates) since Screen
|
|
// Coordinates are not RTL mirrored.
|
|
// [samera]
|
|
//
|
|
if (pStatusInfo->ci.dwExStyle&RTL_MIRRORED_WINDOW) {
|
|
if (GET_X_LPARAM(lParam) < (rc.left + pStatusInfo->dxGripper))
|
|
return HTBOTTOMLEFT;
|
|
} else if (GET_X_LPARAM(lParam) > (rc.right - pStatusInfo->dxGripper)) {
|
|
return HTBOTTOMRIGHT;
|
|
}
|
|
}
|
|
goto DoDefault;
|
|
|
|
case WM_SETTEXT:
|
|
{
|
|
wParam = 0;
|
|
uMsg = SB_SETTEXT;
|
|
}
|
|
/* Fall through */
|
|
case SB_SETTEXT:
|
|
return SBSetText(pStatusInfo, wParam, (LPCTSTR)lParam);
|
|
|
|
case SB_SETTEXTA:
|
|
{
|
|
BOOL bRet, bAlloced = FALSE;
|
|
LPTSTR lpsz;
|
|
|
|
if (!(wParam & SBT_OWNERDRAW))
|
|
{
|
|
lpsz = ProduceWFromA(pStatusInfo->uiCodePage, (LPSTR)lParam);
|
|
bAlloced = TRUE;
|
|
}
|
|
else
|
|
{
|
|
lpsz = (LPTSTR)lParam;
|
|
}
|
|
|
|
if (!pStatusInfo)
|
|
bRet = FALSE;
|
|
else
|
|
{
|
|
bRet = SBSetText(pStatusInfo, wParam, (LPCTSTR)lpsz);
|
|
}
|
|
|
|
if (bAlloced)
|
|
{
|
|
FreeProducedString(lpsz);
|
|
}
|
|
return bRet;
|
|
}
|
|
|
|
// The WM_GETTEXT and WM_GETTEXTLENGTH messages must return a
|
|
// character count, no flags. (Otherwise USER gets mad at us.)
|
|
// So we throw away the flags by returning only the LOWORD().
|
|
case WM_GETTEXT:
|
|
return LOWORD(SBGetText(pStatusInfo, 0, (LPVOID)lParam, (int)wParam, SBGT_TCHAR));
|
|
case WM_GETTEXTLENGTH:
|
|
return LOWORD(SBGetText(pStatusInfo, 0, NULL, 0, SBGT_TCHAR));
|
|
|
|
case SB_GETTEXT:
|
|
/* We assume the buffer is large enough to hold the string, just
|
|
* as listboxes do; the app should call SB_GETTEXTLEN first
|
|
*/
|
|
return SBGetText(pStatusInfo, wParam, (LPVOID)lParam, SBGT_INFINITE, SBGT_TCHAR | SBGT_OWNERDRAWOK);
|
|
|
|
case SB_GETTEXTLENGTH:
|
|
return SBGetText(pStatusInfo, wParam, NULL, 0, SBGT_TCHAR);
|
|
|
|
case SB_GETTEXTA:
|
|
/* We assume the buffer is large enough to hold the string, just
|
|
* as listboxes do; the app should call SB_GETTEXTLEN first
|
|
*/
|
|
return SBGetText(pStatusInfo, wParam, (LPVOID)lParam, SBGT_INFINITE, SBGT_ANSI | SBGT_OWNERDRAWOK);
|
|
|
|
case SB_GETTEXTLENGTHA:
|
|
return SBGetText(pStatusInfo, wParam, NULL, 0, SBGT_ANSI);
|
|
|
|
case SB_SETBKCOLOR:
|
|
{
|
|
COLORREF clr = pStatusInfo->_clrBk;
|
|
pStatusInfo->_clrBk = (COLORREF)lParam;
|
|
InvalidateRect(hWnd, NULL, TRUE);
|
|
return clr;
|
|
}
|
|
|
|
case SB_SETPARTS:
|
|
if (!wParam || wParam>MAXPARTS)
|
|
return FALSE;
|
|
|
|
return SetStatusParts(pStatusInfo, (int) wParam, (LPINT)lParam);
|
|
|
|
case SB_GETPARTS:
|
|
if (lParam)
|
|
{
|
|
PSTRINGINFO pStringInfo;
|
|
LPINT lpInt;
|
|
|
|
/* Fill in the lesser of the number of entries asked for or
|
|
* the number of entries there are
|
|
*/
|
|
if (wParam > (WPARAM)pStatusInfo->nParts)
|
|
wParam = pStatusInfo->nParts;
|
|
|
|
for (pStringInfo=pStatusInfo->sInfo, lpInt=(LPINT)lParam;
|
|
wParam>0; --wParam, ++pStringInfo, ++lpInt)
|
|
*lpInt = pStringInfo->right;
|
|
}
|
|
|
|
/* Always return the number of actual entries
|
|
*/
|
|
return(pStatusInfo->nParts);
|
|
|
|
case SB_GETBORDERS:
|
|
// Can't validate any more than this....
|
|
if ((LPINT)lParam != NULL)
|
|
{
|
|
((LPINT)lParam)[0] = pStatusInfo->nBorderX;
|
|
((LPINT)lParam)[1] = pStatusInfo->nBorderY;
|
|
((LPINT)lParam)[2] = pStatusInfo->nBorderPart;
|
|
return TRUE;
|
|
}
|
|
else
|
|
return FALSE;
|
|
|
|
case SB_ISSIMPLE:
|
|
return !(pStatusInfo->sSimple.uType & SBT_NOSIMPLE);
|
|
|
|
case SB_GETRECT:
|
|
return Status_GetRect(pStatusInfo, (int)wParam, (LPRECT)lParam);
|
|
|
|
case SB_SETMINHEIGHT: // this is a substitute for WM_MEASUREITEM
|
|
pStatusInfo->nMinHeight = (int) wParam;
|
|
RecalcTooltipRects(pStatusInfo);
|
|
break;
|
|
|
|
case SB_SIMPLE:
|
|
{
|
|
BOOL bInvalidate = FALSE;
|
|
|
|
if (wParam)
|
|
{
|
|
if (pStatusInfo->sSimple.uType & SBT_NOSIMPLE)
|
|
{
|
|
pStatusInfo->sSimple.uType &= ~SBT_NOSIMPLE;
|
|
bInvalidate = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((pStatusInfo->sSimple.uType & SBT_NOSIMPLE) == 0)
|
|
{
|
|
pStatusInfo->sSimple.uType |= SBT_NOSIMPLE;
|
|
bInvalidate = TRUE;
|
|
}
|
|
}
|
|
|
|
if (bInvalidate) {
|
|
DebugMsg(TF_STATUS, TEXT("SB_SIMPLE: %d"), wParam);
|
|
RecalcTooltipRects(pStatusInfo);
|
|
SendNotifyEx(pStatusInfo->ci.hwndParent, pStatusInfo->ci.hwnd, SBN_SIMPLEMODECHANGE, NULL, FALSE);
|
|
InvalidateRect(pStatusInfo->ci.hwnd, NULL, TRUE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SB_SETICON:
|
|
case SB_GETICON:
|
|
{
|
|
PSTRINGINFO pStringInfo = NULL;
|
|
|
|
// -1 implies we are setting the icon for sSimple
|
|
if ((UINT_PTR)-1 == wParam)
|
|
pStringInfo = &pStatusInfo->sSimple;
|
|
else if(wParam < (UINT)pStatusInfo->nParts)
|
|
pStringInfo = &pStatusInfo->sInfo[wParam];
|
|
|
|
if (uMsg == SB_GETICON)
|
|
return (LRESULT)(pStringInfo ? pStringInfo->hIcon : NULL);
|
|
|
|
if (pStringInfo && (pStringInfo->hIcon != (HICON)lParam))
|
|
{
|
|
BITMAP bm = {0};
|
|
RECT rc;
|
|
|
|
if (lParam)
|
|
{
|
|
ICONINFO ii;
|
|
|
|
// Save the dimensions of the icon
|
|
GetIconInfo((HICON)lParam, &ii);
|
|
GetObject(ii.hbmColor, sizeof(BITMAP), &bm);
|
|
DeleteObject(ii.hbmColor);
|
|
DeleteObject(ii.hbmMask);
|
|
}
|
|
|
|
pStringInfo->sizeIcon.cx = bm.bmWidth;
|
|
pStringInfo->sizeIcon.cy = bm.bmHeight;
|
|
pStringInfo->hIcon = (HICON)lParam;
|
|
|
|
Status_GetRect(pStatusInfo, (int)wParam, &rc);
|
|
InvalidateRect(pStatusInfo->ci.hwnd, &rc, TRUE);
|
|
UpdateWindow(pStatusInfo->ci.hwnd);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// HIWORD(wParam) is the cbChar
|
|
// LOWORD(wParam) is the nPart
|
|
case SB_GETTIPTEXT:
|
|
{
|
|
PSTRINGINFO pStringInfo = GetStringInfo(pStatusInfo, LOWORD(wParam));
|
|
|
|
if (pStringInfo && pStringInfo->pszToolTip)
|
|
lstrcpyn((LPTSTR)lParam, pStringInfo->pszToolTip, HIWORD(wParam));
|
|
|
|
break;
|
|
}
|
|
|
|
case SB_SETTIPTEXT:
|
|
{
|
|
PSTRINGINFO pStringInfo = GetStringInfo(pStatusInfo, (int) wParam);
|
|
|
|
if (pStringInfo)
|
|
Str_Set(&pStringInfo->pszToolTip, (LPCTSTR)lParam);
|
|
|
|
break;
|
|
}
|
|
case SB_GETTIPTEXTA:
|
|
{
|
|
PSTRINGINFO pStringInfo = GetStringInfo(pStatusInfo, LOWORD(wParam));
|
|
|
|
if (pStringInfo)
|
|
WideCharToMultiByte(CP_ACP, 0, pStringInfo->pszToolTip, -1, (LPSTR)lParam,
|
|
HIWORD(wParam), NULL, NULL);
|
|
break;
|
|
}
|
|
|
|
case SB_SETTIPTEXTA:
|
|
{
|
|
PSTRINGINFO pStringInfo = GetStringInfo(pStatusInfo, (int) wParam);
|
|
LPTSTR lpsz;
|
|
|
|
lpsz = ProduceWFromA(pStatusInfo->uiCodePage, (LPSTR)lParam);
|
|
|
|
if (pStringInfo)
|
|
Str_Set(&pStringInfo->pszToolTip, (LPCTSTR)lpsz);
|
|
|
|
LocalFree(lpsz);
|
|
break;
|
|
}
|
|
|
|
#define lpNmhdr ((LPNMHDR)(lParam))
|
|
#define lpnmTT ((LPTOOLTIPTEXT) lParam)
|
|
#define IsTextPtr(lpszText) (((lpszText) != LPSTR_TEXTCALLBACK) && (HIWORD64(lpszText)))
|
|
case WM_NOTIFY:
|
|
{
|
|
PSTRINGINFO pStringInfo = NULL;
|
|
if (lpNmhdr->code == TTN_NEEDTEXT)
|
|
{
|
|
pStringInfo = GetStringInfo(pStatusInfo, (int) lpNmhdr->idFrom);
|
|
if (!pStringInfo || !pStringInfo->fNeedToTip)
|
|
break;
|
|
}
|
|
//
|
|
// We are just going to pass this on to the
|
|
// real parent. Note that -1 is used as
|
|
// the hwndFrom. This prevents SendNotifyEx
|
|
// from updating the NMHDR structure.
|
|
//
|
|
SendNotifyEx(pStatusInfo->ci.hwndParent, (HWND) -1,
|
|
lpNmhdr->code, lpNmhdr, pStatusInfo->ci.bUnicode);
|
|
|
|
if ((lpNmhdr->code == TTN_NEEDTEXT) && lpnmTT->lpszText
|
|
&& IsTextPtr(lpnmTT->lpszText) && !lpnmTT->lpszText[0])
|
|
{
|
|
if (pStringInfo)
|
|
lpnmTT->lpszText = pStringInfo->pszToolTip;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case WM_NOTIFYFORMAT:
|
|
return CIHandleNotifyFormat(&pStatusInfo->ci, lParam);
|
|
|
|
case WM_SETFONT:
|
|
if (!pStatusInfo)
|
|
return FALSE;
|
|
|
|
SBSetFont(pStatusInfo, (HFONT)wParam, (BOOL)lParam);
|
|
return TRUE;
|
|
|
|
case WM_LBUTTONUP:
|
|
nNotification = NM_CLICK;
|
|
StatusForceCreateTooltips(pStatusInfo);
|
|
RelayToToolTips(pStatusInfo->hwndToolTips, hWnd, uMsg, wParam, lParam);
|
|
goto SendNotify;
|
|
|
|
case WM_LBUTTONDBLCLK:
|
|
nNotification = NM_DBLCLK;
|
|
goto SendNotify;
|
|
|
|
case WM_RBUTTONDBLCLK:
|
|
nNotification = NM_RDBLCLK;
|
|
goto SendNotify;
|
|
|
|
case WM_RBUTTONUP:
|
|
nNotification = NM_RCLICK;
|
|
SendNotify:
|
|
LPARAM_TO_POINT(lParam, nm.pt);
|
|
nm.dwItemSpec = IndexFromPt(pStatusInfo, nm.pt);
|
|
if (!SendNotifyEx(pStatusInfo->ci.hwndParent, pStatusInfo->ci.hwnd, nNotification, (LPNMHDR)&nm,FALSE))
|
|
goto DoDefault;
|
|
return 0;
|
|
|
|
case WM_GETFONT:
|
|
if (!pStatusInfo)
|
|
return 0;
|
|
|
|
return (LRESULT)pStatusInfo->hStatFont;
|
|
|
|
case WM_SIZE:
|
|
{
|
|
int nHeight;
|
|
RECT rc;
|
|
LPTSTR lpStr;
|
|
PSTRINGINFO pStringInfo;
|
|
int i, nTabs;
|
|
|
|
if (!pStatusInfo)
|
|
return 0;
|
|
|
|
GetWindowRect(pStatusInfo->ci.hwnd, &rc);
|
|
rc.right -= rc.left; // -> dx
|
|
rc.bottom -= rc.top; // -> dy
|
|
|
|
// If there is no parent, then this is a top level window
|
|
if (pStatusInfo->ci.hwndParent)
|
|
{
|
|
ScreenToClient(pStatusInfo->ci.hwndParent, (LPPOINT)&rc);
|
|
|
|
//
|
|
// Places the status bar properly
|
|
//
|
|
if (pStatusInfo->ci.dwExStyle&RTL_MIRRORED_WINDOW)
|
|
rc.left -= rc.right;
|
|
}
|
|
|
|
// need room for text, 3d border, and extra edge
|
|
nHeight =
|
|
max(pStatusInfo->nFontHeight, g_cySmIcon) + 2 * g_cyBorder ;
|
|
|
|
if (nHeight < pStatusInfo->nMinHeight)
|
|
nHeight = pStatusInfo->nMinHeight;
|
|
nHeight += pStatusInfo->nBorderY;
|
|
|
|
|
|
// we don't have a divider thing -> force CCS_NODIVIDER
|
|
NewSize(pStatusInfo->ci.hwnd, nHeight, GetWindowStyle(pStatusInfo->ci.hwnd) | CCS_NODIVIDER,
|
|
rc.left, rc.top, rc.right, rc.bottom);
|
|
|
|
// If the pane is right aligned then we need to invalidate all the pane
|
|
// to force paint the entire pane. because the system will invalidate none if
|
|
// the status bar get shrieked or only the new added part if the status bar
|
|
// get grow and this does not work with the right justified text.
|
|
pStringInfo = pStatusInfo->sInfo;
|
|
for (i = 0; i < pStatusInfo->nParts; ++i, ++pStringInfo)
|
|
{
|
|
if ((pStringInfo->uType&SBT_ALLTYPES) == SBT_NORMAL &&
|
|
(lpStr = (LPTSTR)(pStringInfo->dwString)) != NULL)
|
|
{
|
|
for ( nTabs = 0; (lpStr = StrChr(lpStr, TEXT('\t'))) != NULL; lpStr++)
|
|
{
|
|
nTabs++;
|
|
}
|
|
if ( nTabs >= 2)
|
|
{
|
|
Status_GetRect(pStatusInfo, i, &rc);
|
|
InvalidateRect(pStatusInfo->ci.hwnd, &rc, FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// need to invalidate the right end of the status bar
|
|
// to maintain the finished edge look.
|
|
GetClientRect(pStatusInfo->ci.hwnd, &rc);
|
|
|
|
if (rc.right > pStatusInfo->nLastX)
|
|
rc.left = pStatusInfo->nLastX;
|
|
else
|
|
rc.left = rc.right;
|
|
rc.left -= (g_cxBorder + pStatusInfo->nBorderX);
|
|
if (pStatusInfo->dxGripper)
|
|
rc.left -= pStatusInfo->dxGripper;
|
|
else
|
|
rc.left -= pStatusInfo->nBorderPart;
|
|
|
|
if (pStatusInfo->hTheme)
|
|
{
|
|
MARGINS m = {0};
|
|
GetThemeMargins(pStatusInfo->hTheme, NULL, SP_PANE, 0, TMT_SIZINGMARGINS, &rc, &m);
|
|
rc.left -= m.cxRightWidth;
|
|
}
|
|
|
|
InvalidateRect(pStatusInfo->ci.hwnd, &rc, TRUE);
|
|
RecalcTooltipRects(pStatusInfo);
|
|
pStatusInfo->nLastX = rc.right;
|
|
break;
|
|
}
|
|
|
|
case WM_PRINTCLIENT:
|
|
case WM_PAINT:
|
|
if (!pStatusInfo)
|
|
break;
|
|
|
|
if (pStatusInfo->sSimple.uType & SBT_NOSIMPLE)
|
|
PaintStatusWnd(pStatusInfo, (HDC)wParam, pStatusInfo->sInfo, pStatusInfo->nParts, pStatusInfo->nBorderX);
|
|
else
|
|
PaintStatusWnd(pStatusInfo, (HDC)wParam, &pStatusInfo->sSimple, 1, 0);
|
|
|
|
return 0;
|
|
|
|
case WM_ERASEBKGND:
|
|
if (pStatusInfo)
|
|
{
|
|
RECT rc;
|
|
GetClientRect(hWnd, &rc);
|
|
if (pStatusInfo->hTheme)
|
|
{
|
|
DrawThemeBackground(pStatusInfo->hTheme, (HDC)wParam, 0, 0, &rc, 0);
|
|
return 1;
|
|
}
|
|
else if (pStatusInfo->_clrBk != CLR_DEFAULT)
|
|
{
|
|
FillRectClr((HDC)wParam, &rc, pStatusInfo->_clrBk);
|
|
return 1;
|
|
}
|
|
}
|
|
goto DoDefault;
|
|
|
|
case WM_GETOBJECT:
|
|
if( lParam == OBJID_QUERYCLASSNAMEIDX )
|
|
return MSAA_CLASSNAMEIDX_STATUS;
|
|
goto DoDefault;
|
|
|
|
case WM_THEMECHANGED:
|
|
if (pStatusInfo->hTheme)
|
|
CloseThemeData(pStatusInfo->hTheme);
|
|
|
|
pStatusInfo->hTheme = OpenThemeData(pStatusInfo->ci.hwnd, L"Status");
|
|
InvalidateRect(pStatusInfo->ci.hwnd, NULL, TRUE);
|
|
return 0;
|
|
|
|
default:
|
|
{
|
|
LRESULT lres;
|
|
if (CCWndProc(&pStatusInfo->ci, uMsg, wParam, lParam, &lres))
|
|
return lres;
|
|
}
|
|
break;
|
|
}
|
|
|
|
DoDefault:
|
|
return DefWindowProc(hWnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
BOOL InitStatusClass(HINSTANCE hInstance)
|
|
{
|
|
WNDCLASS rClass;
|
|
|
|
rClass.lpfnWndProc = StatusWndProc;
|
|
rClass.style = CS_DBLCLKS | CS_GLOBALCLASS | CS_VREDRAW;
|
|
rClass.cbClsExtra = 0;
|
|
rClass.cbWndExtra = sizeof(PSTATUSINFO);
|
|
rClass.hInstance = hInstance;
|
|
rClass.hIcon = NULL;
|
|
rClass.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
rClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
|
|
rClass.lpszMenuName = NULL;
|
|
rClass.lpszClassName = c_szStatusClass;
|
|
|
|
if (!RegisterClass(&rClass) && !GetClassInfo(hInstance, c_szStatusClass, &rClass))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HWND WINAPI CreateStatusWindow(LONG style, LPCTSTR pszText, HWND hwndParent, UINT uID)
|
|
{
|
|
// remove border styles to fix capone and other apps
|
|
|
|
return CreateWindowEx(0, c_szStatusClass, pszText, style & ~(WS_BORDER | CCS_NODIVIDER),
|
|
-100, -100, 10, 10, hwndParent, IntToPtr_(HMENU, uID), HINST_THISDLL, NULL);
|
|
}
|
|
|
|
HWND WINAPI CreateStatusWindowA(LONG style, LPCSTR pszText, HWND hwndParent,
|
|
UINT uID)
|
|
{
|
|
// remove border styles to fix capone and other apps
|
|
|
|
return CreateWindowExA(0, STATUSCLASSNAMEA, pszText, style & ~(WS_BORDER | CCS_NODIVIDER),
|
|
-100, -100, 10, 10, hwndParent, IntToPtr_(HMENU, uID), HINST_THISDLL, NULL);
|
|
}
|
|
void WINAPI DrawStatusTextEx(PSTATUSINFO pStatusInfo, HDC hDC, LPRECT lprc, LPCTSTR pszText, STRINGINFO * psi, UINT uFlags, BOOL fClipText)
|
|
{
|
|
int len, nWidth = 0, nHeight = 0;
|
|
HBRUSH hFaceBrush=NULL;
|
|
COLORREF crTextColor, crBkColor;
|
|
UINT uOpts = 0;
|
|
BOOL bNull;
|
|
int nOldMode;
|
|
int i = 0, left = 0;
|
|
LPTSTR lpTab, lpNext;
|
|
TCHAR szBuf[MAX_STATUS_TEXT_LEN];
|
|
int oldAlign;
|
|
BOOL fDrawnIcon = FALSE;
|
|
RECT rc = * lprc;
|
|
RECT rcItem = *lprc;
|
|
|
|
//
|
|
// IMPORTANT NOTE:
|
|
// pStatusInfo can be NULL, please check before reference.
|
|
//
|
|
|
|
if (uFlags & SBT_RTLREADING)
|
|
{
|
|
oldAlign = GetTextAlign(hDC);
|
|
SetTextAlign(hDC, oldAlign | TA_RTLREADING);
|
|
}
|
|
|
|
if (pszText)
|
|
lstrcpyn(szBuf, pszText, ARRAYSIZE(szBuf));
|
|
else
|
|
szBuf[0] = TEXT('\0');
|
|
|
|
if (pStatusInfo && pStatusInfo->hTheme)
|
|
{
|
|
if (!(uFlags & SBT_NOBORDERS))
|
|
DrawThemeBackground(pStatusInfo->hTheme, hDC, fClipText?SP_GRIPPERPANE:SP_PANE, 0, &rc, 0);
|
|
InflateRect(&rc, -g_cxBorder, -g_cyBorder);
|
|
|
|
crTextColor = SetTextColor(hDC, g_clrBtnText);
|
|
crBkColor = SetBkColor(hDC, g_clrBtnFace);
|
|
nOldMode = SetBkMode(hDC, TRANSPARENT);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Create the three brushes we need. If the button face is a solid
|
|
// color, then we will just draw in opaque, instead of using a
|
|
// brush to avoid the flash
|
|
//
|
|
if (GetNearestColor(hDC, g_clrBtnFace) == g_clrBtnFace ||
|
|
!(hFaceBrush = CreateSolidBrush(g_clrBtnFace)))
|
|
{
|
|
uOpts = ETO_CLIPPED | ETO_OPAQUE;
|
|
nOldMode = SetBkMode(hDC, OPAQUE);
|
|
}
|
|
else
|
|
{
|
|
uOpts = ETO_CLIPPED;
|
|
nOldMode = SetBkMode(hDC, TRANSPARENT);
|
|
}
|
|
crTextColor = SetTextColor(hDC, g_clrBtnText);
|
|
if (pStatusInfo && (pStatusInfo->_clrBk != CLR_DEFAULT))
|
|
crBkColor = SetBkColor(hDC, pStatusInfo->_clrBk);
|
|
else
|
|
crBkColor = SetBkColor(hDC, g_clrBtnFace);
|
|
|
|
// Draw the hilites
|
|
|
|
if (!(uFlags & SBT_NOBORDERS))
|
|
// BF_ADJUST does the InflateRect stuff
|
|
DrawEdge(hDC, &rc, (uFlags & SBT_POPOUT) ? BDR_RAISEDINNER : BDR_SUNKENOUTER, BF_RECT | BF_ADJUST);
|
|
else
|
|
InflateRect(&rc, -g_cxBorder, -g_cyBorder);
|
|
|
|
if (hFaceBrush)
|
|
{
|
|
HBRUSH hOldBrush = SelectObject(hDC, hFaceBrush);
|
|
if (hOldBrush)
|
|
{
|
|
PatBlt(hDC, rc.left, rc.top,
|
|
rc.right-rc.left, rc.bottom-rc.top, PATCOPY);
|
|
SelectObject(hDC, hOldBrush);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i=0, lpNext=szBuf, bNull=FALSE; i<3; ++i)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
int cxIcon = 0;
|
|
int leftIcon;
|
|
UINT uiCodePage = pStatusInfo? pStatusInfo->uiCodePage: CP_ACP;
|
|
/* Optimize for NULL left or center strings
|
|
*/
|
|
if (!(uFlags & SBT_NOTABPARSING))
|
|
{
|
|
if (*lpNext==TEXT('\t') && i<=1)
|
|
{
|
|
++lpNext;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Determine the end of the current string
|
|
*/
|
|
for (lpTab=lpNext; ; lpTab=CharNextEx((WORD)uiCodePage, lpTab, 0))
|
|
{
|
|
if (!*lpTab) {
|
|
bNull = TRUE;
|
|
break;
|
|
} else if (!(uFlags & SBT_NOTABPARSING))
|
|
{
|
|
if (*lpTab == TEXT('\t'))
|
|
break;
|
|
}
|
|
}
|
|
*lpTab = TEXT('\0');
|
|
len = lstrlen(lpNext);
|
|
|
|
if (pStatusInfo && pStatusInfo->hTheme)
|
|
{
|
|
RECT rc = {0};
|
|
hr = GetThemeTextExtent(pStatusInfo->hTheme, hDC, 0, 0, lpNext, -1, DT_CALCRECT | DT_SINGLELINE, &rc, &rc);
|
|
nWidth = RECTWIDTH(rc);
|
|
nHeight = RECTHEIGHT(rc);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
MGetTextExtent(hDC, lpNext, len, &nWidth, &nHeight);
|
|
|
|
if (psi)
|
|
{
|
|
if (psi->hIcon && !fDrawnIcon)
|
|
{
|
|
cxIcon = psi->sizeIcon.cx + g_cxEdge * 2;
|
|
fDrawnIcon = TRUE;
|
|
}
|
|
}
|
|
|
|
/* i=0 means left, 1 means center, and 2 means right justified text
|
|
*/
|
|
switch (i) {
|
|
case 0:
|
|
leftIcon = rcItem.left + g_cxEdge;
|
|
break;
|
|
|
|
case 1:
|
|
leftIcon = (rcItem.left + rcItem.right - (nWidth + cxIcon)) / 2;
|
|
break;
|
|
|
|
default:
|
|
leftIcon = rcItem.right - g_cxEdge - (nWidth + cxIcon);
|
|
break;
|
|
}
|
|
|
|
left = leftIcon + cxIcon;
|
|
|
|
if (psi)
|
|
{
|
|
if (cxIcon)
|
|
{
|
|
int nTop = rc.top + ((rc.bottom - rc.top) - (psi->sizeIcon.cy )) / 2 ;
|
|
|
|
if (leftIcon > rcItem.left)
|
|
{
|
|
if (psi->hIcon)
|
|
{
|
|
DrawIconEx(hDC, leftIcon, nTop, psi->hIcon,
|
|
psi->sizeIcon.cx, psi->sizeIcon.cy,
|
|
0, NULL, DI_NORMAL);
|
|
}
|
|
}
|
|
rc.left = leftIcon + cxIcon;
|
|
}
|
|
|
|
if (!*lpNext && cxIcon)
|
|
psi->fNeedToTip = TRUE;
|
|
else
|
|
psi->fNeedToTip = (BOOL)(nWidth >= (rc.right - rc.left));
|
|
}
|
|
|
|
if (pStatusInfo && pStatusInfo->hTheme)
|
|
{
|
|
RECT rcText = rc;
|
|
rcText.left = left;
|
|
rcText.top = (rc.bottom - nHeight + rc.top) / 2;
|
|
if (fClipText)
|
|
{
|
|
rcText.right -= pStatusInfo->dxGripper;
|
|
}
|
|
|
|
hr = DrawThemeText(pStatusInfo->hTheme, hDC, 0, 0, lpNext, -1, DT_SINGLELINE | DT_NOPREFIX, 0, &rcText);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
ExtTextOut(hDC, left, (rc.bottom - nHeight + rc.top) / 2, uOpts, &rc, lpNext, len, NULL);
|
|
|
|
/* Now that we have drawn text once, take off the OPAQUE flag
|
|
*/
|
|
uOpts = ETO_CLIPPED;
|
|
|
|
if (bNull)
|
|
break;
|
|
|
|
*lpTab = TEXT('\t');
|
|
lpNext = lpTab + 1;
|
|
}
|
|
|
|
if (uFlags & SBT_RTLREADING)
|
|
SetTextAlign(hDC, oldAlign);
|
|
|
|
SetTextColor(hDC, crTextColor);
|
|
SetBkColor(hDC, crBkColor);
|
|
SetBkMode(hDC, nOldMode);
|
|
|
|
if (hFaceBrush)
|
|
DeleteObject(hFaceBrush);
|
|
|
|
}
|
|
|
|
void RecalcTooltipRects(PSTATUSINFO pStatusInfo)
|
|
{
|
|
if(pStatusInfo->hwndToolTips)
|
|
{
|
|
UINT i;
|
|
TOOLINFO ti;
|
|
STRINGINFO * psi;
|
|
|
|
ti.cbSize = sizeof(ti);
|
|
ti.hwnd = pStatusInfo->ci.hwnd;
|
|
ti.lpszText = LPSTR_TEXTCALLBACK;
|
|
|
|
if (pStatusInfo->sSimple.uType & SBT_NOSIMPLE)
|
|
{
|
|
for ( i = 0, psi = pStatusInfo->sInfo; i < (UINT)pStatusInfo->nParts; i++, psi++)
|
|
{
|
|
ti.uId = i;
|
|
Status_GetRect(pStatusInfo, i, &ti.rect);
|
|
SendMessage(pStatusInfo->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
|
|
}
|
|
SetRect(&ti.rect, 0,0,0,0);
|
|
ti.uId = SB_SIMPLEID;
|
|
SendMessage(pStatusInfo->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
|
|
}
|
|
else
|
|
{
|
|
|
|
GetClientRect(pStatusInfo->ci.hwnd, &ti.rect);
|
|
InflateRect(&ti.rect, -g_cxBorder, -g_cyBorder);
|
|
ti.uId = SB_SIMPLEID;
|
|
SendMessage(pStatusInfo->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
|
|
SetRect(&ti.rect, 0,0,0,0);
|
|
for ( i = 0, psi = pStatusInfo->sInfo; i < (UINT)pStatusInfo->nParts; i++, psi++)
|
|
{
|
|
ti.uId = i;
|
|
SendMessage(pStatusInfo->hwndToolTips, TTM_NEWTOOLRECT, 0, (LPARAM)((LPTOOLINFO)&ti));
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
PSTRINGINFO GetStringInfo(PSTATUSINFO pStatusInfo, int nIndex)
|
|
{
|
|
PSTRINGINFO pRet = NULL;
|
|
|
|
if (nIndex == SB_SIMPLEID)
|
|
pRet = &pStatusInfo->sSimple;
|
|
else if (nIndex < pStatusInfo->nParts)
|
|
pRet = &pStatusInfo->sInfo[nIndex];
|
|
|
|
return pRet;
|
|
}
|
|
|
|
int IndexFromPt(PSTATUSINFO pStatusInfo, POINT pt)
|
|
{
|
|
RECT rc;
|
|
int nPart = 0;
|
|
|
|
//
|
|
// More IE4 bug-for-bug compatibility. IE4 tested for simple mode
|
|
// incorrectly.
|
|
//
|
|
if (pStatusInfo->ci.iVersion < 5)
|
|
{
|
|
// This is not a typo! Well, actually, it *is* a typo, but it's
|
|
// a typo we have to preserve for compatibility. I don't know if
|
|
// anybody relied on the typo, but I'm playing it safe.
|
|
//
|
|
// The bug was that in IE4, a click on a simple status bar usually
|
|
// came back as SB_HITTEST_NOITEM instead of SB_SIMPLEID.
|
|
//
|
|
// I re-parenthesized the test so typo.pl won't trigger. The original
|
|
// IE4 code lacked the parentheses.
|
|
|
|
if ((!pStatusInfo->sSimple.uType) & SBT_NOSIMPLE)
|
|
return SB_SIMPLEID;
|
|
}
|
|
else
|
|
{
|
|
if (!(pStatusInfo->sSimple.uType & SBT_NOSIMPLE))
|
|
return SB_SIMPLEID;
|
|
}
|
|
|
|
for(nPart = 0; nPart < pStatusInfo->nParts; nPart++)
|
|
{
|
|
Status_GetRect(pStatusInfo, nPart, &rc);
|
|
if (PtInRect(&rc, pt))
|
|
return nPart;
|
|
}
|
|
return SB_HITTEST_NOITEM;
|
|
}
|