// Author: Lucian Barbulescu lucian@xts.ro
//
// Disclaimer
// ----------
// THIS SOFTWARE AND THE ACCOMPANYING FILES ARE DISTRIBUTED "AS IS" AND WITHOUT
// ANY WARRANTIES WHETHER EXPRESSED OR IMPLIED. NO REPONSIBILITIES FOR POSSIBLE
// DAMAGES OR EVEN FUNCTIONALITY CAN BE TAKEN. THE USER MUST ASSUME THE ENTIRE
// RISK OF USING THIS SOFTWARE.
//
// Terms of use
// ------------
// THIS SOFTWARE IS FREE FOR PERSONAL USE OR FREEWARE APPLICATIONS.
// IF YOU USE THIS SOFTWARE IN COMMERCIAL OR SHAREWARE APPLICATIONS YOU
// MUST HAVE THE PERMISION OF THE AUTHOR.
//
//
// History
// -------
//
// v1.0.0 - 7 April 2004 - First release
//
// v1.0.1 - 8 April 2004 - FIXED BUG: when toolbar is attached to the right or
// the left no dragging possible by clicking on the handle
// (thanks to ReorX for informing me about this bug)
//
// - FIXED BUG: when the floating toolbar is docked the
// parent dialog does not receives the focus
//
// v1.0.2 - 9 April 2004 - ADDED: VC6 compatibility
// - ADDED: Tooltips support
// (thanks to Randy More for his article about adding
// tooltips to toolbars in dialogs)
// - FIXED BUG: UPDATE_COMMAND_UI does not work
// (thanks to =[ Abin ]= for informing me about this bug)
#include "stdafx.h"
#include <afxpriv.h>
#include "ToolbarDialog.h"
// CSmartToolbar
IMPLEMENT_DYNAMIC(CSmartToolbar, CToolBar)
CSmartToolbar::CSmartToolbar()
{
m_bState = TS_HIDDEN;
m_bNoMove = false;
}
CSmartToolbar::~CSmartToolbar()
{
}
BEGIN_MESSAGE_MAP(CSmartToolbar, CToolBar)
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_MOUSEMOVE()
ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
END_MESSAGE_MAP()
// CSmartToolbar message handlers
LRESULT CSmartToolbar::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM)
{
if (IsWindowVisible())
{
//CFrameWnd *pParent = (CFrameWnd *)GetParent();
CFrameWnd *pParent = (CFrameWnd *)m_pMessageReceiver;
if (pParent)
OnUpdateCmdUI(pParent, (BOOL)wParam);
}
return 0L;
}
void CSmartToolbar::OnLButtonDown(UINT nFlags, CPoint point)
{
if(m_bState & TS_FIXED)
{
int ht = GetToolBarCtrl().HitTest(&point);
if((ht < 0)||(ht >= GetToolBarCtrl().GetButtonCount()))
{
SetCapture();
CPoint newPoint = point;
this->MapWindowPoints(m_pMessageReceiver,&newPoint,1);
m_pMessageReceiver->SendMessage(TDM_BUTTONDOWN,WPARAM(0),LPARAM(&newPoint));
}
else
m_bNoMove = true;
}
CToolBar::OnLButtonDown(nFlags, point);
}
void CSmartToolbar::OnLButtonUp(UINT nFlags, CPoint point)
{
if(m_bState & TS_FIXED)
{
if(!m_bNoMove)
{
ReleaseCapture();
CPoint newPoint = point;
this->MapWindowPoints(m_pMessageReceiver,&newPoint,1);
m_pMessageReceiver->SendMessage(TDM_BUTTONUP,WPARAM(0),LPARAM(&newPoint));
}
else
m_bNoMove = false;
}
CToolBar::OnLButtonUp(nFlags, point);
}
void CSmartToolbar::OnMouseMove(UINT nFlags, CPoint point)
{
if((m_bState & TS_FIXED) && !m_bNoMove && (nFlags & MK_LBUTTON))
{
CPoint newPoint = point;
this->MapWindowPoints(m_pMessageReceiver,&newPoint,1);
m_pMessageReceiver->SendMessage(TDM_MOUSEMOVE,WPARAM(0),LPARAM(&newPoint));
}
CToolBar::OnMouseMove(nFlags, point);
}
void CSmartToolbar::Initialize(CWnd* pParent, UINT uTemplateID)
{
DWORD style = WS_CHILD|WS_VISIBLE|CBRS_FLYBY|CBRS_GRIPPER|CBRS_SIZE_DYNAMIC|CBRS_TOOLTIPS;
if(m_bState & TS_FIXED)
style|=(m_bState&TS_FIXED);
else
style|= TS_TOP;
CreateEx(pParent,TBSTYLE_FLAT,style);
LoadToolBar(uTemplateID);
}
void CSmartToolbar::UpdateToolbarStyle(DWORD dwStyle)
{
m_bState = dwStyle;
if(dwStyle == TS_HIDDEN)
{
//hide the toolbar and return
ModifyStyle(WS_VISIBLE,0);
return;
}
else
{
//ensure the toolbar is visible
ModifyStyle(0,WS_VISIBLE);
}
DWORD dwCurrentStyle = GetBarStyle();
dwCurrentStyle &= ~(CBRS_ALIGN_ANY);
dwCurrentStyle &= ~(CBRS_BORDER_ANY);
//now decide where to place the toolbar
if(dwStyle == TS_FLOATING){
SetBarStyle((dwCurrentStyle & ~CBRS_GRIPPER) | TS_TOP);
}
else{
SetBarStyle(dwCurrentStyle | dwStyle | CBRS_GRIPPER);
}
}
// CFloatToolbarDlg
IMPLEMENT_DYNAMIC(CFloatToolbarDlg, CWnd)
CFloatToolbarDlg::CFloatToolbarDlg(CSmartToolbar* pToolbar)
{
m_pToolbar = pToolbar;
m_bMoving = false;
}
CFloatToolbarDlg::~CFloatToolbarDlg()
{
}
BEGIN_MESSAGE_MAP(CFloatToolbarDlg, CWnd)
ON_WM_CLOSE()
ON_WM_LBUTTONUP()
ON_WM_SETCURSOR()
ON_WM_MOUSEMOVE()
ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
END_MESSAGE_MAP()
// CFloatToolbarDlg message handlers
BOOL CFloatToolbarDlg::ShowWindow(int nCmdShow)
{
RepositionBars(AFX_IDW_CONTROLBAR_FIRST,AFX_IDW_CONTROLBAR_LAST,0);
return CWnd::ShowWindow(nCmdShow);
}
void CFloatToolbarDlg::CreateToolDlg(CWnd* pParent)
{
BOOL bSuccess;
// Register window class
CString csClassName = AfxRegisterWndClass(CS_OWNDC|CS_HREDRAW|CS_VREDRAW,
::LoadCursor(NULL, IDC_APPSTARTING),
CBrush(::GetSysColor(COLOR_BTNFACE)));
SIZE sz;
m_pToolbar->GetToolBarCtrl().GetMaxSize(&sz);
// Create popup window
bSuccess = CreateEx(WS_EX_TOOLWINDOW, // Extended style
csClassName, // Classname
_T("Toolbar"), // Title
WS_POPUP | WS_CAPTION | WS_SYSMENU , // style
0,0,
sz.cx+6,100,
pParent->GetSafeHwnd(), // handle to parent
0, // No menu
NULL);
CRect rWnd, rClient;
GetWindowRect(rWnd);
GetClientRect(rClient);
rWnd.bottom = rWnd.top + rWnd.Height() - rClient.Height() + sz.cy;
MoveWindow(rWnd);
CenterWindow();
}
void CFloatToolbarDlg::OnClose()
{
GetParent()->SendMessage(TDM_FLOATCLOSE,WPARAM(0),LPARAM(0));
}
LRESULT CFloatToolbarDlg::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM lParam)
{
SendMessageToDescendants(WM_IDLEUPDATECMDUI,
wParam, lParam, TRUE, TRUE);
return 0;
}
BOOL CFloatToolbarDlg::OnToolTipText(UINT nID, NMHDR* pNMHDR, LRESULT* pResult)
{
return (BOOL)(GetParent()->SendMessage(WM_NOTIFY,(WPARAM)nID,(LPARAM)pNMHDR));
}
void CFloatToolbarDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
if(m_bMoving)
{
m_bMoving = false;
ReleaseCapture();
CPoint newPoint = point;
this->MapWindowPoints(GetParent(),&newPoint,1);
GetParent()->SendMessage(TDM_BUTTONUP,WPARAM(0),LPARAM(&newPoint));
}
CWnd::OnLButtonUp(nFlags, point);
}
BOOL CFloatToolbarDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if(message == WM_LBUTTONDOWN)
{
if(nHitTest == HTCAPTION)
{
m_bMoving = true;
SetCapture();
ShowWindow(SW_HIDE);
CPoint pt;
GetCursorPos(&pt);
GetParent()->ScreenToClient(&pt);
GetParent()->SendMessage(TDM_BUTTONDOWN,WPARAM(0),LPARAM(&pt));
}
}
return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
void CFloatToolbarDlg::OnMouseMove(UINT nFlags, CPoint point)
{
if(nFlags & MK_LBUTTON)
{
if(m_bMoving)
{
CPoint newPoint = point;
this->MapWindowPoints(GetParent(),&newPoint,1);
GetParent()->SendMessage(TDM_MOUSEMOVE,WPARAM(0),LPARAM(&newPoint));
}
}
CWnd::OnMouseMove(nFlags, point);
}
BOOL CFloatToolbarDlg::OnCommand(WPARAM wParam, LPARAM lParam)
{
HWND hWnd = m_pToolbar->m_hWnd;
if((LPARAM)hWnd == lParam)
{
GetParent()->SendMessage(TDM_TOOLBTNPRESS,wParam,LPARAM(0));
}
return CWnd::OnCommand(wParam, lParam);
}
// CToolbarDialog dialog
IMPLEMENT_DYNAMIC(CToolbarDialog, CDialog)
CToolbarDialog::CToolbarDialog(UINT uID, CWnd* pParent, UINT uToolbarID, DWORD dwToolbarState)
: CDialog(uID, pParent),m_ptOffset(0,0),m_ptDialogOffset(0,0),
m_rcOldToolRect(0,0,0,0)
{
m_pToolbar = new CSmartToolbar();
m_pFloatToolbar = new CFloatToolbarDlg(m_pToolbar);
m_pToolbar->SetTolbarState(dwToolbarState);
m_uToolbarID = uToolbarID;
m_dwToolbarState = dwToolbarState;
WORD HatchBits[8] = { 0x11, 0x22, 0x44, 0x88, 0x11,
0x22, 0x44, 0x88 };
// Use the bit pattern to create a bitmap.
CBitmap bm;
bm.CreateBitmap(8,8,1,1, HatchBits);
m_brBrush.CreatePatternBrush(&bm);
}
CToolbarDialog::~CToolbarDialog()
{
delete m_pFloatToolbar;
delete m_pToolbar;
}
void CToolbarDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CToolbarDialog, CDialog)
ON_WM_SIZE()
ON_MESSAGE(TDM_BUTTONDOWN,OnToolbarLButtonDown)
ON_MESSAGE(TDM_BUTTONUP,OnToolbarLButtonUp)
ON_MESSAGE(TDM_MOUSEMOVE,OnToolbarMouseMove)
ON_MESSAGE(TDM_FLOATCLOSE,OnFloatToolbarClose)
ON_MESSAGE(TDM_TOOLBTNPRESS,OnToolbarButtonPressed)
ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
END_MESSAGE_MAP()
// CToolbarDialog message handlers
BOOL CToolbarDialog::OnInitDialog()
{
CDialog::OnInitDialog();
m_pToolbar->Initialize(this, m_uToolbarID);
m_pToolbar->SetMessageReceiver(this);
m_pFloatToolbar->CreateToolDlg(this);
//initialy there is no toolbar
//so I have to "lie" the PositionToolbar function
//I set the "current state" to TS_HIDDEN
DWORD dwState = m_dwToolbarState;
m_dwToolbarState = TS_HIDDEN;
PositionToolbar(dwState);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CToolbarDialog::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
}
LRESULT CToolbarDialog::OnKickIdle(WPARAM, LPARAM)
{
if(m_pToolbar->GetParent() != this)
AfxCallWndProc(m_pFloatToolbar,m_pFloatToolbar->GetSafeHwnd(),WM_IDLEUPDATECMDUI,(WPARAM)FALSE, 0);
else
SendMessageToDescendants(WM_IDLEUPDATECMDUI,(WPARAM)FALSE, 0, TRUE, TRUE);
return 0;
}
void CToolbarDialog::PositionToolbar(DWORD dwPosition)
{
//ensure the floating toolbar is hidden if this is required
if((dwPosition != TS_FLOATING) && (m_pToolbar->GetParent() == m_pFloatToolbar))
{
m_pFloatToolbar->ShowWindow(SW_HIDE);
m_pToolbar->SetParent(this);
}
m_pToolbar->UpdateToolbarStyle(dwPosition);
//based on the new style of the toolbar several actions have
//to be performed
CPoint ptOffset(0,0);
CPoint ptDialogOffset(0,0);
if(dwPosition & TS_FIXED)
{
//the offset of the controls and the new size of the dialog
//have to be computed
CRect rcClientStart;
CRect rcClientNow;
GetClientRect(rcClientStart);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST,
0, reposQuery, rcClientNow);
//ptOffset.SetPoint(rcClientNow.left - rcClientStart.left,
// rcClientNow.top - rcClientStart.top);
ptOffset.x = rcClientNow.left - rcClientStart.left;
ptOffset.y = rcClientNow.top - rcClientStart.top;
//ptDialogOffset.SetPoint(rcClientStart.Width() - rcClientNow.Width(),
// rcClientStart.Height() - rcClientNow.Height());
ptDialogOffset.x = rcClientStart.Width() - rcClientNow.Width();
ptDialogOffset.y = rcClientStart.Height() - rcClientNow.Height();
}
// We need to resize the dialog to make room for control bars.
// First, figure out how big the control bars are.
//now we have to move the controls, but only if this is realy neaded
if((ptOffset - m_ptOffset) != CSize(0,0))
{
CRect rcChild;
CWnd* pwndChild = GetWindow(GW_CHILD);
while (pwndChild)
{
pwndChild->GetWindowRect(rcChild);
ScreenToClient(rcChild);
rcChild.OffsetRect(ptOffset - m_ptOffset);
pwndChild->MoveWindow(rcChild, TRUE);
pwndChild = pwndChild->GetNextWindow();
}
}
// Adjust the dialog window dimensions if this is realy neaded
if((ptDialogOffset - m_ptDialogOffset) != CSize(0,0))
{
CRect rcWindow;
GetWindowRect(rcWindow);
rcWindow.right += ptDialogOffset.x - m_ptDialogOffset.x;
rcWindow.bottom += ptDialogOffset.y - m_ptDialogOffset.y;
MoveWindow(rcWindow, TRUE);
RedrawWindow();
}
//if the new style is TS_FLOATING, set the new parent of the toolbar
if(dwPosition == TS_FLOATING)
{
m_pToolbar->SetParent(m_pFloatToolbar);
m_pFloatToolbar->ShowWindow(SW_SHOW);
}
m_ptDialogOffset = ptDialogOffset;
m_ptOffset = ptOffset;
// And position the control bars
RepositionBars(AFX_IDW_CONTROLBAR_FIRST, AFX_IDW_CONTROLBAR_LAST, 0);
}
LONG CToolbarDialog::OnToolbarLButtonDown(WPARAM wParam, LPARAM lParam)
{
CPoint* pPoint = (CPoint*)lParam;
CRect rcToolRect;
ComputeToolbarRect(*pPoint,&rcToolRect);
DrawToolRect(rcToolRect);
return 0;
}
LONG CToolbarDialog::OnToolbarMouseMove(WPARAM wParam, LPARAM lParam)
{
CPoint* pPoint = (CPoint*)lParam;
CRect rcToolRect;
//TRACE2("x=%d, y=%d \n",pPoint->x, pPoint->y);
ComputeToolbarRect(*pPoint,&rcToolRect);
DrawToolRect(rcToolRect);
return 0;
}
LONG CToolbarDialog::OnFloatToolbarClose(WPARAM wParam, LPARAM lParam)
{
m_dwToolbarState = TS_HIDDEN;
PositionToolbar(m_dwToolbarState);
return 0;
}
LONG CToolbarDialog::OnToolbarButtonPressed(WPARAM wParam, LPARAM lParam)
{
ToolbarButtonCommand(UINT(wParam));
return 0;
}
LONG CToolbarDialog::OnToolbarLButtonUp(WPARAM wParam, LPARAM lParam)
{
PositionToolbar(m_dwToolbarState);
if(m_dwToolbarState == TS_FLOATING)
{
CRect rcWnd = m_rcOldToolRect;
ClientToScreen(rcWnd);
m_pFloatToolbar->MoveWindow(rcWnd);
}
else
SetFocus();
DrawToolRect(CRect(0,0,0,0));
return 0;
}
void CToolbarDialog::ComputeToolbarRect(CPoint ptPoint, CRect* pToolRect)
{
SIZE sz;
m_pToolbar->GetToolBarCtrl().GetMaxSize(&sz);
long min, max;
min = sz.cx > sz.cy ? sz.cy : sz.cx;
max = sz.cx > sz.cy ? sz.cx : sz.cy;
pToolRect->SetRect(0,0,0,0);
CRect chRect;
GetClientRect(chRect);
if(ptPoint.y <= (chRect.top + 30))
{
//the rectangle is a horisontal one
pToolRect->right = max;
pToolRect->bottom = min;
m_dwToolbarState = TS_TOP;
}
else if(ptPoint.y >= (chRect.bottom - 30))
{
//the rectangle is a horisontal one
pToolRect->right = max;
pToolRect->bottom = min;
m_dwToolbarState = TS_BOTTOM;
}
else if(ptPoint.x <= (chRect.left + 30))
{
//the rectangle is a vertical one
pToolRect->right = min;
pToolRect->bottom = max;
m_dwToolbarState = TS_LEFT;
}
else if(ptPoint.x >= (chRect.right - 30))
{
//the rectangle is a vertical one
pToolRect->right = min;
pToolRect->bottom = max;
m_dwToolbarState = TS_RIGHT;
}
else
{
CRect rcFloatRect;
m_pFloatToolbar->GetWindowRect(rcFloatRect);
pToolRect->right = rcFloatRect.Width();
pToolRect->bottom = rcFloatRect.Height();
m_dwToolbarState = TS_FLOATING;
}
pToolRect->OffsetRect(ptPoint);
}
void CToolbarDialog::DrawToolRect(CRect rcToolRect)
{
//erase the old rectangle
RedrawWindow(m_rcOldToolRect);
//if the new Tool Rect is 0,0,0,0, do not draw it
if(rcToolRect != CRect(0,0,0,0))
{
CDC* pDC = GetDC();
pDC->FrameRect(rcToolRect,&m_brBrush);
ReleaseDC(pDC);
}
m_rcOldToolRect = rcToolRect;
}
BOOL CToolbarDialog::OnCommand(WPARAM wParam, LPARAM lParam)
{
HWND hWnd = m_pToolbar->m_hWnd;
if((LPARAM)hWnd == lParam)
{
OnToolbarButtonPressed(wParam,LPARAM(0));
}
return CDialog::OnCommand(wParam, lParam);
}
//this function must be implemented in your class
LONG CToolbarDialog::ToolbarButtonCommand(UINT uButtonID)
{
ASSERT(FALSE);
return 0;
}