513 lines
13 KiB
C++
513 lines
13 KiB
C++
/*
|
|
** Command & Conquer Renegade(tm)
|
|
** Copyright 2025 Electronic Arts Inc.
|
|
**
|
|
** This program is free software: you can redistribute it and/or modify
|
|
** it under the terms of the GNU General Public License as published by
|
|
** the Free Software Foundation, either version 3 of the License, or
|
|
** (at your option) any later version.
|
|
**
|
|
** This program is distributed in the hope that it will be useful,
|
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
** GNU General Public License for more details.
|
|
**
|
|
** You should have received a copy of the GNU General Public License
|
|
** along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Toolbar.CPP
|
|
//
|
|
// Implementation of a 'fancy' toolbar using hi-color buttons
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "Toolbar.H"
|
|
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CFancyToolbar, CControlBar)
|
|
//{{AFX_MSG_MAP(CFancyToolbar)
|
|
ON_WM_PAINT()
|
|
ON_WM_LBUTTONDOWN()
|
|
ON_WM_LBUTTONUP()
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// Local Constants
|
|
//
|
|
const TCHAR * const TOOLBAR_CLASS_NAME = TEXT ("FANCYTOOLBAR");
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// CFancyToolbar
|
|
//
|
|
CFancyToolbar::CFancyToolbar (void)
|
|
: m_iButtons (0),
|
|
m_iCurrentButton (-1)
|
|
{
|
|
// Ensure that the toolbar window class is registered
|
|
RegisterFancyToolbarClass ();
|
|
|
|
::memset (m_pButtonArray, 0, sizeof (m_pButtonArray));
|
|
return ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// ~CFancyToolbar
|
|
//
|
|
CFancyToolbar::~CFancyToolbar (void)
|
|
{
|
|
for (int iButton = 0; iButton < m_iButtons; iButton ++)
|
|
{
|
|
if (m_pButtonArray[iButton].hBMPUp)
|
|
{
|
|
// Free the BMP for this button
|
|
::DeleteObject (m_pButtonArray[iButton].hBMPUp);
|
|
m_pButtonArray[iButton].hBMPUp = NULL;
|
|
}
|
|
|
|
if (m_pButtonArray[iButton].hBMPDn)
|
|
{
|
|
// Free the BMP for this button
|
|
::DeleteObject (m_pButtonArray[iButton].hBMPDn);
|
|
m_pButtonArray[iButton].hBMPDn = NULL;
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// RegisterFancyToolbarClass
|
|
//
|
|
void
|
|
CFancyToolbar::RegisterFancyToolbarClass (void)
|
|
{
|
|
// Is this class already registered?
|
|
WNDCLASS classInfo = { 0 };
|
|
if (::GetClassInfo (::AfxGetInstanceHandle (),
|
|
TOOLBAR_CLASS_NAME,
|
|
&classInfo) != TRUE)
|
|
{
|
|
classInfo.style = CS_PARENTDC;
|
|
classInfo.lpfnWndProc = ::DefWindowProc;
|
|
classInfo.hInstance = ::AfxGetInstanceHandle ();
|
|
classInfo.hCursor = ::LoadCursor (NULL, IDC_ARROW);
|
|
classInfo.hbrBackground = (HBRUSH)COLOR_BTNFACE;
|
|
classInfo.lpszClassName = TOOLBAR_CLASS_NAME;
|
|
|
|
// Register the class with windows
|
|
::RegisterClass (&classInfo);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// Create
|
|
//
|
|
void
|
|
CFancyToolbar::OnPaint ()
|
|
{
|
|
Paint ();
|
|
return ;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// Create
|
|
//
|
|
BOOL
|
|
CFancyToolbar::Create
|
|
(
|
|
LPCTSTR pszWindowName,
|
|
CWnd *pCParentWnd,
|
|
UINT uiID
|
|
)
|
|
{
|
|
// Create the toolbar window using our own special window class
|
|
RECT rect = { 0 };
|
|
BOOL bReturn = CWnd::Create (TOOLBAR_CLASS_NAME, pszWindowName, WS_CHILD | WS_VISIBLE, rect, pCParentWnd, uiID);
|
|
|
|
// Were we successful?
|
|
ASSERT (bReturn);
|
|
if (bReturn)
|
|
{
|
|
// Set the bar style, but don't allow docking
|
|
SetBarStyle (GetBarStyle() | CBRS_TOOLTIPS | CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
|
|
EnableDocking (CBRS_ALIGN_ANY);
|
|
}
|
|
|
|
// Return the TRUE/FALSE result code
|
|
return bReturn;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// DrawButton
|
|
//
|
|
void
|
|
CFancyToolbar::DrawButton
|
|
(
|
|
HDC hDC,
|
|
int iXPos,
|
|
int iYPos,
|
|
HBITMAP hBMP
|
|
)
|
|
{
|
|
// Create a screen-compatible DC
|
|
HDC hMemDC = ::CreateCompatibleDC (hDC);
|
|
if (hMemDC)
|
|
{
|
|
// Select the BMP to paint into the DC
|
|
HBITMAP hOldBMP = (HBITMAP)::SelectObject (hMemDC, hBMP);
|
|
|
|
// Paint the BMP in its slot
|
|
::BitBlt (hDC, iXPos, iYPos, BUTTON_WIDTH, BUTTON_HEIGHT, hMemDC, 0, 0, SRCCOPY);
|
|
|
|
// Select the old BMP back into the DC and free the DC
|
|
::SelectObject (hMemDC, hOldBMP);
|
|
DeleteDC (hMemDC);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// AddButton
|
|
//
|
|
void
|
|
CFancyToolbar::AddButton
|
|
(
|
|
UINT iBMPUp,
|
|
UINT iBMPDn,
|
|
int iCommandID,
|
|
BUTTON_TYPE buttonType
|
|
)
|
|
{
|
|
// Increment the button count
|
|
int iButton = m_iButtons ++;
|
|
|
|
// Fill in the internal button structure for this entry
|
|
m_pButtonArray[iButton].hBMPUp = ::LoadBitmap (::AfxGetResourceHandle (), MAKEINTRESOURCE (iBMPUp));
|
|
m_pButtonArray[iButton].hBMPDn = ::LoadBitmap (::AfxGetResourceHandle (), MAKEINTRESOURCE (iBMPDn));
|
|
m_pButtonArray[iButton].iCommandID = iCommandID;
|
|
m_pButtonArray[iButton].buttonType = buttonType;
|
|
m_pButtonArray[iButton].currentState = StateUp;
|
|
m_pButtonArray[iButton].bVisible = TRUE;
|
|
return ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// Paint
|
|
//
|
|
void
|
|
CFancyToolbar::Paint (void)
|
|
{
|
|
// Get the window's DC
|
|
HDC hDC = ::GetDC (m_hWnd);
|
|
if (hDC)
|
|
{
|
|
// Get the bounding rectangle of the window
|
|
RECT rect;
|
|
::GetClientRect (m_hWnd, &rect);
|
|
|
|
// Paint the background light gray
|
|
HBRUSH hBrush = ::CreateSolidBrush (RGB (192, 192, 192));
|
|
::FillRect (hDC, &rect, hBrush);
|
|
::DeleteObject (hBrush);
|
|
|
|
// Loop through each button and paint it
|
|
int iXPos = BORDER_LEFT;
|
|
for (int iButton = 0; iButton < m_iButtons; iButton ++)
|
|
{
|
|
// Is this button visible?
|
|
if (m_pButtonArray[iButton].bVisible)
|
|
{
|
|
// Determine which BMP to use
|
|
HBITMAP hBMP = m_pButtonArray[iButton].hBMPUp;
|
|
if (m_pButtonArray[iButton].currentState == StateDn)
|
|
{
|
|
hBMP = m_pButtonArray[iButton].hBMPDn;
|
|
}
|
|
|
|
// Draw this button
|
|
DrawButton (hDC,
|
|
iXPos,
|
|
((rect.bottom-rect.top) >> 1) - BUTTON_MIDDLE,
|
|
hBMP);
|
|
|
|
// Increment the current x position
|
|
iXPos += BUTTON_WIDTH;
|
|
}
|
|
}
|
|
|
|
// Free the windows DC
|
|
::ReleaseDC (m_hWnd, hDC);
|
|
}
|
|
|
|
// Let the window know its done painting
|
|
::ValidateRect (m_hWnd, NULL);
|
|
return ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// ButtonFromPoint
|
|
//
|
|
int
|
|
CFancyToolbar::ButtonFromPoint (const CPoint &point)
|
|
{
|
|
int iIndex = -1;
|
|
|
|
// Loop through all the buttons until we've found the
|
|
// one we're looking for
|
|
int iXPos = BORDER_LEFT;
|
|
for (int iButton = 0; (iButton < m_iButtons) && (iIndex == -1); iButton ++)
|
|
{
|
|
// Is this the button we're looking for?
|
|
if (m_pButtonArray[iButton].bVisible &&
|
|
(point.x >= iXPos) && (point.x <= iXPos + BUTTON_WIDTH) &&
|
|
(point.y >= BORDER_TOP) && (point.y <= BORDER_TOP+BUTTON_HEIGHT))
|
|
{
|
|
// Yup, this is the button we're looking for
|
|
iIndex = iButton;
|
|
}
|
|
|
|
// Increment the current position
|
|
iXPos += BUTTON_WIDTH;
|
|
}
|
|
|
|
// Return the zero based index of the button we're looking for
|
|
return iIndex;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// HandleLButtonUp
|
|
//
|
|
void
|
|
CFancyToolbar::OnLButtonDown
|
|
(
|
|
UINT nFlags,
|
|
CPoint point
|
|
)
|
|
{
|
|
// Determine which button was clicked
|
|
int iButton = ButtonFromPoint (point);
|
|
if (iButton >= 0)
|
|
{
|
|
// Flip the button state
|
|
m_pButtonArray[iButton].currentState = (STATE_INFO)!m_pButtonArray[iButton].currentState;
|
|
|
|
// Determine which BMP to paint
|
|
HBITMAP hBMP = m_pButtonArray[iButton].hBMPUp;
|
|
if (m_pButtonArray[iButton].currentState == StateDn)
|
|
{
|
|
hBMP = m_pButtonArray[iButton].hBMPDn;
|
|
}
|
|
|
|
// Get the DC for the window
|
|
HDC hDC = ::GetDC (m_hWnd);
|
|
if (hDC)
|
|
{
|
|
// Get the bounding rectangle for the window
|
|
RECT rect;
|
|
::GetClientRect (m_hWnd, &rect);
|
|
|
|
// Draw the selected button
|
|
DrawButton (hDC,
|
|
BORDER_LEFT + iButton*BUTTON_WIDTH,
|
|
((rect.bottom-rect.top) >> 1) - BUTTON_MIDDLE,
|
|
hBMP);
|
|
|
|
// Release the window's DC
|
|
::ReleaseDC (m_hWnd, hDC);
|
|
}
|
|
|
|
// Is this a 'normal' or 2 state button?
|
|
if (m_pButtonArray[iButton].buttonType == TypeNormal)
|
|
{
|
|
// Normal button, so trap all mouse message so if the user
|
|
// lets up the mouse button outside the button rectangle we don't do anything
|
|
m_iCurrentButton = iButton;
|
|
::SetCapture (m_hWnd);
|
|
}
|
|
else
|
|
{
|
|
// 2 state button
|
|
m_iCurrentButton = -1;
|
|
|
|
// Send the message to the window's parent to let them know a command has occured
|
|
::AfxGetMainWnd ()->PostMessage (WM_COMMAND,
|
|
MAKELONG (m_pButtonArray[iButton].iCommandID, BN_CLICKED),
|
|
(LPARAM)m_hWnd);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CControlBar::OnLButtonDown (nFlags, point);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// HandleLButtonUp
|
|
//
|
|
void
|
|
CFancyToolbar::OnLButtonUp
|
|
(
|
|
UINT nFlags,
|
|
CPoint point
|
|
)
|
|
{
|
|
// Were we currently processing a button?
|
|
if (m_iCurrentButton >= 0)
|
|
{
|
|
// Which button were we processing?
|
|
int iButton = ButtonFromPoint (point);
|
|
if (iButton == m_iCurrentButton)
|
|
{
|
|
// Fire a command to the parent
|
|
::AfxGetMainWnd ()->PostMessage (WM_COMMAND,
|
|
MAKELONG (m_pButtonArray[iButton].iCommandID, BN_CLICKED),
|
|
(LPARAM)m_hWnd);
|
|
}
|
|
|
|
// Reset the button state
|
|
m_pButtonArray[m_iCurrentButton].currentState = StateUp;
|
|
|
|
// Get the window's DC
|
|
HDC hDC = ::GetDC (m_hWnd);
|
|
if (hDC)
|
|
{
|
|
// Get the bounding rectangle of the window
|
|
RECT rect;
|
|
::GetClientRect (m_hWnd, &rect);
|
|
|
|
// Paint the button
|
|
DrawButton (hDC,
|
|
BORDER_LEFT + m_iCurrentButton*BUTTON_WIDTH,
|
|
((rect.bottom-rect.top) >> 1) - BUTTON_MIDDLE,
|
|
m_pButtonArray[m_iCurrentButton].hBMPUp);
|
|
|
|
// Free the window's DC
|
|
::ReleaseDC (m_hWnd, hDC);
|
|
}
|
|
|
|
// Let go of the mouse capture
|
|
::ReleaseCapture ();
|
|
}
|
|
else
|
|
{
|
|
CControlBar::OnLButtonUp (nFlags, point);
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// OnDraw
|
|
//
|
|
void
|
|
CFancyToolbar::OnDraw (CDC* pDC)
|
|
{
|
|
return ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// PreCreateWindow
|
|
//
|
|
BOOL
|
|
CFancyToolbar::PreCreateWindow (CREATESTRUCT& cs)
|
|
{
|
|
// Allow the base class to process this message
|
|
return CControlBar::PreCreateWindow (cs);
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// SetButtonState
|
|
//
|
|
void
|
|
CFancyToolbar::SetButtonState
|
|
(
|
|
int iCommandID,
|
|
STATE_INFO newState,
|
|
BOOL bRepaint
|
|
)
|
|
{
|
|
BOOL bFound = FALSE;
|
|
|
|
// Loop through all the buttons until we've found the one we're looking for
|
|
for (int iButton = 0;
|
|
(iButton < m_iButtons) && (bFound == FALSE);
|
|
iButton ++)
|
|
{
|
|
if (m_pButtonArray[iButton].iCommandID == iCommandID)
|
|
{
|
|
// Set the new state
|
|
m_pButtonArray[iButton].currentState = newState;
|
|
|
|
if (bRepaint)
|
|
{
|
|
// Repaint the toolbar
|
|
//Paint ();
|
|
InvalidateRect (NULL);
|
|
UpdateWindow ();
|
|
}
|
|
|
|
// Found it!
|
|
bFound = TRUE;
|
|
}
|
|
}
|
|
|
|
return ;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
//
|
|
// GetButtonState
|
|
//
|
|
CFancyToolbar::STATE_INFO
|
|
CFancyToolbar::GetButtonState (int iCommandID) const
|
|
{
|
|
STATE_INFO stateInfo = StateUp;
|
|
|
|
// Loop through all the buttons until we've found the one we're looking for
|
|
BOOL bFound = FALSE;
|
|
for (int iButton = 0;
|
|
(iButton < m_iButtons) && (bFound == FALSE);
|
|
iButton ++)
|
|
{
|
|
if (m_pButtonArray[iButton].iCommandID == iCommandID)
|
|
{
|
|
// Return the current state to the caller
|
|
stateInfo = m_pButtonArray[iButton].currentState;
|
|
|
|
// Found it!
|
|
bFound = TRUE;
|
|
}
|
|
}
|
|
|
|
// Return the state of the requested button
|
|
return stateInfo;
|
|
}
|
|
|