// ======================================================================================== // Class Implementation : COXSizeToolBar // ======================================================================================== // Source file : OXSizeToolBar.cpp // Version: 9.3 // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. // Some portions Copyright (C)1994-5 Micro Focus Inc, 2465 East Bayshore Rd, Palo Alto, CA 94303. // ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include "OXSizeToolBar.h" #include "UTBStrOp.h" #include "UTB64Bit.h" #ifdef _DEBUG #undef THIS_FILE static char BASED_CODE THIS_FILE[] = __FILE__; #endif IMPLEMENT_DYNAMIC(COXSizeToolBar, COXSizeControlBar) #define new DEBUG_NEW ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // CToolBar idle update through COXToolCmdUI class. void COXToolCmdUI::Enable(BOOL bOn) { m_bEnableChanged = TRUE; COXSizeToolBar* pSizeToolBar = (COXSizeToolBar*)m_pOther; COXToolBarCtrl* pToolBar = &pSizeToolBar->m_ToolCtrl; ASSERT(pToolBar != NULL); ASSERT(m_nIndex < m_nIndexMax); // Get toolbar button state TBBUTTON TB; pToolBar->GetButton(m_nIndex, &TB); UINT nNewState = TB.fsState & ~TBSTATE_ENABLED; if (bOn) nNewState |= TBSTATE_ENABLED; if (nNewState != TB.fsState) pToolBar->SetState(m_nID, nNewState); } void COXToolCmdUI::SetCheck(int nCheck) { ASSERT(nCheck >= 0 && nCheck <= 2); // 0=>off, 1=>on, 2=>indeterminate COXSizeToolBar* pSizeToolBar = (COXSizeToolBar*)m_pOther; COXToolBarCtrl* pToolBar = &pSizeToolBar->m_ToolCtrl; ASSERT(pToolBar != NULL); ASSERT(m_nIndex < m_nIndexMax); // Get toolbar button state TBBUTTON TB; pToolBar->GetButton(m_nIndex, &TB); UINT nNewState = TB.fsState & ~ (TBSTATE_CHECKED | TBSTATE_INDETERMINATE); if (nCheck == 1) nNewState |= TBSTATE_CHECKED; else if (nCheck == 2) nNewState |= TBSTATE_INDETERMINATE; if (nNewState != TB.fsState) pToolBar->SetState(m_nID, nNewState); // should we set the button style too ? // pToolBar->_SetButtonStyle(m_nIndex, nNewStyle | TBBS_CHECKBOX); */ } void COXToolCmdUI::SetText(LPCTSTR) { // ignore it } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // COXToolBarCtrl ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// COXToolBarCtrl::COXToolBarCtrl() { } COXToolBarCtrl::~COXToolBarCtrl() { } BEGIN_MESSAGE_MAP(COXToolBarCtrl, CToolBarCtrl) //{{AFX_MSG_MAP(COXToolBarCtrl) ON_WM_LBUTTONDOWN() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // COXToolBarCtrl message handlers // Returns the size that would bound all the buttons in the toolbar void COXToolBarCtrl::GetBoundingSize(CSize & size) { size.cx = 0; size.cy = 0; for (int i = GetButtonCount(); i >= 0; i--) { CRect rect; if (GetItemRect(i, &rect)) { size.cx = __max(size.cx, rect.right); size.cy = __max(size.cy, rect.bottom); } } } // returns index of button that lies at that point int COXToolBarCtrl::HitTestButtons(CPoint point) { // Hit test all the buttons. It might be an idea to cache the rectangles for all the // items, but for now this loop doesn't seem too bad for (int i = GetButtonCount(); i >= 0; i--) { TBBUTTON tbButton; CRect rect; if (GetItemRect(i, &rect)) { if (rect.PtInRect(point)) { GetButton(i, &tbButton); if (tbButton.fsStyle == TBSTYLE_SEP) return -1; // inside a separator else return i; // inside a button } if (point.y > rect.bottom) // if point we're testing is below items in the // array, then stop (minor optimization) break; } } return -1; } void COXToolBarCtrl::OnLButtonDown(UINT nFlags, CPoint point) { if (HitTestButtons(point) != -1) { CToolBarCtrl::OnLButtonDown(nFlags, point); } else { CWnd* pParent = GetOwner(); ASSERT(pParent != NULL); // map to co-ordinates of parent window ClientToScreen(&point); pParent->ScreenToClient(&point); // let the parent handle it pParent->SendMessage(WM_LBUTTONDOWN, nFlags, MAKELONG(point.x, point.y)); } } ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // COXSizeToolBar ///////////////////////////////////////////////////////////////////////////// // Definition of static members // Data members ------------------------------------------------------------- // protected: // private: // Member functions --------------------------------------------------------- // public: #define TOOLCTRL_ID 99 ///////////////////////////////////////////////////////////////////////////// // COXSizeToolBar COXSizeToolBar::COXSizeToolBar(int nStyle) : COXSizeControlBar(nStyle) { // these are the default sizes for the toolbar bitmap m_ToolCtrlButtonSize.cx = 16; m_ToolCtrlButtonSize.cy = 15; m_pBitmapIds = NULL; m_nBitmapButtons = 0; } COXSizeToolBar::~COXSizeToolBar() { } BEGIN_MESSAGE_MAP(COXSizeToolBar, COXSizeControlBar) //{{AFX_MSG_MAP(COXSizeToolBar) ON_WM_CREATE() //}}AFX_MSG_MAP ON_NOTIFY_RANGE(TTN_NEEDTEXT, 0, 0xffff, OnTBNToolTip) ON_NOTIFY_RANGE(TBN_QUERYINSERT, 0, 0xffff, OnTBNQueryInsert) ON_NOTIFY_RANGE(TBN_QUERYDELETE, 0, 0xffff, OnTBNQueryDelete) ON_NOTIFY_RANGE(TBN_BEGINADJUST, 0, 0xffff, OnTBNBeginAdjust) ON_NOTIFY_RANGE(TBN_TOOLBARCHANGE, 0, 0xffff, OnTBNToolBarChange) ON_NOTIFY_RANGE(TBN_GETBUTTONINFO, 0, 0xffff, OnTBNGetButtonInfo) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // COXSizeToolBar message handlers #define DOCKED_HORZ_BORDER 4 #define DOCKED_VERT_BORDER 2 // respond to this event as we need to override it void COXSizeToolBar::OnSizedOrDocked(int cx, int cy, BOOL /* bFloating */, int /* flags */) { CRect rect(1, 1, cx, cy); // rectangle for CToolbarCtrl // shrink rectangle if we're docked rect.InflateRect(-DOCKED_HORZ_BORDER, -DOCKED_VERT_BORDER); m_ToolCtrl.MoveWindow(&rect); m_ToolCtrl.AutoSize(); } BOOL COXSizeToolBar::Create(CWnd* pParent, DWORD dwStyle, UINT nID, LPRECT pRect) { m_ToolCtrlButtonSize.cx = 16; m_ToolCtrlButtonSize.cy = 15; // if no rectangle supplied, then what is hopefully as sensible default. // ie a single row of buttons CRect rect; if (pRect != NULL) rect.CopyRect(pRect); else { rect.left = 0; rect.top = 0; rect.bottom = m_ToolCtrlButtonSize.cy + 18; rect.right = m_ToolCtrlButtonSize.cx * 12; } BOOL status = COXSizeControlBar::Create(pParent, NULL, nID, dwStyle, rect); if (status == TRUE) { } return status; } int COXSizeToolBar::OnCreate(LPCREATESTRUCT lpCS) { if (COXSizeControlBar::OnCreate(lpCS) == -1) return -1; CRect rect; GetClientRect(&rect); DWORD dwStyle = WS_VISIBLE | WS_CHILD | TBSTYLE_WRAPABLE | TBSTYLE_TOOLTIPS | CCS_NOPARENTALIGN | CCS_NORESIZE | CCS_NODIVIDER; // | CCS_NOHILITE* - CCS_NOHILITE doesn't exist in VC2.2 // If there a list of bitmap id's, then let the user configure the toolbar if (m_pBitmapIds != NULL) dwStyle |= CCS_ADJUSTABLE; if (!m_ToolCtrl.Create(dwStyle, rect, this, TOOLCTRL_ID)) { TRACE0("Failed to create CToolBarCtrl\n"); return -1; } return 0; } // These are functions designed as 1-1 replacements for existing CToolBar functions. They // are not the most efficient way to make use of the CSizeToolBarCtrl control, but mean it'll // work with standard AppWizard created applications. BOOL COXSizeToolBar::LoadBitmap(LPCTSTR lpszResourceName) { if (!m_Bitmap.LoadBitmap(lpszResourceName)) return FALSE; // get the bitmap info, and use this to get the no of buttons in the bitmap BITMAP bm; m_Bitmap.GetObject(sizeof(BITMAP), &bm); int nButtons = bm.bmWidth / m_ToolCtrlButtonSize.cx; if (m_ToolCtrl.AddBitmap(nButtons, &m_Bitmap) == -1) return FALSE; return TRUE; } void COXSizeToolBar::SetSizes(SIZE sizeButton, SIZE sizeImage) { // if succeeded, keep track of it in the size of the control if (m_ToolCtrl.SetButtonSize(sizeButton)) m_ToolCtrlButtonSize = sizeButton; m_ToolCtrl.SetBitmapSize(sizeImage); } void COXSizeToolBar::SetBitmapIds(UINT* pIds, int nButtons) { m_pBitmapIds = pIds; m_nBitmapButtons = nButtons; } int COXSizeToolBar::FindBitmapIndex(UINT nID) { ASSERT(m_pBitmapIds != NULL); for (int i = 0; i < m_nBitmapButtons ; i++) { if (m_pBitmapIds[i] == (int)nID) return i; } return -1; } // emulate CToolBar::SetButtons() BOOL COXSizeToolBar::SetButtons(UINT* pButtons, int nButtons) { // allocate an array of TBBUTTON's TBBUTTON* parrButtons = new TBBUTTON[nButtons]; // allocate an array int nImageNo = 0; // no of image in bitmap (coun int nBtn = 0; // no of buttons we've actually created (including separators) // may not equal no of buttons supplied by user, as id's may not // be found int i = 0; for (i = 0; i < nButtons; i++) { UINT nID = pButtons[i]; parrButtons[i].dwData = NULL; parrButtons[i].iString = NULL; if (nID == ID_SEPARATOR) { parrButtons[nBtn].iBitmap = NULL; parrButtons[nBtn].idCommand = 0; parrButtons[nBtn].fsState = 0; parrButtons[nBtn].fsStyle = TBSTYLE_SEP; } else { if (m_pBitmapIds != NULL) // if there's a list of bitmaps, then translate this { nImageNo = FindBitmapIndex(nID); if (nImageNo == -1) { TRACE1("Couldn't find bitmap for ID=%d\n", nID); continue; // skip to next iteration } else { parrButtons[i].iBitmap = nImageNo; } } else { parrButtons[i].iBitmap = nImageNo; nImageNo++; } parrButtons[nBtn].idCommand = nID; parrButtons[nBtn].fsState = TBSTATE_ENABLED; parrButtons[nBtn].fsStyle = TBSTYLE_BUTTON; } nBtn++; } BOOL status = m_ToolCtrl.AddButtons(nBtn, parrButtons); for (i = 0; i < nBtn; i++) { if (parrButtons[i].fsStyle == TBSTYLE_BUTTON) { CRect ButtonRect; m_ToolCtrl.GetItemRect(i, &ButtonRect); m_VertDockSize.cx = ButtonRect.Width() + (DOCKED_HORZ_BORDER + 1) * 2; // One set is enough break; } } delete parrButtons; return status; } // tool tip notification handler for tool bar afx_msg void COXSizeToolBar::OnTBNToolTip(UINT /* uID */, NMHDR* pNMHDR, LRESULT* /* pResult */) { TOOLTIPTEXT* pTip = (TOOLTIPTEXT*)pNMHDR; pTip->hinst = NULL; pTip->lpszText = NULL; UINT ButtonId = PtrToUint(pTip->hdr.idFrom); CString strText; if (strText.LoadString(ButtonId)) { LPCTSTR pTipText = _tcschr(strText, _T('\n')); // tool tip is after "\n" in the string if (pTipText != NULL) { UTBStr::tcscpy(pTip->szText, 80, pTipText + 1); pTip->lpszText = pTip->szText; // try to ensure tool tip control and ensure it is top most in the Z-order CToolTipCtrl* pToolTipCtrl = m_ToolCtrl.GetToolTips(); pToolTipCtrl->SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } // Set the text in the main window. Doesn't appear to be an easy way to check if // we're not over any particular bit of the tool tip. AfxGetMainWnd()->SendMessage(WM_SETMESSAGESTRING, ButtonId); return; } TRACE1("COXSizeToolBar:No Tooltip prompt for ID=%d\n", ButtonId); return; } // Return information for bitmap indexes in the toolbar afx_msg void COXSizeToolBar::OnTBNGetButtonInfo(UINT nID, NMHDR* pNMHDR, LRESULT* pResult) { if (nID != TOOLCTRL_ID) return; TBNOTIFY * pTBN = (TBNOTIFY *)pNMHDR; int nIndex = pTBN->iItem; if (nIndex < m_nBitmapButtons) { *pResult = TRUE; UINT nButtonId = m_pBitmapIds[nIndex]; pTBN->tbButton.iBitmap = nIndex; pTBN->tbButton.idCommand = nButtonId; pTBN->tbButton.fsState = TBSTATE_ENABLED; pTBN->tbButton.fsStyle = TBSTYLE_BUTTON; pTBN->tbButton.iString = 0; if (pTBN->pszText != NULL) { CString strText; if (strText.LoadString(nButtonId)) { LPCTSTR pTipText = _tcschr(strText, _T('\n')); // tool tip is after "\n" in the string if (pTipText != NULL) { UTBStr::tcsncpy(pTBN->pszText, pTBN->cchText+1, pTipText + 1, pTBN->cchText); return; } } TRACE1("COXSizeToolBar:No Tooltip prompt for ID=%d\n", nButtonId); UTBStr::tcsncpy(pTBN->pszText, pTBN->cchText+1, _T("???"), pTBN->cchText); } } else *pResult = FALSE; } afx_msg void COXSizeToolBar::OnTBNBeginAdjust(UINT /* uID */, NMHDR* /* pNMHDR */, LRESULT* /* pResult */) { } afx_msg void COXSizeToolBar::OnTBNQueryInsert(UINT /* uID */, NMHDR* /* pNMHDR */, LRESULT* pResult) { *pResult = TRUE; // always allow buttons to be inserted } afx_msg void COXSizeToolBar::OnTBNQueryDelete(UINT /* uID */, NMHDR* /* pNMHDR */, LRESULT* pResult) { *pResult = TRUE; // always allow buttons to be deleted } afx_msg void COXSizeToolBar::OnTBNToolBarChange(UINT /* uID */, NMHDR* /* pNMHDR */, LRESULT* /* pResult */) { } void COXSizeToolBar::OnUpdateCmdUI(CFrameWnd* pTarget, BOOL bDisableIfNoHndler) { COXSizeControlBar::OnUpdateCmdUI(pTarget,bDisableIfNoHndler); COXToolCmdUI state; state.m_pOther = this; state.m_nIndexMax = m_ToolCtrl.GetButtonCount(); for (state.m_nIndex = 0; state.m_nIndex < state.m_nIndexMax; state.m_nIndex++) { TBBUTTON TB; m_ToolCtrl.GetButton(state.m_nIndex, &TB); if (!(TB.fsStyle & TBSTYLE_SEP)) { state.m_nID = TB.idCommand; state.DoUpdate(pTarget, bDisableIfNoHndler); } } // update any dialog controls added to the toolbar (probably unlikely in this case) UpdateDialogControls(pTarget, bDisableIfNoHndler); } CToolBarCtrl* COXSizeToolBar::GetToolBarCtrl() { return &m_ToolCtrl; }