477 lines
14 KiB
C++
477 lines
14 KiB
C++
/*--------------------------------------------------------------------------*
|
||
*
|
||
* Microsoft Windows
|
||
* Copyright (C) Microsoft Corporation, 1992 - 1999
|
||
*
|
||
* File: mdiuisim.cpp
|
||
*
|
||
* Contents: Implementation file for CMDIMenuDecoration
|
||
*
|
||
* History: 17-Nov-97 jeffro Created
|
||
*
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
#include "stdafx.h"
|
||
#include "amc.h"
|
||
#include "mdiuisim.h"
|
||
|
||
|
||
struct MDIDataMap {
|
||
DWORD dwStyle;
|
||
int nCommand;
|
||
};
|
||
|
||
const int cMapEntries = 4;
|
||
|
||
const MDIDataMap anMDIDataMap[cMapEntries] = {
|
||
{ MMDS_CLOSE, SC_CLOSE }, // DFCS_CAPTIONCLOSE
|
||
{ MMDS_MINIMIZE, SC_MINIMIZE }, // DFCS_CAPTIONMIN
|
||
{ MMDS_MAXIMIZE, SC_MAXIMIZE }, // DFCS_CAPTIONMAX
|
||
{ MMDS_RESTORE, SC_RESTORE }, // DFCS_CAPTIONRESTORE
|
||
};
|
||
|
||
// this array is in the order the decorations are drawn, left-to-right
|
||
const int anDrawOrder[cMapEntries] = {
|
||
DFCS_CAPTIONMIN,
|
||
DFCS_CAPTIONRESTORE,
|
||
DFCS_CAPTIONMAX,
|
||
DFCS_CAPTIONCLOSE
|
||
};
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* DrawCaptionControl
|
||
*
|
||
*
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
static void DrawCaptionControl (
|
||
CDC * pdc,
|
||
LPCRECT pRect,
|
||
int nIndex,
|
||
bool fPushed)
|
||
{
|
||
const int cxInflate = -1;
|
||
const int cyInflate = -2;
|
||
CRect rectDraw = pRect;
|
||
|
||
rectDraw.InflateRect (cxInflate, cyInflate);
|
||
rectDraw.OffsetRect ((nIndex == DFCS_CAPTIONMIN) ? 1 : -1, 0);
|
||
|
||
if (fPushed)
|
||
nIndex |= DFCS_PUSHED;
|
||
|
||
pdc->DrawFrameControl (rectDraw, DFC_CAPTION, nIndex);
|
||
}
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::CMouseTrackContext::CMouseTrackContext
|
||
*
|
||
*
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
CMDIMenuDecoration::CMouseTrackContext::CMouseTrackContext (
|
||
CMDIMenuDecoration* pMenuDec,
|
||
CPoint point)
|
||
: m_fHotButtonPressed (false),
|
||
m_pMenuDec (pMenuDec)
|
||
{
|
||
ASSERT_VALID (m_pMenuDec);
|
||
|
||
// set up hit testing rectangles for each button
|
||
int cxButton = GetSystemMetrics (SM_CXMENUSIZE);
|
||
int cyButton = GetSystemMetrics (SM_CYMENUSIZE);
|
||
DWORD dwStyle = m_pMenuDec->GetStyle ();
|
||
|
||
CRect rectT (0, 0, cxButton, cyButton);
|
||
|
||
for (int i = 0; i < cMapEntries; i++)
|
||
{
|
||
int nDataIndex = anDrawOrder[i];
|
||
|
||
if (dwStyle & anMDIDataMap[nDataIndex].dwStyle)
|
||
{
|
||
m_rectButton[nDataIndex] = rectT;
|
||
rectT.OffsetRect (cxButton, 0);
|
||
}
|
||
else
|
||
m_rectButton[nDataIndex].SetRectEmpty();
|
||
}
|
||
|
||
m_nHotButton = HitTest (point);
|
||
ASSERT (m_nHotButton != -1);
|
||
|
||
// if the user clicked on a disbled button, we don't want to track -- punt!
|
||
if (!m_pMenuDec->IsSysCommandEnabled (anMDIDataMap[m_nHotButton].nCommand))
|
||
AfxThrowUserException ();
|
||
|
||
// press the hot button initially
|
||
ToggleHotButton ();
|
||
|
||
// capture the mouse
|
||
m_pMenuDec->SetCapture ();
|
||
}
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::CMouseTrackContext::~CMouseTrackContext
|
||
*
|
||
*
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
CMDIMenuDecoration::CMouseTrackContext::~CMouseTrackContext ()
|
||
{
|
||
ReleaseCapture();
|
||
|
||
if (m_fHotButtonPressed)
|
||
ToggleHotButton ();
|
||
}
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::CMouseTrackContext::Track
|
||
*
|
||
*
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
void CMDIMenuDecoration::CMouseTrackContext::Track (CPoint point)
|
||
{
|
||
int nButton = HitTest (point);
|
||
|
||
/*-----------------------------------------------------*/
|
||
/* if we're over the hot button and it's not pressed, */
|
||
/* or we're not over the hot button and it is pressed, */
|
||
/* toggle the state of the hot button */
|
||
/*-----------------------------------------------------*/
|
||
if (((nButton != m_nHotButton) && m_fHotButtonPressed) ||
|
||
((nButton == m_nHotButton) && !m_fHotButtonPressed))
|
||
{
|
||
ToggleHotButton ();
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::CMouseTrackContext::ToggleHotButton
|
||
*
|
||
*
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
void CMDIMenuDecoration::CMouseTrackContext::ToggleHotButton ()
|
||
{
|
||
DrawCaptionControl (&CClientDC (m_pMenuDec),
|
||
m_rectButton[m_nHotButton], m_nHotButton,
|
||
m_fHotButtonPressed = !m_fHotButtonPressed);
|
||
}
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::CMouseTrackContext::HitTest
|
||
*
|
||
*
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
int CMDIMenuDecoration::CMouseTrackContext::HitTest (CPoint point) const
|
||
{
|
||
for (int i = 0; i < countof (m_rectButton); i++)
|
||
{
|
||
if (m_rectButton[i].PtInRect (point))
|
||
return (i);
|
||
}
|
||
|
||
return (-1);
|
||
}
|
||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// CMDIMenuDecoration
|
||
|
||
CMDIMenuDecoration::CMDIMenuDecoration()
|
||
{
|
||
// anMDIDataMap is indexed by these values
|
||
ASSERT (DFCS_CAPTIONCLOSE == 0);
|
||
ASSERT (DFCS_CAPTIONMIN == 1);
|
||
ASSERT (DFCS_CAPTIONMAX == 2);
|
||
ASSERT (DFCS_CAPTIONRESTORE == 3);
|
||
}
|
||
|
||
CMDIMenuDecoration::~CMDIMenuDecoration()
|
||
{
|
||
}
|
||
|
||
|
||
BEGIN_MESSAGE_MAP(CMDIMenuDecoration, CWnd)
|
||
//{{AFX_MSG_MAP(CMDIMenuDecoration)
|
||
ON_WM_PAINT()
|
||
ON_WM_WINDOWPOSCHANGING()
|
||
ON_WM_LBUTTONDOWN()
|
||
ON_WM_LBUTTONUP()
|
||
ON_WM_MOUSEMOVE()
|
||
//}}AFX_MSG_MAP
|
||
END_MESSAGE_MAP()
|
||
|
||
|
||
/////////////////////////////////////////////////////////////////////////////
|
||
// CMDIMenuDecoration message handlers
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::OnPaint
|
||
*
|
||
* WM_PAINT handler for CMDIMenuDecoration.
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
void CMDIMenuDecoration::OnPaint()
|
||
{
|
||
CPaintDC dcPaint (this);
|
||
|
||
//#define DRAW_OFF_SCREEN
|
||
#ifndef DRAW_OFF_SCREEN
|
||
CDC& dc = dcPaint;
|
||
#else
|
||
CRect rect;
|
||
GetClientRect (rect);
|
||
const int cx = rect.Width();
|
||
const int cy = rect.Height();
|
||
|
||
CDC dcMem;
|
||
CDC& dc = dcMem;
|
||
dcMem.CreateCompatibleDC (&dcPaint);
|
||
|
||
CBitmap bmMem;
|
||
bmMem.CreateCompatibleBitmap (&dcPaint, cx, cy);
|
||
|
||
CBitmap* pbmOld = dcMem.SelectObject (&bmMem);
|
||
#endif
|
||
|
||
if (dcPaint.m_ps.fErase)
|
||
dc.FillRect (&dcPaint.m_ps.rcPaint, AMCGetSysColorBrush (COLOR_BTNFACE));
|
||
|
||
int cxButton = GetSystemMetrics (SM_CXMENUSIZE);
|
||
int cyButton = GetSystemMetrics (SM_CYMENUSIZE);
|
||
DWORD dwStyle = GetStyle ();
|
||
|
||
CRect rectDraw (0, 0, cxButton, cyButton);
|
||
|
||
// make sure we don't have both the maximize and restore styles
|
||
ASSERT ((dwStyle & (MMDS_MAXIMIZE | MMDS_RESTORE)) !=
|
||
(MMDS_MAXIMIZE | MMDS_RESTORE));
|
||
|
||
// we shouldn't get here if we're tracking
|
||
ASSERT (m_spTrackCtxt.get() == NULL);
|
||
|
||
CMenu* pSysMenu = GetActiveSystemMenu ();
|
||
|
||
for (int i = 0; i < cMapEntries; i++)
|
||
{
|
||
int nDataIndex = anDrawOrder[i];
|
||
|
||
if (dwStyle & anMDIDataMap[nDataIndex].dwStyle)
|
||
{
|
||
int nState = nDataIndex;
|
||
|
||
if (!IsSysCommandEnabled (anMDIDataMap[nDataIndex].nCommand, pSysMenu))
|
||
nState |= DFCS_INACTIVE;
|
||
|
||
DrawCaptionControl (&dc, rectDraw, nState, false);
|
||
rectDraw.OffsetRect (cxButton, 0);
|
||
}
|
||
}
|
||
|
||
#ifdef DRAW_OFF_SCREEN
|
||
dcPaint.BitBlt (0, 0, cx, cy, &dcMem, 0, 0, SRCCOPY);
|
||
dcMem.SelectObject (pbmOld);
|
||
#endif
|
||
}
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::OnWindowPosChanging
|
||
*
|
||
* WM_WINDOWPOSCHANGING handler for CMDIMenuDecoration.
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
void CMDIMenuDecoration::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
|
||
{
|
||
DWORD dwStyle = GetStyle ();
|
||
|
||
if (dwStyle & MMDS_AUTOSIZE)
|
||
{
|
||
int cxButton = GetSystemMetrics (SM_CXMENUSIZE);
|
||
int cyButton = GetSystemMetrics (SM_CYMENUSIZE);
|
||
|
||
lpwndpos->cx = 0;
|
||
lpwndpos->cy = cyButton;
|
||
|
||
dwStyle &= MMDS_BTNSTYLES;
|
||
|
||
while (dwStyle != 0)
|
||
{
|
||
if (dwStyle & 1)
|
||
lpwndpos->cx += cxButton;
|
||
|
||
dwStyle >>= 1;
|
||
}
|
||
}
|
||
|
||
else
|
||
CWnd::OnWindowPosChanging(lpwndpos);
|
||
}
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::OnLButtonDown
|
||
*
|
||
* WM_LBUTTONDOWN handler for CMDIMenuDecoration.
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
// this routine needs placement new syntax --
|
||
// temporarily remove MFC's incompatible placement new definition
|
||
#ifdef _DEBUG
|
||
#undef new
|
||
#endif
|
||
|
||
void CMDIMenuDecoration::OnLButtonDown(UINT nFlags, CPoint point)
|
||
{
|
||
typedef std::auto_ptr<char> CharPtr;
|
||
|
||
CWnd::OnLButtonDown(nFlags, point);
|
||
|
||
try
|
||
{
|
||
/*------------------------------------------------------------------*/
|
||
/* This looks ugly. We'd like to write: */
|
||
/* */
|
||
/* m_spTrackCtxt = CMouseTrackContextPtr ( */
|
||
/* new CMouseTrackContext (this, point)); */
|
||
/* */
|
||
/* but CMouseTrackContext's ctor might throw an exception. If it */
|
||
/* does, the smart pointer won't yet have been initialized so the */
|
||
/* CMouseTrackContext won't be deleted. */
|
||
/* */
|
||
/* To get around it, we'll create a smart pointer pointing to a */
|
||
/* dynamically-allocated buffer of the right size. That buffer */
|
||
/* will not leak with an exception. We can then use a placement */
|
||
/* new to initialize a CMouseTrackContext in the hunk of memory. */
|
||
/* It's now not a problem if the CMouseTrackContext throws, because */
|
||
/* the buffer is still protected it's own smart pointer. Once */
|
||
/* the placement new completes successfully, we can transfer */
|
||
/* ownership of the object to a CMouseTrackContext smart pointer */
|
||
/* and we're golden. */
|
||
/*------------------------------------------------------------------*/
|
||
|
||
// allocate a hunk of memory and construct a CMouseTrackContext in it
|
||
CharPtr spchBuffer = CharPtr (new char[sizeof (CMouseTrackContext)]);
|
||
CMouseTrackContext* pNewCtxt = new (spchBuffer.get()) CMouseTrackContext (this, point);
|
||
|
||
// if we get here, the CMouseTrackContext initialized properly,
|
||
// so we can transfer ownership to the CMouseTrackContext smart pointer
|
||
spchBuffer.release ();
|
||
m_spTrackCtxt = CMouseTrackContextPtr (pNewCtxt);
|
||
}
|
||
catch (CUserException* pe)
|
||
{
|
||
// do nothing, just eat the exception
|
||
pe->Delete();
|
||
}
|
||
}
|
||
|
||
#ifdef _DEBUG
|
||
#define new DEBUG_NEW
|
||
#endif
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::OnLButtonUp
|
||
*
|
||
* WM_LBUTTONUP handler for CMDIMenuDecoration.
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
void CMDIMenuDecoration::OnLButtonUp(UINT nFlags, CPoint point)
|
||
{
|
||
if (m_spTrackCtxt.get() != NULL)
|
||
{
|
||
const int nHotButton = m_spTrackCtxt->m_nHotButton;
|
||
const int nHitButton = m_spTrackCtxt->HitTest (point);
|
||
|
||
// delete the track context
|
||
m_spTrackCtxt = CMouseTrackContextPtr (NULL);
|
||
|
||
if (nHitButton == nHotButton)
|
||
{
|
||
int cmd = anMDIDataMap[nHotButton].nCommand;
|
||
|
||
// make sure the command looks like a valid sys command
|
||
ASSERT (cmd >= 0xF000);
|
||
|
||
ClientToScreen (&point);
|
||
GetOwner()->SendMessage (WM_SYSCOMMAND, cmd,
|
||
MAKELPARAM (point.x, point.y));
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::OnMouseMove
|
||
*
|
||
* WM_MOUSEMOVE handler for CMDIMenuDecoration.
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
void CMDIMenuDecoration::OnMouseMove(UINT nFlags, CPoint point)
|
||
{
|
||
if (m_spTrackCtxt.get() != NULL)
|
||
m_spTrackCtxt->Track (point);
|
||
}
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::GetActiveSystemMenu
|
||
*
|
||
*
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
CMenu* CMDIMenuDecoration::GetActiveSystemMenu ()
|
||
{
|
||
CFrameWnd* pwndFrame = GetParentFrame()->GetActiveFrame();
|
||
ASSERT (pwndFrame != NULL);
|
||
|
||
CMenu* pSysMenu = pwndFrame->GetSystemMenu (FALSE);
|
||
ASSERT (pSysMenu != NULL);
|
||
|
||
return (pSysMenu);
|
||
}
|
||
|
||
|
||
|
||
/*--------------------------------------------------------------------------*
|
||
* CMDIMenuDecoration::IsSysCommandEnabled
|
||
*
|
||
*
|
||
*--------------------------------------------------------------------------*/
|
||
|
||
bool CMDIMenuDecoration::IsSysCommandEnabled (int nSysCommand, CMenu* pSysMenu)
|
||
{
|
||
if (pSysMenu == NULL)
|
||
pSysMenu = GetActiveSystemMenu ();
|
||
|
||
int nState = pSysMenu->GetMenuState (nSysCommand, MF_BYCOMMAND);
|
||
ASSERT (nState != 0xFFFFFFFF);
|
||
|
||
return ((nState & MF_GRAYED) == 0);
|
||
}
|