Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

476 lines
14 KiB

/*--------------------------------------------------------------------------*
*
* 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);
}