2025-11-27 16:46:48 +09:00

543 lines
14 KiB
C++

// ========================================================================================
// 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 <afxpriv.h>
#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;
}