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

  1. /*--------------------------------------------------------------------------*
  2. *
  3. * Microsoft Windows
  4. * Copyright (C) Microsoft Corporation, 1992 - 1999
  5. *
  6. * File: mdiuisim.cpp
  7. *
  8. * Contents: Implementation file for CMDIMenuDecoration
  9. *
  10. * History: 17-Nov-97 jeffro Created
  11. *
  12. *--------------------------------------------------------------------------*/
  13. #include "stdafx.h"
  14. #include "amc.h"
  15. #include "mdiuisim.h"
  16. struct MDIDataMap {
  17. DWORD dwStyle;
  18. int nCommand;
  19. };
  20. const int cMapEntries = 4;
  21. const MDIDataMap anMDIDataMap[cMapEntries] = {
  22. { MMDS_CLOSE, SC_CLOSE }, // DFCS_CAPTIONCLOSE
  23. { MMDS_MINIMIZE, SC_MINIMIZE }, // DFCS_CAPTIONMIN
  24. { MMDS_MAXIMIZE, SC_MAXIMIZE }, // DFCS_CAPTIONMAX
  25. { MMDS_RESTORE, SC_RESTORE }, // DFCS_CAPTIONRESTORE
  26. };
  27. // this array is in the order the decorations are drawn, left-to-right
  28. const int anDrawOrder[cMapEntries] = {
  29. DFCS_CAPTIONMIN,
  30. DFCS_CAPTIONRESTORE,
  31. DFCS_CAPTIONMAX,
  32. DFCS_CAPTIONCLOSE
  33. };
  34. /*--------------------------------------------------------------------------*
  35. * DrawCaptionControl
  36. *
  37. *
  38. *--------------------------------------------------------------------------*/
  39. static void DrawCaptionControl (
  40. CDC * pdc,
  41. LPCRECT pRect,
  42. int nIndex,
  43. bool fPushed)
  44. {
  45. const int cxInflate = -1;
  46. const int cyInflate = -2;
  47. CRect rectDraw = pRect;
  48. rectDraw.InflateRect (cxInflate, cyInflate);
  49. rectDraw.OffsetRect ((nIndex == DFCS_CAPTIONMIN) ? 1 : -1, 0);
  50. if (fPushed)
  51. nIndex |= DFCS_PUSHED;
  52. pdc->DrawFrameControl (rectDraw, DFC_CAPTION, nIndex);
  53. }
  54. /*--------------------------------------------------------------------------*
  55. * CMDIMenuDecoration::CMouseTrackContext::CMouseTrackContext
  56. *
  57. *
  58. *--------------------------------------------------------------------------*/
  59. CMDIMenuDecoration::CMouseTrackContext::CMouseTrackContext (
  60. CMDIMenuDecoration* pMenuDec,
  61. CPoint point)
  62. : m_fHotButtonPressed (false),
  63. m_pMenuDec (pMenuDec)
  64. {
  65. ASSERT_VALID (m_pMenuDec);
  66. // set up hit testing rectangles for each button
  67. int cxButton = GetSystemMetrics (SM_CXMENUSIZE);
  68. int cyButton = GetSystemMetrics (SM_CYMENUSIZE);
  69. DWORD dwStyle = m_pMenuDec->GetStyle ();
  70. CRect rectT (0, 0, cxButton, cyButton);
  71. for (int i = 0; i < cMapEntries; i++)
  72. {
  73. int nDataIndex = anDrawOrder[i];
  74. if (dwStyle & anMDIDataMap[nDataIndex].dwStyle)
  75. {
  76. m_rectButton[nDataIndex] = rectT;
  77. rectT.OffsetRect (cxButton, 0);
  78. }
  79. else
  80. m_rectButton[nDataIndex].SetRectEmpty();
  81. }
  82. m_nHotButton = HitTest (point);
  83. ASSERT (m_nHotButton != -1);
  84. // if the user clicked on a disbled button, we don't want to track -- punt!
  85. if (!m_pMenuDec->IsSysCommandEnabled (anMDIDataMap[m_nHotButton].nCommand))
  86. AfxThrowUserException ();
  87. // press the hot button initially
  88. ToggleHotButton ();
  89. // capture the mouse
  90. m_pMenuDec->SetCapture ();
  91. }
  92. /*--------------------------------------------------------------------------*
  93. * CMDIMenuDecoration::CMouseTrackContext::~CMouseTrackContext
  94. *
  95. *
  96. *--------------------------------------------------------------------------*/
  97. CMDIMenuDecoration::CMouseTrackContext::~CMouseTrackContext ()
  98. {
  99. ReleaseCapture();
  100. if (m_fHotButtonPressed)
  101. ToggleHotButton ();
  102. }
  103. /*--------------------------------------------------------------------------*
  104. * CMDIMenuDecoration::CMouseTrackContext::Track
  105. *
  106. *
  107. *--------------------------------------------------------------------------*/
  108. void CMDIMenuDecoration::CMouseTrackContext::Track (CPoint point)
  109. {
  110. int nButton = HitTest (point);
  111. /*-----------------------------------------------------*/
  112. /* if we're over the hot button and it's not pressed, */
  113. /* or we're not over the hot button and it is pressed, */
  114. /* toggle the state of the hot button */
  115. /*-----------------------------------------------------*/
  116. if (((nButton != m_nHotButton) && m_fHotButtonPressed) ||
  117. ((nButton == m_nHotButton) && !m_fHotButtonPressed))
  118. {
  119. ToggleHotButton ();
  120. }
  121. }
  122. /*--------------------------------------------------------------------------*
  123. * CMDIMenuDecoration::CMouseTrackContext::ToggleHotButton
  124. *
  125. *
  126. *--------------------------------------------------------------------------*/
  127. void CMDIMenuDecoration::CMouseTrackContext::ToggleHotButton ()
  128. {
  129. DrawCaptionControl (&CClientDC (m_pMenuDec),
  130. m_rectButton[m_nHotButton], m_nHotButton,
  131. m_fHotButtonPressed = !m_fHotButtonPressed);
  132. }
  133. /*--------------------------------------------------------------------------*
  134. * CMDIMenuDecoration::CMouseTrackContext::HitTest
  135. *
  136. *
  137. *--------------------------------------------------------------------------*/
  138. int CMDIMenuDecoration::CMouseTrackContext::HitTest (CPoint point) const
  139. {
  140. for (int i = 0; i < countof (m_rectButton); i++)
  141. {
  142. if (m_rectButton[i].PtInRect (point))
  143. return (i);
  144. }
  145. return (-1);
  146. }
  147. /////////////////////////////////////////////////////////////////////////////
  148. // CMDIMenuDecoration
  149. CMDIMenuDecoration::CMDIMenuDecoration()
  150. {
  151. // anMDIDataMap is indexed by these values
  152. ASSERT (DFCS_CAPTIONCLOSE == 0);
  153. ASSERT (DFCS_CAPTIONMIN == 1);
  154. ASSERT (DFCS_CAPTIONMAX == 2);
  155. ASSERT (DFCS_CAPTIONRESTORE == 3);
  156. }
  157. CMDIMenuDecoration::~CMDIMenuDecoration()
  158. {
  159. }
  160. BEGIN_MESSAGE_MAP(CMDIMenuDecoration, CWnd)
  161. //{{AFX_MSG_MAP(CMDIMenuDecoration)
  162. ON_WM_PAINT()
  163. ON_WM_WINDOWPOSCHANGING()
  164. ON_WM_LBUTTONDOWN()
  165. ON_WM_LBUTTONUP()
  166. ON_WM_MOUSEMOVE()
  167. //}}AFX_MSG_MAP
  168. END_MESSAGE_MAP()
  169. /////////////////////////////////////////////////////////////////////////////
  170. // CMDIMenuDecoration message handlers
  171. /*--------------------------------------------------------------------------*
  172. * CMDIMenuDecoration::OnPaint
  173. *
  174. * WM_PAINT handler for CMDIMenuDecoration.
  175. *--------------------------------------------------------------------------*/
  176. void CMDIMenuDecoration::OnPaint()
  177. {
  178. CPaintDC dcPaint (this);
  179. //#define DRAW_OFF_SCREEN
  180. #ifndef DRAW_OFF_SCREEN
  181. CDC& dc = dcPaint;
  182. #else
  183. CRect rect;
  184. GetClientRect (rect);
  185. const int cx = rect.Width();
  186. const int cy = rect.Height();
  187. CDC dcMem;
  188. CDC& dc = dcMem;
  189. dcMem.CreateCompatibleDC (&dcPaint);
  190. CBitmap bmMem;
  191. bmMem.CreateCompatibleBitmap (&dcPaint, cx, cy);
  192. CBitmap* pbmOld = dcMem.SelectObject (&bmMem);
  193. #endif
  194. if (dcPaint.m_ps.fErase)
  195. dc.FillRect (&dcPaint.m_ps.rcPaint, AMCGetSysColorBrush (COLOR_BTNFACE));
  196. int cxButton = GetSystemMetrics (SM_CXMENUSIZE);
  197. int cyButton = GetSystemMetrics (SM_CYMENUSIZE);
  198. DWORD dwStyle = GetStyle ();
  199. CRect rectDraw (0, 0, cxButton, cyButton);
  200. // make sure we don't have both the maximize and restore styles
  201. ASSERT ((dwStyle & (MMDS_MAXIMIZE | MMDS_RESTORE)) !=
  202. (MMDS_MAXIMIZE | MMDS_RESTORE));
  203. // we shouldn't get here if we're tracking
  204. ASSERT (m_spTrackCtxt.get() == NULL);
  205. CMenu* pSysMenu = GetActiveSystemMenu ();
  206. for (int i = 0; i < cMapEntries; i++)
  207. {
  208. int nDataIndex = anDrawOrder[i];
  209. if (dwStyle & anMDIDataMap[nDataIndex].dwStyle)
  210. {
  211. int nState = nDataIndex;
  212. if (!IsSysCommandEnabled (anMDIDataMap[nDataIndex].nCommand, pSysMenu))
  213. nState |= DFCS_INACTIVE;
  214. DrawCaptionControl (&dc, rectDraw, nState, false);
  215. rectDraw.OffsetRect (cxButton, 0);
  216. }
  217. }
  218. #ifdef DRAW_OFF_SCREEN
  219. dcPaint.BitBlt (0, 0, cx, cy, &dcMem, 0, 0, SRCCOPY);
  220. dcMem.SelectObject (pbmOld);
  221. #endif
  222. }
  223. /*--------------------------------------------------------------------------*
  224. * CMDIMenuDecoration::OnWindowPosChanging
  225. *
  226. * WM_WINDOWPOSCHANGING handler for CMDIMenuDecoration.
  227. *--------------------------------------------------------------------------*/
  228. void CMDIMenuDecoration::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
  229. {
  230. DWORD dwStyle = GetStyle ();
  231. if (dwStyle & MMDS_AUTOSIZE)
  232. {
  233. int cxButton = GetSystemMetrics (SM_CXMENUSIZE);
  234. int cyButton = GetSystemMetrics (SM_CYMENUSIZE);
  235. lpwndpos->cx = 0;
  236. lpwndpos->cy = cyButton;
  237. dwStyle &= MMDS_BTNSTYLES;
  238. while (dwStyle != 0)
  239. {
  240. if (dwStyle & 1)
  241. lpwndpos->cx += cxButton;
  242. dwStyle >>= 1;
  243. }
  244. }
  245. else
  246. CWnd::OnWindowPosChanging(lpwndpos);
  247. }
  248. /*--------------------------------------------------------------------------*
  249. * CMDIMenuDecoration::OnLButtonDown
  250. *
  251. * WM_LBUTTONDOWN handler for CMDIMenuDecoration.
  252. *--------------------------------------------------------------------------*/
  253. // this routine needs placement new syntax --
  254. // temporarily remove MFC's incompatible placement new definition
  255. #ifdef _DEBUG
  256. #undef new
  257. #endif
  258. void CMDIMenuDecoration::OnLButtonDown(UINT nFlags, CPoint point)
  259. {
  260. typedef std::auto_ptr<char> CharPtr;
  261. CWnd::OnLButtonDown(nFlags, point);
  262. try
  263. {
  264. /*------------------------------------------------------------------*/
  265. /* This looks ugly. We'd like to write: */
  266. /* */
  267. /* m_spTrackCtxt = CMouseTrackContextPtr ( */
  268. /* new CMouseTrackContext (this, point)); */
  269. /* */
  270. /* but CMouseTrackContext's ctor might throw an exception. If it */
  271. /* does, the smart pointer won't yet have been initialized so the */
  272. /* CMouseTrackContext won't be deleted. */
  273. /* */
  274. /* To get around it, we'll create a smart pointer pointing to a */
  275. /* dynamically-allocated buffer of the right size. That buffer */
  276. /* will not leak with an exception. We can then use a placement */
  277. /* new to initialize a CMouseTrackContext in the hunk of memory. */
  278. /* It's now not a problem if the CMouseTrackContext throws, because */
  279. /* the buffer is still protected it's own smart pointer. Once */
  280. /* the placement new completes successfully, we can transfer */
  281. /* ownership of the object to a CMouseTrackContext smart pointer */
  282. /* and we're golden. */
  283. /*------------------------------------------------------------------*/
  284. // allocate a hunk of memory and construct a CMouseTrackContext in it
  285. CharPtr spchBuffer = CharPtr (new char[sizeof (CMouseTrackContext)]);
  286. CMouseTrackContext* pNewCtxt = new (spchBuffer.get()) CMouseTrackContext (this, point);
  287. // if we get here, the CMouseTrackContext initialized properly,
  288. // so we can transfer ownership to the CMouseTrackContext smart pointer
  289. spchBuffer.release ();
  290. m_spTrackCtxt = CMouseTrackContextPtr (pNewCtxt);
  291. }
  292. catch (CUserException* pe)
  293. {
  294. // do nothing, just eat the exception
  295. pe->Delete();
  296. }
  297. }
  298. #ifdef _DEBUG
  299. #define new DEBUG_NEW
  300. #endif
  301. /*--------------------------------------------------------------------------*
  302. * CMDIMenuDecoration::OnLButtonUp
  303. *
  304. * WM_LBUTTONUP handler for CMDIMenuDecoration.
  305. *--------------------------------------------------------------------------*/
  306. void CMDIMenuDecoration::OnLButtonUp(UINT nFlags, CPoint point)
  307. {
  308. if (m_spTrackCtxt.get() != NULL)
  309. {
  310. const int nHotButton = m_spTrackCtxt->m_nHotButton;
  311. const int nHitButton = m_spTrackCtxt->HitTest (point);
  312. // delete the track context
  313. m_spTrackCtxt = CMouseTrackContextPtr (NULL);
  314. if (nHitButton == nHotButton)
  315. {
  316. int cmd = anMDIDataMap[nHotButton].nCommand;
  317. // make sure the command looks like a valid sys command
  318. ASSERT (cmd >= 0xF000);
  319. ClientToScreen (&point);
  320. GetOwner()->SendMessage (WM_SYSCOMMAND, cmd,
  321. MAKELPARAM (point.x, point.y));
  322. }
  323. }
  324. }
  325. /*--------------------------------------------------------------------------*
  326. * CMDIMenuDecoration::OnMouseMove
  327. *
  328. * WM_MOUSEMOVE handler for CMDIMenuDecoration.
  329. *--------------------------------------------------------------------------*/
  330. void CMDIMenuDecoration::OnMouseMove(UINT nFlags, CPoint point)
  331. {
  332. if (m_spTrackCtxt.get() != NULL)
  333. m_spTrackCtxt->Track (point);
  334. }
  335. /*--------------------------------------------------------------------------*
  336. * CMDIMenuDecoration::GetActiveSystemMenu
  337. *
  338. *
  339. *--------------------------------------------------------------------------*/
  340. CMenu* CMDIMenuDecoration::GetActiveSystemMenu ()
  341. {
  342. CFrameWnd* pwndFrame = GetParentFrame()->GetActiveFrame();
  343. ASSERT (pwndFrame != NULL);
  344. CMenu* pSysMenu = pwndFrame->GetSystemMenu (FALSE);
  345. ASSERT (pSysMenu != NULL);
  346. return (pSysMenu);
  347. }
  348. /*--------------------------------------------------------------------------*
  349. * CMDIMenuDecoration::IsSysCommandEnabled
  350. *
  351. *
  352. *--------------------------------------------------------------------------*/
  353. bool CMDIMenuDecoration::IsSysCommandEnabled (int nSysCommand, CMenu* pSysMenu)
  354. {
  355. if (pSysMenu == NULL)
  356. pSysMenu = GetActiveSystemMenu ();
  357. int nState = pSysMenu->GetMenuState (nSysCommand, MF_BYCOMMAND);
  358. ASSERT (nState != 0xFFFFFFFF);
  359. return ((nState & MF_GRAYED) == 0);
  360. }