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