home *** CD-ROM | disk | FTP | other *** search
/ Mastering MFC Development / MMD.ISO / samples / c05 / dynamenu / dynaview.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-20  |  11.0 KB  |  394 lines

  1. // DynaView.cpp : implementation of the CMenusDynamicView class
  2. //
  3.  
  4. #include "stdafx.h"
  5. #include "DynaMenu.h"
  6.  
  7. #include "DynaMDoc.h"
  8. #include "DynaView.h"
  9. #include "resource.h"
  10.  
  11. #ifdef _DEBUG
  12. #define new DEBUG_NEW
  13. #undef THIS_FILE
  14. static char THIS_FILE[] = __FILE__;
  15. #endif
  16.  
  17. const COLORREF BLACK=RGB(0,0,0);
  18. const COLORREF RED=RGB(255,0,0);
  19. const COLORREF GREEN=RGB(0,255,0);
  20. const COLORREF BLUE=RGB(0,0,255);
  21.  
  22. const COLORREF CYAN=RGB(0,255,255);
  23. const COLORREF PURPLE=RGB(255,0,255);
  24. const COLORREF YELLOW=RGB(255,255,0);
  25.  
  26. const COLORREF WHITE=RGB(255,255,255);
  27.  
  28. /////////////////////////////////////////////////////////////////////////////
  29. // CMenusDynamicView
  30.  
  31. IMPLEMENT_DYNCREATE(CMenusDynamicView, CView)
  32.  
  33. BEGIN_MESSAGE_MAP(CMenusDynamicView, CView)
  34.     //{{AFX_MSG_MAP(CMenusDynamicView)
  35.     ON_COMMAND(ID_OPTIONS_EXTRACOLORS, OnOptionsExtracolors)
  36.     ON_COMMAND(ID_OPTIONS_STANDARDCOLORS, OnOptionsStandardcolors)
  37.     ON_COMMAND(ID_OPTIONS_EXTRACOLORS_CASCADE, OnOptionsExtracolorsCascade)
  38.     ON_WM_DESTROY()
  39.     //}}AFX_MSG_MAP
  40.     ON_WM_CONTEXTMENU()
  41.     ON_COMMAND_RANGE(ID_COLORS_BLACK, ID_COLORS_BLUE, OnColors)
  42.     ON_UPDATE_COMMAND_UI_RANGE(ID_COLORS_BLACK, ID_COLORS_BLUE, OnUpdateColors)
  43.     ON_COMMAND_RANGE(ID_COLORS_CYAN, ID_COLORS_YELLOW, OnColors)
  44.     ON_UPDATE_COMMAND_UI_RANGE(ID_COLORS_CYAN, ID_COLORS_YELLOW, OnUpdateColors)
  45.     ON_UPDATE_COMMAND_UI_RANGE(ID_OPTIONS_EXTRACOLORS, ID_OPTIONS_EXTRACOLORS_CASCADE, OnUpdateOptions)
  46. END_MESSAGE_MAP()
  47.  
  48. /////////////////////////////////////////////////////////////////////////////
  49. // CMenusDynamicView construction/destruction
  50. CMenusDynamicView::CMenusDynamicView()
  51.     :m_pExtraColors(0)
  52. {
  53. }
  54.  
  55. CMenusDynamicView::~CMenusDynamicView()
  56. {
  57. }
  58.  
  59. BOOL CMenusDynamicView::PreCreateWindow(CREATESTRUCT& cs)
  60. {
  61.     return CView::PreCreateWindow(cs);
  62. }
  63.  
  64. /////////////////////////////////////////////////////////////////////////////
  65. // CMenusDynamicView drawing
  66.  
  67. void CMenusDynamicView::OnDraw(CDC* pDC)
  68. {
  69.     CMenusDynamicDoc* pDoc = GetDocument();
  70.     ASSERT_VALID(pDoc);
  71.  
  72.     CRect r;
  73.     GetClientRect(&r);
  74.     int x = r.right / 2, y = r.bottom / 2;
  75.  
  76.     pDC->SetTextColor(pDoc->GetColor());
  77.     pDC->SetTextAlign (TA_CENTER | TA_BASELINE);
  78.     pDC->TextOut (x, y, pDoc->GetPhrase());
  79. }
  80.  
  81. /////////////////////////////////////////////////////////////////////////////
  82. // CMenusDynamicView diagnostics
  83.  
  84. #ifdef _DEBUG
  85. void CMenusDynamicView::AssertValid() const
  86. {
  87.     CView::AssertValid();
  88. }
  89.  
  90. void CMenusDynamicView::Dump(CDumpContext& dc) const
  91. {
  92.     CView::Dump(dc);
  93. }
  94.  
  95. CMenusDynamicDoc* CMenusDynamicView::GetDocument() // non-debug version is inline
  96. {
  97.     ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMenusDynamicDoc)));
  98.     return (CMenusDynamicDoc*)m_pDocument;
  99. }
  100. #endif //_DEBUG
  101.  
  102. /////////////////////////////////////////////////////////////////////////////
  103. // CMenusDynamicView message handlers
  104. void CMenusDynamicView::OnOptionsExtracolors() 
  105. {
  106.     CMenu *pAddinMenu, *pTopMenu;
  107.     
  108.     // To append to the Colors menu, we'll need a pointer to it.
  109.     // We begin by obtaining a pointer to the top-level menu.
  110.     pTopMenu = AfxGetMainWnd()->GetMenu();
  111.  
  112.     // Colors is the 3rd menu, but that's #2 in a 0-based system.
  113.     pAddinMenu = pTopMenu->GetSubMenu(2);
  114.     ASSERT(pAddinMenu != NULL);
  115.     
  116.     // First, add a separator to separate the default menus
  117.     // from the dynamic menus.
  118.     pAddinMenu->AppendMenu(MF_SEPARATOR);
  119.  
  120.     // Append the 3 menu items. They will generate consecutive
  121.     // command IDs.
  122.     CString prompt;
  123.     for (int i = 0; i < 3; i++)
  124.     {
  125.         prompt.LoadString(ID_COLORS_CYAN + i);
  126.         pAddinMenu->AppendMenu(MF_STRING, 
  127.                         ID_COLORS_CYAN + i, prompt);
  128.     }
  129. }
  130.  
  131. void CMenusDynamicView::OnOptionsStandardcolors() 
  132. {
  133.     CMenu *pAddinMenu, *pTopMenu;
  134.     
  135.     // To remove items from the Colors menu, we'll need a pointer to it.
  136.     // We begin by obtaining a pointer to the top-level menu.
  137.     pTopMenu = AfxGetMainWnd()->GetMenu();
  138.  
  139.     // Colors is the 3rd menu, but that's #2 in a 0-based system.
  140.     pAddinMenu = pTopMenu->GetSubMenu(2);
  141.  
  142.     int i = pAddinMenu->GetMenuItemCount();
  143.     
  144.     // If the extra colors were displayed as additions
  145.     // to the colors menu, then removing them is simple. 
  146.     // We want to leave only 4 menu items.
  147.     if (8 == i)
  148.     {
  149.         i--;                // adjust to 0-based.
  150.         while(i > 3)
  151.         {
  152.             pAddinMenu->RemoveMenu(i, MF_BYPOSITION);
  153.             i--;
  154.         }
  155.     }
  156.     else
  157.     // Otherwise, it's a cascading menu which must be properly
  158.     // destroyed, since it was allocated using CreatePopupMenu
  159.     // in another function.
  160.     {
  161.         // First delete the cascading menu. It's also
  162.         // necessary to free the object that was allocated in
  163.         // OnOptionsExtracolorsCascade.
  164.         pAddinMenu->DeleteMenu(5, MF_BYPOSITION);
  165.         delete m_pExtraColors;
  166.         m_pExtraColors = 0;
  167.         // Then remove the separator bar.
  168.         pAddinMenu->RemoveMenu(4, MF_BYPOSITION);
  169.     }
  170.  
  171.     // Finally, set the color to black and force a redraw;
  172.     CMenusDynamicDoc* pDoc = GetDocument();
  173.     pDoc->SetColor(BLACK);
  174.     Invalidate();
  175. }
  176.  
  177. // This functions updates the menu items under the Options menu.
  178. void CMenusDynamicView::OnUpdateOptions(CCmdUI* pCmdUI)
  179. {
  180.     CMenu *pAddinMenu, *pTopMenu;
  181.     pTopMenu = AfxGetMainWnd()->GetMenu();
  182.     pAddinMenu = pTopMenu->GetSubMenu(2);
  183.  
  184.     switch (pCmdUI->m_nID)
  185.     {
  186.         case ID_OPTIONS_EXTRACOLORS:
  187.         case ID_OPTIONS_EXTRACOLORS_CASCADE:
  188.             pCmdUI->Enable(pAddinMenu->GetMenuItemCount() == 4);
  189.             break;
  190.         case ID_OPTIONS_STANDARDCOLORS:
  191.             pCmdUI->Enable(pAddinMenu->GetMenuItemCount() != 4);
  192.     }
  193. }
  194.  
  195. void CMenusDynamicView::OnColors(UINT nID)
  196. {
  197.     CMenusDynamicDoc* pDoc = GetDocument();
  198.     ASSERT_VALID(pDoc);
  199.  
  200.     pDoc->SetColor(IDtoColorRef(nID));
  201.     Invalidate();
  202. }
  203.  
  204. void CMenusDynamicView::OnUpdateColors(CCmdUI* pCmdUI)
  205. {
  206.     CMenusDynamicDoc* pDoc = GetDocument();
  207.     ASSERT_VALID(pDoc);
  208.  
  209.     pCmdUI->SetCheck(pDoc->GetColor() == IDtoColorRef(pCmdUI->m_nID));
  210. }
  211.  
  212. void CMenusDynamicView::OnOptionsExtracolorsCascade() 
  213. {
  214.     // A cascading menu is a new menu object that must persist
  215.     // after this function terminates. Thus the use the pointer
  216.     // m_pExtraColors, which is a member of the view class. It's needed
  217.     // elsewhere in this class to properly delete the object
  218.     // allocated in this function.
  219.     m_pExtraColors = new CMenu;
  220.     m_pExtraColors->CreatePopupMenu();
  221.  
  222.     // Next, add existing menu items to this newly created
  223.     // menu object.
  224.     CString prompt;
  225.     for (int i = 0; i < 3; i++)
  226.     {
  227.         prompt.LoadString(ID_COLORS_CYAN + i);
  228.         m_pExtraColors->AppendMenu(MF_STRING, 
  229.                                     ID_COLORS_CYAN + i, prompt);
  230.     }
  231.     
  232.     CMenu *pTopMenu, *pSubMenu;
  233.  
  234.     pTopMenu = AfxGetMainWnd()->GetMenu();
  235.     pSubMenu = pTopMenu->GetSubMenu(2);
  236.     
  237.     // Now that we have a pointer to the appropriate sub menu,
  238.     // the new menu object can be appended. The flag MF_POPUP
  239.     // tells AppendMenu how to interpret the 3rd argument.
  240.     prompt.LoadString(ID_EXTRA_COLORS);
  241.     pSubMenu->AppendMenu(MF_STRING | MF_POPUP, 
  242.                 (UINT)m_pExtraColors->m_hMenu, prompt);
  243.  
  244.     // Be sure to examine the code in OnOptionsStandardcolors()
  245.     // to see how this menu object is deleted, NOT removed!
  246.     // Also, examine OnDestroy.
  247. }
  248.  
  249. void CMenusDynamicView::OnDestroy() 
  250. {
  251.     CView::OnDestroy();
  252.  
  253.     // If the view allocated a cascading menu, its
  254.     // memory must be given back.
  255.     if (m_pExtraColors)
  256.     {
  257.         delete m_pExtraColors;
  258.         m_pExtraColors = 0;
  259.     }
  260. }
  261.  
  262. CRect CMenusDynamicView::GetPhraseBounds()
  263. {
  264.     CClientDC dc(this);
  265.     
  266.     TEXTMETRIC tm;
  267.     dc.GetTextMetrics(&tm);
  268.  
  269.     CMenusDynamicDoc* pDoc = GetDocument();
  270.     CString temp = pDoc->GetPhrase();
  271.     CSize cs = dc.GetTextExtent(temp, temp.GetLength());
  272.     
  273.     // Recall that the phrase is centered on a point in
  274.     // the exact middle of the view. See View class's OnDraw.
  275.  
  276.     // This makes creating a bounding rectangle a bit tricky.
  277.     // We'll have to divide the text extents by 2, and take into
  278.     // account the amount of descent of the current font.
  279.     CRect r;
  280.     GetClientRect(&r);
  281.  
  282.     int x = (r.right / 2) - (cs.cx / 2);
  283.     int y = (r.bottom / 2) - (cs.cy / 2) - tm.tmDescent;
  284.     
  285.     return CRect(CPoint(x, y), cs);
  286. }
  287.  
  288. void CMenusDynamicView::OnContextMenu(CWnd*, CPoint point)
  289. {
  290.     // First, we have to determine if the mouse is somewhere on the
  291.     // phrase. Begin by obtaining a rectangle that bounds the phrase.
  292.     CRect BoundingRectangle = GetPhraseBounds();
  293.     
  294.     // Because the parameter is in screen coordinates, we'll have to
  295.     // convert it to client coordinates before doing any hit-testing.
  296.     CPoint pt(point);
  297.     ScreenToClient(&pt);
  298.     
  299.     // Then, if the mouse is not the rectangle, exit this function.
  300.     if (FALSE == BoundingRectangle.PtInRect(pt))
  301.         return;
  302.  
  303.     CMenu menu;
  304.     VERIFY(menu.LoadMenu(CG_IDR_POPUP_MENUS_DYNAMIC_VIEW));
  305.  
  306.     CMenu* pPopup = menu.GetSubMenu(0);
  307.     ASSERT(pPopup != NULL);
  308.  
  309.     // The four existing menu items (black, red, green and blue) were
  310.     // added to the popup menu using the menu editor. For demonstration
  311.     // purposes, 3 more menu items will by dynamically added to the
  312.     // popup. They'll be separated from the first 4.
  313.     pPopup->AppendMenu(MF_SEPARATOR);
  314.     CString prompt;
  315.     for (int i = 0; i < 3; i++)
  316.     {
  317.         prompt.LoadString(ID_COLORS_CYAN + i);
  318.         pPopup->AppendMenu(MF_STRING, ID_COLORS_CYAN + i, prompt);
  319.     }
  320.  
  321.     // Follow the chain of owners to find a window that's not a child.
  322.     // This window will serve as the owner of the popup menu.
  323.     CWnd* pWndPopupOwner = this;
  324.     while (pWndPopupOwner->GetStyle() & WS_CHILD)
  325.         pWndPopupOwner = pWndPopupOwner->GetParent();
  326.  
  327.     // Finally, display the popup menu.
  328.     pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,
  329.                             point.x, point.y, pWndPopupOwner);
  330. }
  331.  
  332. BOOL CMenusDynamicView::PreTranslateMessage(MSG* pMsg)
  333. {
  334.     // CG: This block was added by the Pop-up Menu component
  335.     {
  336.         // Shift+F10: show pop-up menu.
  337.         if ((((pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN) && // If we hit a key and
  338.             (pMsg->wParam == VK_F10) && (GetKeyState(VK_SHIFT) & ~1)) != 0) ||    // it's Shift+F10 OR
  339.             (pMsg->message == WM_CONTEXTMENU))                                    // Natural keyboard key
  340.         {
  341.             CRect rect;
  342.             GetClientRect(rect);
  343.             ClientToScreen(rect);
  344.  
  345.             CPoint point = rect.TopLeft();
  346.             point.Offset(5, 5);
  347.             OnContextMenu(NULL, point);
  348.  
  349.             return TRUE;
  350.         }
  351.     }
  352.     // Shift + F10 show popup menu.  (Like Word and Excel).
  353.  
  354.     // If we hit a key and it's Shift+F10 OR Natural keyboard key
  355.     if ((((pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN) &&
  356.         (pMsg->wParam == VK_F10) && (GetKeyState(VK_SHIFT) & ~1)) != 0) ||
  357.         (pMsg->message == WM_CONTEXTMENU))                                    
  358.     {
  359.         CRect rect;
  360.         GetClientRect(rect);
  361.         ClientToScreen(rect);
  362.  
  363.         CPoint point = rect.TopLeft();
  364.         point.Offset(5, 5);
  365.         OnContextMenu(NULL, point);
  366.  
  367.         return TRUE;
  368.     }
  369.  
  370.     return CView::PreTranslateMessage(pMsg);
  371. }
  372.  
  373. // This function converts one of the 7 Command IDs to a COLORREF value.
  374. COLORREF CMenusDynamicView::IDtoColorRef(int nID)
  375. {
  376.     switch (nID)
  377.     {
  378.         case ID_COLORS_RED:
  379.             return RED;
  380.         case ID_COLORS_GREEN:
  381.             return GREEN;        
  382.         case ID_COLORS_BLUE:
  383.             return BLUE;
  384.         case ID_COLORS_CYAN:
  385.             return CYAN;
  386.         case ID_COLORS_PURPLE:
  387.             return PURPLE;
  388.         case ID_COLORS_YELLOW:
  389.             return YELLOW;
  390.         default:
  391.             return BLACK;        
  392.     }
  393. }
  394.