// statbar.cpp : definition of old backward compatible CStatusBar // // This is a part of the Microsoft Foundation Classes C++ library. // Copyright (C) 1992-1998 Microsoft Corporation // All rights reserved. // // This source code is only intended as a supplement to the // Microsoft Foundation Classes Reference and related // electronic documentation provided with the library. // See these sources for detailed information regarding the // Microsoft Foundation Classes product. #include "stdafx.h" #define _AFX_NO_OLE_SUPPORT #include #include "statbar.h" #include "globals.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #define new DEBUG_NEW ///////////////////////////////////////////////////////////////////////////// // CStatusBar creation etc struct AFX_STATUSPANE { UINT nID; // IDC of indicator: 0 => normal text area UINT nStyle; // style flags (SBPS_*) int cxText; // width of string area in pixels // on both sides there is a 1 pixel gap and // a one pixel border, making a pane 4 pixels wider LPCTSTR lpszText; // text in the pane }; inline AFX_STATUSPANE* CStatusBar::_GetPanePtr(int nIndex) const { ASSERT(nIndex >= 0 && nIndex < m_nCount); ASSERT(m_pData != NULL); return ((AFX_STATUSPANE*)m_pData) + nIndex; } #ifdef AFX_INIT_SEG #pragma code_seg(AFX_INIT_SEG) #endif CStatusBar::CStatusBar() { m_hFont = NULL; // setup correct margins m_cxRightBorder = m_cxDefaultGap; m_cxSizeBox = 0; m_bHideSizeBox = FALSE; if (globalData.bWin4) { m_cxLeftBorder = 4; m_cyTopBorder = 2; m_cyBottomBorder = 0; m_cxRightBorder = 0; } if (globalData.hStatusFont == NULL) { // load status bar font CClientDC dc(NULL); LOGFONT logfont; memset(&logfont, 0, sizeof(logfont)); logfont.lfWeight = FW_NORMAL; logfont.lfHeight = -MulDiv(globalData.bWin4 ? 8 : 10, globalData.cyPixelsPerInch, 72); lstrcpy(logfont.lfFaceName, _T("MS Sans Serif")); BOOL bCustom = AfxCustomLogFont(AFX_IDS_STATUS_FONT, &logfont); if (bCustom || !GetSystemMetrics(SM_DBCSENABLED)) { // only set pitch & family if not a custom font if (!bCustom) logfont.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS; // 10 point height Sans Serif font (8 point for Win4) globalData.hStatusFont = ::CreateFontIndirect(&logfont); } if (globalData.hStatusFont == NULL) { if (!GetSystemMetrics(SM_DBCSENABLED)) TRACE0("Warning: Using system font for status font.\n"); globalData.hStatusFont = (HFONT)::GetStockObject(SYSTEM_FONT); } } } void CStatusBar::OnSettingChange(UINT /*uFlags*/, LPCTSTR /* lpszSection */) { if (globalData.bWin4) { // get the drawing area for the status bar CRect rect; GetClientRect(rect); CalcInsideRect(rect, TRUE); // the size box is based off the size of a scrollbar m_cxSizeBox = min(GetSystemMetrics(SM_CXVSCROLL)+1, rect.Height()); } } CStatusBar::~CStatusBar() { // free strings before freeing array of elements for (int i = 0; i < m_nCount; i++) VERIFY(SetPaneText(i, NULL, FALSE)); // no update } BOOL CStatusBar::PreCreateWindow(CREATESTRUCT& cs) { // in Win4, status bars do not have a border at all, since it is // provided by the client area. if (globalData.bWin4 && (m_dwStyle & (CBRS_ALIGN_ANY|CBRS_BORDER_ANY)) == CBRS_BOTTOM) { m_dwStyle &= ~(CBRS_BORDER_ANY|CBRS_BORDER_3D); } return CControlBar::PreCreateWindow(cs); } BOOL CStatusBar::Create(CWnd* pParentWnd, DWORD dwStyle, UINT nID) { ASSERT_VALID(pParentWnd); // must have a parent // save the style m_dwStyle = (dwStyle & CBRS_ALL); // create the HWND CRect rect; rect.SetRectEmpty(); LPCTSTR lpszClass = AfxRegisterWndClass(0, ::LoadCursor(NULL, IDC_ARROW), (HBRUSH)(COLOR_BTNFACE+1), NULL); if (!CWnd::Create(lpszClass, NULL, dwStyle, rect, pParentWnd, nID)) return FALSE; // Note: Parent must resize itself for control bar to be resized // set initial font and calculate bar height SendMessage(WM_SETFONT, (WPARAM)globalData.hStatusFont); return TRUE; } BOOL CStatusBar::SetIndicators(const UINT* lpIDArray, int nIDCount) { ASSERT_VALID(this); ASSERT(nIDCount >= 1); // must be at least one of them ASSERT(lpIDArray == NULL || AfxIsValidAddress(lpIDArray, sizeof(UINT) * nIDCount, FALSE)); // free strings before freeing array of elements for (int i = 0; i < m_nCount; i++) VERIFY(SetPaneText(i, NULL, FALSE)); // no update // first allocate array for panes and copy initial data if (!AllocElements(nIDCount, sizeof(AFX_STATUSPANE))) return FALSE; ASSERT(nIDCount == m_nCount); BOOL bOK = TRUE; if (lpIDArray != NULL) { ASSERT(m_hFont != NULL); // must have a font ! CString strText; CClientDC dcScreen(NULL); HGDIOBJ hOldFont = dcScreen.SelectObject(m_hFont); for (int i = 0; i < nIDCount; i++) { AFX_STATUSPANE* pSBP = _GetPanePtr(i); pSBP->nID = *lpIDArray++; if (pSBP->nID != 0) { if (!strText.LoadString(pSBP->nID)) { TRACE1("Warning: failed to load indicator string 0x%04X.\n", pSBP->nID); bOK = FALSE; break; } pSBP->cxText = dcScreen.GetTextExtent(strText, strText.GetLength()).cx; ASSERT(pSBP->cxText >= 0); if (!SetPaneText(i, strText, FALSE)) { bOK = FALSE; break; } } else { // no indicator (must access via index) // default to 1/4 the screen width (first pane is stretchy) pSBP->cxText = ::GetSystemMetrics(SM_CXSCREEN) / 4; if (i == 0) pSBP->nStyle |= (SBPS_STRETCH | SBPS_NOBORDERS); } } dcScreen.SelectObject(hOldFont); } return bOK; } #ifdef AFX_CORE3_SEG #pragma code_seg(AFX_CORE3_SEG) #endif ///////////////////////////////////////////////////////////////////////////// // CStatusBar attribute access int CStatusBar::CommandToIndex(UINT nIDFind) const { ASSERT_VALID(this); if (m_nCount <= 0) return -1; AFX_STATUSPANE* pSBP = _GetPanePtr(0); for (int i = 0; i < m_nCount; i++, pSBP++) if (pSBP->nID == nIDFind) return i; return -1; } UINT CStatusBar::GetItemID(int nIndex) const { ASSERT_VALID(this); return _GetPanePtr(nIndex)->nID; } void CStatusBar::GetItemRect(int nIndex, LPRECT lpRect) const { ASSERT_VALID(this); ASSERT(AfxIsValidAddress(lpRect, sizeof(RECT))); // return rectangle containing inset size ASSERT(nIndex >= 0 && nIndex < m_nCount); CRect rect; GetClientRect(rect); CalcInsideRect(rect, TRUE); // protect space for size box int cxSizeBox = m_bHideSizeBox ? 0 : m_cxSizeBox; int xMax = (rect.right -= cxSizeBox); if (cxSizeBox == 0) xMax += m_cxRightBorder + 1; // walk through to calculate extra space int cxExtra = rect.Width() + m_cxDefaultGap; AFX_STATUSPANE* pSBP = (AFX_STATUSPANE*)m_pData; for (int i = 0; i < m_nCount; i++, pSBP++) cxExtra -= (pSBP->cxText + CX_BORDER * 4 + m_cxDefaultGap); // if cxExtra <= 0 then we will not stretch but just clip for (i = 0, pSBP = (AFX_STATUSPANE*)m_pData; i < m_nCount; i++, pSBP++) { ASSERT(pSBP->cxText >= 0); int cxText = pSBP->cxText; if ((pSBP->nStyle & SBPS_STRETCH) && cxExtra > 0) { cxText += cxExtra; cxExtra = 0; } rect.right = rect.left + cxText + CX_BORDER * 4; rect.right = min(rect.right, xMax); if (i == nIndex) break; // stop with correct rectangle (includes border) rect.left = rect.right + m_cxDefaultGap; rect.left = min(rect.left, xMax); } ASSERT(i == nIndex); *lpRect = rect; } UINT CStatusBar::GetPaneStyle(int nIndex) const { return _GetPanePtr(nIndex)->nStyle; } void CStatusBar::SetPaneStyle(int nIndex, UINT nStyle) { AFX_STATUSPANE* pSBP = _GetPanePtr(nIndex); if (pSBP->nStyle != nStyle) { // just change the style of 1 pane, and invalidate it pSBP->nStyle = nStyle; CRect rect; GetItemRect(nIndex, &rect); InvalidateRect(rect); } } void CStatusBar::GetPaneInfo(int nIndex, UINT& nID, UINT& nStyle, int& cxWidth) const { ASSERT_VALID(this); AFX_STATUSPANE* pSBP = _GetPanePtr(nIndex); nID = pSBP->nID; nStyle = pSBP->nStyle; cxWidth = pSBP->cxText; } void CStatusBar::SetPaneInfo(int nIndex, UINT nID, UINT nStyle, int cxWidth) { ASSERT_VALID(this); AFX_STATUSPANE* pSBP = _GetPanePtr(nIndex); pSBP->nID = nID; SetPaneStyle(nIndex, nStyle); // single pane invalidate if (cxWidth != pSBP->cxText) { // change width of one pane -> invalidate the entire status bar pSBP->cxText = cxWidth; Invalidate(); } } void CStatusBar::GetPaneText(int nIndex, CString& s) const { ASSERT_VALID(this); AFX_STATUSPANE* pSBP = _GetPanePtr(nIndex); s = pSBP->lpszText; } BOOL CStatusBar::SetPaneText(int nIndex, LPCTSTR lpszNewText, BOOL bUpdate) { ASSERT_VALID(this); AFX_STATUSPANE* pSBP = _GetPanePtr(nIndex); if (pSBP->lpszText != NULL) { if (lpszNewText != NULL && lstrcmp(pSBP->lpszText, lpszNewText) == 0) return TRUE; // nothing to change free((LPVOID)pSBP->lpszText); } BOOL bOK = TRUE; if (lpszNewText == NULL || *lpszNewText == '\0') { pSBP->lpszText = NULL; } else { pSBP->lpszText = _tcsdup(lpszNewText); if (pSBP->lpszText == NULL) bOK = FALSE; // old text is lost and replaced by NULL } if (bUpdate) { // invalidate the text of the pane - not including the border CRect rect; GetItemRect(nIndex, &rect); if (!(pSBP->nStyle & SBPS_NOBORDERS)) rect.InflateRect(-CX_BORDER, -CY_BORDER); else rect.top -= CY_BORDER; // base line adjustment InvalidateRect(rect); } return bOK; } ///////////////////////////////////////////////////////////////////////////// // CStatusBar implementation CSize CStatusBar::CalcFixedLayout(BOOL, BOOL bHorz) { ASSERT_VALID(this); // recalculate based on font height + borders TEXTMETRIC tm; { CClientDC dcScreen(NULL); HGDIOBJ hOldFont = dcScreen.SelectObject(m_hFont); VERIFY(dcScreen.GetTextMetrics(&tm)); dcScreen.SelectObject(hOldFont); } CRect rectSize; rectSize.SetRectEmpty(); CalcInsideRect(rectSize, bHorz); // will be negative size // sizeof text + 1 or 2 extra on top, 2 on bottom + borders return CSize(32767, tm.tmHeight - tm.tmInternalLeading + CY_BORDER * (globalData.bWin4 ? 4 : 3) - rectSize.Height()); } void CStatusBar::DoPaint(CDC* pDC) { ASSERT_VALID(this); ASSERT_VALID(pDC); CControlBar::DoPaint(pDC); // draw border CRect rect; GetClientRect(rect); CalcInsideRect(rect, TRUE); ASSERT(m_hFont != NULL); // must have a font! HGDIOBJ hOldFont = pDC->SelectObject(m_hFont); // protect space for size box int cxSizeBox = m_bHideSizeBox ? 0 : m_cxSizeBox; int xMax = (rect.right -= cxSizeBox); if (cxSizeBox == 0) xMax += m_cxRightBorder + 1; // walk through to calculate extra space int cxExtra = rect.Width() + m_cxDefaultGap; AFX_STATUSPANE* pSBP = (AFX_STATUSPANE*)m_pData; for (int i = 0; i < m_nCount; i++, pSBP++) cxExtra -= (pSBP->cxText + CX_BORDER * 4 + m_cxDefaultGap); // if cxExtra <= 0 then we will not stretch but just clip for (i = 0, pSBP = (AFX_STATUSPANE*)m_pData; i < m_nCount; i++, pSBP++) { ASSERT(pSBP->cxText >= 0); int cxText = pSBP->cxText; if ((pSBP->nStyle & SBPS_STRETCH) && cxExtra > 0) { cxText += cxExtra; cxExtra = 0; } rect.right = rect.left + cxText + CX_BORDER * 4; rect.right = min(rect.right, xMax); if (!globalData.bWin32s || pDC->RectVisible(&rect)) DrawStatusText(pDC, rect, pSBP->lpszText, pSBP->nStyle); rect.left = rect.right + m_cxDefaultGap; if (rect.left >= xMax) break; } pDC->SelectObject(hOldFont); // draw the size box in the bottom right corner if (cxSizeBox != 0) { int cxMax = min(cxSizeBox, rect.Height()+m_cyTopBorder); rect.left = xMax + (cxSizeBox - cxMax) + CX_BORDER; rect.bottom -= CX_BORDER; HPEN hPenOld = (HPEN)pDC->SelectObject(globalData.hpenBtnHilite); for (int i = 0; i < cxMax; i += 4) { pDC->MoveTo(rect.left+i, rect.bottom); pDC->LineTo(rect.left+cxMax, rect.bottom-cxMax+i); } pDC->SelectObject(globalData.hpenBtnShadow); for (i = 1; i < cxMax; i += 4) { pDC->MoveTo(rect.left+i, rect.bottom); pDC->LineTo(rect.left+cxMax, rect.bottom-cxMax+i); } for (i = 2; i < cxMax; i += 4) { pDC->MoveTo(rect.left+i, rect.bottom); pDC->LineTo(rect.left+cxMax, rect.bottom-cxMax+i); } pDC->SelectObject(hPenOld); } } void CStatusBar::DrawStatusText(CDC* pDC, const CRect& rect, LPCTSTR lpszText, UINT nStyle) { ASSERT_VALID(pDC); if (!(nStyle & SBPS_NOBORDERS)) { // draw the borders COLORREF clrHilite; COLORREF clrShadow; if (nStyle & SBPS_POPOUT) { // reverse colors clrHilite = globalData.clrBtnShadow; clrShadow = globalData.clrBtnHilite; } else { // normal colors clrHilite = globalData.clrBtnHilite; clrShadow = globalData.clrBtnShadow; } pDC->Draw3dRect(rect, clrShadow, clrHilite); } // just support left justified text if (lpszText != NULL && !(nStyle & SBPS_DISABLED)) { CRect rectText(rect); if (!(nStyle & SBPS_NOBORDERS)) // only adjust if there are borders rectText.InflateRect(-2*CX_BORDER, -CY_BORDER); else rectText.OffsetRect(0, -CY_BORDER); // baselines line up // background is already grey int nOldMode = pDC->SetBkMode(TRANSPARENT); COLORREF crTextColor = pDC->SetTextColor(globalData.clrBtnText); COLORREF crBkColor = pDC->SetBkColor(globalData.clrBtnFace); // align on bottom (since descent is more important than ascent) pDC->SetTextAlign(TA_LEFT | TA_BOTTOM); pDC->ExtTextOut(rectText.left, rectText.bottom, ETO_CLIPPED, &rectText, lpszText, lstrlen(lpszText), NULL); } } ///////////////////////////////////////////////////////////////////////////// // CStatusBar message handlers BEGIN_MESSAGE_MAP(CStatusBar, CControlBar) //{{AFX_MSG_MAP(CStatusBar) ON_WM_NCHITTEST() ON_WM_SYSCOMMAND() ON_WM_SIZE() ON_MESSAGE(WM_SETFONT, OnSetFont) ON_MESSAGE(WM_GETFONT, OnGetFont) ON_MESSAGE(WM_SETTEXT, OnSetText) ON_MESSAGE(WM_GETTEXT, OnGetText) ON_MESSAGE(WM_GETTEXTLENGTH, OnGetTextLength) ON_MESSAGE(WM_SIZEPARENT, OnSizeParent) ON_WM_SETTINGCHANGE() //}}AFX_MSG_MAP END_MESSAGE_MAP() UINT CStatusBar::OnNcHitTest(CPoint point) { // hit test the size box - convert to HTCAPTION if so if (!m_bHideSizeBox && m_cxSizeBox != 0) { CRect rect; GetClientRect(rect); CalcInsideRect(rect, TRUE); int cxMax = min(m_cxSizeBox-1, rect.Height()); rect.left = rect.right - cxMax; ClientToScreen(&rect); if (rect.PtInRect(point)) return HTBOTTOMRIGHT; } return CControlBar::OnNcHitTest(point); } void CStatusBar::OnSysCommand(UINT nID, LPARAM lParam) { if (!m_bHideSizeBox && m_cxSizeBox != 0 && (nID & 0xFFF0) == SC_SIZE) { CFrameWnd* pFrameWnd = GetParentFrame(); if (pFrameWnd != NULL) { pFrameWnd->SendMessage(WM_SYSCOMMAND, (WPARAM)nID, lParam); return; } } CControlBar::OnSysCommand(nID, lParam); } void CStatusBar::OnSize(UINT nType, int cx, int cy) { CControlBar::OnSize(nType, cx, cy); // adjust m_cxSizeBox if necessary OnSettingChange(0, NULL); // force repaint on resize (recalculate stretchy) Invalidate(); } LRESULT CStatusBar::OnSetFont(WPARAM wParam, LPARAM) { m_hFont = (HFONT)wParam; ASSERT(m_hFont != NULL); return 0L; // does not re-draw or invalidate - resize parent instead } LRESULT CStatusBar::OnGetFont(WPARAM, LPARAM) { return (LRESULT)(UINT)m_hFont; } LRESULT CStatusBar::OnSetText(WPARAM, LPARAM lParam) { int nIndex = CommandToIndex(0); if (nIndex < 0) return -1; return SetPaneText(nIndex, (LPCTSTR)lParam) ? 0 : -1; } LRESULT CStatusBar::OnGetText(WPARAM wParam, LPARAM lParam) { int nMaxLen = (int)wParam; if (nMaxLen == 0) return 0; // nothing copied LPTSTR lpszDest = (LPTSTR)lParam; int nLen = 0; int nIndex = CommandToIndex(0); // use pane with ID zero if (nIndex >= 0) { AFX_STATUSPANE* pSBP = _GetPanePtr(nIndex); nLen = pSBP->lpszText != NULL ? lstrlen(pSBP->lpszText) : 0; if (nLen > nMaxLen) nLen = nMaxLen - 1; // number of characters to copy (less term.) memcpy(lpszDest, pSBP->lpszText, nLen*sizeof(TCHAR)); } lpszDest[nLen] = '\0'; return nLen+1; // number of bytes copied } LRESULT CStatusBar::OnGetTextLength(WPARAM, LPARAM) { int nLen = 0; int nIndex = CommandToIndex(0); // use pane with ID zero if (nIndex >= 0) { AFX_STATUSPANE* pSBP = _GetPanePtr(nIndex); if (pSBP->lpszText != NULL) nLen = lstrlen(pSBP->lpszText); } return nLen; } LRESULT CStatusBar::OnSizeParent(WPARAM wParam, LPARAM lParam) { AFX_SIZEPARENTPARAMS* lpLayout = (AFX_SIZEPARENTPARAMS*)lParam; if (lpLayout->hDWP != NULL) { // hide size box if parent is maximized CFrameWnd* pFrameWnd = GetParentFrame(); if (pFrameWnd != NULL) { // the size box only appears when status bar is on the bottom // of a non-maximized, sizeable frame window. CRect rectFrame; pFrameWnd->GetClientRect(rectFrame); BOOL bHideSizeBox = pFrameWnd->IsZoomed() || !(pFrameWnd->GetStyle() & WS_THICKFRAME) || rectFrame.bottom != lpLayout->rect.bottom || rectFrame.right != lpLayout->rect.right; // update the size box hidden status, if changed if (bHideSizeBox != m_bHideSizeBox) { m_bHideSizeBox = bHideSizeBox; Invalidate(); } } } return CControlBar::OnSizeParent(wParam, lParam); } ///////////////////////////////////////////////////////////////////////////// // CStatusBar idle update through CStatusCmdUI class #define CStatusCmdUI COldStatusCmdUI class CStatusCmdUI : public CCmdUI // class private to this file! { public: // re-implementations only virtual void Enable(BOOL bOn); virtual void SetCheck(int nCheck); virtual void SetText(LPCTSTR lpszText); }; void CStatusCmdUI::Enable(BOOL bOn) { m_bEnableChanged = TRUE; CStatusBar* pStatusBar = (CStatusBar*)m_pOther; ASSERT(pStatusBar != NULL); ASSERT_KINDOF(CStatusBar, pStatusBar); ASSERT(m_nIndex < m_nIndexMax); UINT nNewStyle = pStatusBar->GetPaneStyle(m_nIndex) & ~SBPS_DISABLED; if (!bOn) nNewStyle |= SBPS_DISABLED; pStatusBar->SetPaneStyle(m_nIndex, nNewStyle); } void CStatusCmdUI::SetCheck(int nCheck) // "checking" will pop out the text { CStatusBar* pStatusBar = (CStatusBar*)m_pOther; ASSERT(pStatusBar != NULL); ASSERT_KINDOF(CStatusBar, pStatusBar); ASSERT(m_nIndex < m_nIndexMax); UINT nNewStyle = pStatusBar->GetPaneStyle(m_nIndex) & ~SBPS_POPOUT; if (nCheck != 0) nNewStyle |= SBPS_POPOUT; pStatusBar->SetPaneStyle(m_nIndex, nNewStyle); } void CStatusCmdUI::SetText(LPCTSTR lpszText) { ASSERT(m_pOther != NULL); ASSERT_KINDOF(CStatusBar, m_pOther); ASSERT(m_nIndex < m_nIndexMax); ((CStatusBar*)m_pOther)->SetPaneText(m_nIndex, lpszText); } void CStatusBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler) { CStatusCmdUI state; state.m_pOther = this; state.m_nIndexMax = (UINT)m_nCount; for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++) { state.m_nID = _GetPanePtr(state.m_nIndex)->nID; state.DoUpdate(pTarget, bDisableIfNoHndler); } // update the dialog controls added to the status bar UpdateDialogControls(pTarget, bDisableIfNoHndler); } ///////////////////////////////////////////////////////////////////////////// // CStatusBar diagnostics #ifdef _DEBUG void CStatusBar::AssertValid() const { CControlBar::AssertValid(); } void CStatusBar::Dump(CDumpContext& dc) const { CControlBar::Dump(dc); dc << "\nm_hFont = " << (UINT)m_hFont; if (dc.GetDepth() > 0) { for (int i = 0; i < m_nCount; i++) { dc << "\nstatus pane[" << i << "] = {"; dc << "\n\tnID = " << _GetPanePtr(i)->nID; dc << "\n\tnStyle = " << _GetPanePtr(i)->nStyle; dc << "\n\tcxText = " << _GetPanePtr(i)->cxText; dc << "\n\tlpszText = " << _GetPanePtr(i)->lpszText; dc << "\n\t}"; } } dc << "\n"; } #endif //_DEBUG #undef new #ifdef AFX_INIT_SEG #pragma code_seg(AFX_INIT_SEG) #endif IMPLEMENT_DYNAMIC(CStatusBar, CControlBar)