home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / winfrmx.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  11KB  |  406 lines

  1. // This is a part of the Microsoft Foundation Classes C++ library.
  2. // Copyright (C) 1992-1998 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Microsoft Foundation Classes Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Microsoft Foundation Classes product.
  10.  
  11. #include "stdafx.h"
  12.  
  13. #ifdef AFX_CORE3_SEG
  14. #pragma code_seg(AFX_CORE3_SEG)
  15. #endif
  16.  
  17. #ifdef _DEBUG
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21.  
  22. /////////////////////////////////////////////////////////////////////////////
  23. // Basic Help support
  24.  
  25. void CWnd::OnHelp()  // use context to derive help context
  26. {
  27.     // attempt to get help from whoever is tracking
  28.     HWND hWnd = ::GetCapture();
  29.     while (hWnd != NULL)
  30.     {
  31.         // attempt to process help
  32.         if (::SendMessage(hWnd, WM_COMMANDHELP, 0, 0))
  33.             return;
  34.  
  35.         // check next parent/owner in the parent/owner chain
  36.         hWnd = AfxGetParentOwner(hWnd);
  37.     }
  38.     // attempt to get help from whoever has the focus
  39.     hWnd = ::GetFocus();
  40.     while (hWnd != NULL)
  41.     {
  42.         // attempt to process help
  43.         if (::SendMessage(hWnd, WM_COMMANDHELP, 0, 0))
  44.             return;
  45.  
  46.         // check next parent/owner in the parent/owner chain
  47.         hWnd = AfxGetParentOwner(hWnd);
  48.     }
  49.     // attempt to get help from the active window
  50.     CWnd* pWnd = GetTopLevelParent();
  51.     hWnd = ::GetLastActivePopup(pWnd->GetSafeHwnd());
  52.     while (hWnd != NULL)
  53.     {
  54.         // attempt to process help
  55.         if (::SendMessage(hWnd, WM_COMMANDHELP, 0, 0))
  56.             return;
  57.  
  58.         // check next parent/owner in the parent/owner chain
  59.         hWnd = AfxGetParentOwner(hWnd);
  60.     }
  61.     // No context available, bring up default.
  62.     SendMessage(WM_COMMAND, ID_DEFAULT_HELP);
  63. }
  64.  
  65. void CFrameWnd::OnHelp()
  66. {
  67.     // Be careful not call WinHelp when the error is failing to lauch help
  68.     if (m_dwPromptContext != 0)
  69.     {
  70.         if (m_dwPromptContext != HID_BASE_PROMPT+AFX_IDP_FAILED_TO_LAUNCH_HELP)
  71.             AfxGetApp()->WinHelp(m_dwPromptContext);
  72.         return;
  73.     }
  74.     CWnd::OnHelp();
  75. }
  76.  
  77. void CWnd::OnHelpIndex()
  78. {
  79.     AfxGetApp()->WinHelp(0L, HELP_INDEX);
  80. }
  81.  
  82. void CWnd::OnHelpFinder()
  83. {
  84.     AfxGetApp()->WinHelp(0L, HELP_FINDER);
  85. }
  86.  
  87. void CWnd::OnHelpUsing()
  88. {
  89.     AfxGetApp()->WinHelp(0L, HELP_HELPONHELP);
  90. }
  91.  
  92. /////////////////////////////////////////////////////////////////////////////
  93. // Context Help Mode support
  94.  
  95. BOOL CFrameWnd::CanEnterHelpMode()
  96. {
  97.     ASSERT(m_bHelpMode != HELP_ACTIVE); // already in help mode?
  98.  
  99.     // unable to start help if the cursor cannot be loaded from the resources
  100.     if (afxData.hcurHelp == NULL)
  101.     {
  102.         afxData.hcurHelp = ::LoadCursor(NULL, IDC_HELP);
  103.         if (afxData.hcurHelp == NULL)
  104.         {
  105.             // load help cursor after handles have been setup
  106.             HINSTANCE hInst = AfxFindResourceHandle(
  107.                 MAKEINTRESOURCE(AFX_IDC_CONTEXTHELP), RT_GROUP_CURSOR);
  108.             afxData.hcurHelp = LoadCursor(hInst,
  109.                 MAKEINTRESOURCE(AFX_IDC_CONTEXTHELP));
  110.         }
  111.         if (afxData.hcurHelp == NULL)
  112.             return FALSE;
  113.     }
  114.  
  115.     // return TRUE if there is a handler for ID_CONTEXT_HELP
  116.     AFX_CMDHANDLERINFO info;
  117.     return OnCmdMsg(ID_CONTEXT_HELP, CN_COMMAND, NULL, &info);
  118. }
  119.  
  120. void CFrameWnd::OnContextHelp()
  121. {
  122.     // don't enter twice, and don't enter if initialization fails
  123.     if (m_bHelpMode == HELP_ACTIVE || !CanEnterHelpMode())
  124.         return;
  125.  
  126.     // don't enter help mode with pending WM_EXITHELPMODE message
  127.     MSG msg;
  128.     if (PeekMessage(&msg, m_hWnd, WM_EXITHELPMODE, WM_EXITHELPMODE,
  129.         PM_REMOVE|PM_NOYIELD))
  130.     {
  131.         return;
  132.     }
  133.  
  134.     BOOL bHelpMode = m_bHelpMode;
  135.     ASSERT(m_bHelpMode == HELP_INACTIVE || m_bHelpMode == HELP_ENTERING);
  136.     m_bHelpMode = HELP_ACTIVE;
  137.  
  138. #ifndef _AFX_NO_OLE_SUPPORT
  139.     // allow any in-place active servers to go into help mode
  140.     if (bHelpMode != HELP_ENTERING && m_pNotifyHook != NULL &&
  141.         !m_pNotifyHook->OnContextHelp(TRUE))
  142.     {
  143.         TRACE0("Error: an in-place server failed to enter context help mode.\n");
  144.         m_pNotifyHook->OnContextHelp(FALSE);    // undo partial help mode
  145.         m_bHelpMode = HELP_INACTIVE;
  146.         return;
  147.     }
  148. #endif
  149.  
  150.     if (bHelpMode == HELP_INACTIVE)
  151.     {
  152.         // need to delay help startup until later
  153.         PostMessage(WM_COMMAND, ID_CONTEXT_HELP);
  154.         m_bHelpMode = HELP_ENTERING;
  155.         return;
  156.     }
  157.  
  158.     ASSERT(m_bHelpMode == HELP_ACTIVE);
  159.  
  160.     // display special help mode message on status bar
  161.     UINT nMsgSave = (UINT)SendMessage(WM_SETMESSAGESTRING,
  162.         (WPARAM)AFX_IDS_HELPMODEMESSAGE);
  163.     if (nMsgSave == 0)
  164.         nMsgSave = AFX_IDS_IDLEMESSAGE;
  165.  
  166.     DWORD   dwContext = 0;
  167.     POINT   point;
  168.  
  169.     GetCursorPos(&point);
  170.     SetHelpCapture(point, NULL);
  171.     LONG lIdleCount = 0;
  172.     CWinApp* pApp = AfxGetApp();
  173.  
  174.     while (m_bHelpMode)
  175.     {
  176.         if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
  177.         {
  178.             if (!ProcessHelpMsg(msg, &dwContext))
  179.                 break;
  180.             ASSERT(dwContext == 0);
  181.         }
  182.         else if (!pApp->OnIdle(lIdleCount++))
  183.         {
  184.             lIdleCount = 0;
  185.             WaitMessage();
  186.         }
  187.     }
  188.  
  189.     m_bHelpMode = HELP_INACTIVE;
  190.     ReleaseCapture();
  191.  
  192.     // make sure the cursor is set appropriately
  193.     SetCapture();
  194.     ReleaseCapture();
  195.  
  196.     // restore original status bar text
  197.     SendMessage(WM_SETMESSAGESTRING, (WPARAM)nMsgSave);
  198.  
  199. #ifndef _AFX_NO_OLE_SUPPORT
  200.     // tell in-place servers to exit Shift+F1 help mode
  201.     if (m_pNotifyHook != NULL)
  202.         m_pNotifyHook->OnContextHelp(FALSE);
  203. #endif
  204.  
  205.     if (dwContext != 0)
  206.     {
  207.         if (dwContext == -1)
  208.             SendMessage(WM_COMMAND, ID_DEFAULT_HELP);
  209.         else
  210.             pApp->WinHelp(dwContext);
  211.     }
  212.     PostMessage(WM_KICKIDLE);    // trigger idle update
  213. }
  214.  
  215. /////////////////////////////////////////////////////////////////////////////
  216. // OnContextHelp helpers.
  217.  
  218. HWND CFrameWnd::SetHelpCapture(POINT point, BOOL* pbDescendant)
  219.     // set or release capture, depending on where the mouse is
  220.     // also assign the proper cursor to be displayed.
  221. {
  222.     if (!m_bHelpMode)
  223.         return NULL;
  224.  
  225.     HWND hWndCapture = ::GetCapture();
  226.     CWnd* pWndHit = WindowFromPoint(point);
  227.     HWND hWndHit = pWndHit->GetSafeHwnd();
  228.     CWnd* pTopHit = pWndHit->GetTopLevelParent();
  229.     CWnd* pTopActive = GetActiveWindow()->GetTopLevelParent();
  230.     BOOL bDescendant = FALSE;
  231.     HTASK hCurTask = (HTASK)GetCurrentThreadId();
  232.     HTASK hTaskHit = hWndHit != NULL ? ::GetWindowTask(hWndHit) : NULL;
  233.  
  234.     if (pTopActive == NULL || hWndHit == ::GetDesktopWindow())
  235.     {
  236.         if (hWndCapture == m_hWnd)
  237.             ReleaseCapture();
  238.         SetCursor(afxData.hcurArrow);
  239.     }
  240.     else if (pTopActive == NULL ||
  241.         hWndHit == NULL || hCurTask != hTaskHit ||
  242.         !AfxIsDescendant(m_hWnd, hWndHit))
  243.     {
  244.         if (hCurTask != hTaskHit)
  245.             hWndHit = NULL;
  246.         if (hWndCapture == m_hWnd)
  247.             ReleaseCapture();
  248.     }
  249.     else
  250.     {
  251.         bDescendant = TRUE;
  252.         if (pTopActive != pTopHit)
  253.             hWndHit = NULL;
  254.         else
  255.         {
  256.             if (hWndCapture != m_hWnd)
  257.                 ::SetCapture(m_hWnd);
  258.             SetCursor(afxData.hcurHelp);
  259.         }
  260.     }
  261.     if (pbDescendant != NULL)
  262.         *pbDescendant = bDescendant;
  263.     return hWndHit;
  264. }
  265.  
  266. AFX_STATIC DWORD AFXAPI _AfxMapClientArea(HWND hWnd, POINT point)
  267. {
  268.     DWORD dwContext;
  269.  
  270.     do
  271.     {
  272.         ASSERT(::IsWindow(hWnd));
  273.  
  274.         // check current window
  275.         ::ScreenToClient(hWnd, &point);
  276.         dwContext = ::SendMessage(hWnd, WM_HELPHITTEST, 0,
  277.             MAKELONG(point.x, point.y));
  278.         ::ClientToScreen(hWnd, &point);
  279.  
  280.         // don't use owner's of popup windows, just child/parent relationship
  281.         if ((GetWindowLong(hWnd, GWL_STYLE) & WS_CHILD) == 0)
  282.             break;
  283.         // check parent window
  284.         hWnd = ::GetParent(hWnd);
  285.     }
  286.     while (hWnd && dwContext == 0);
  287.  
  288.     return dwContext == 0 ? -1 : dwContext;
  289. }
  290.  
  291. AFX_STATIC DWORD AFXAPI _AfxMapNonClientArea(int iHit)
  292. {
  293.     ASSERT(iHit != HTCLIENT);
  294.  
  295.     if (iHit < 0 || iHit > HTHELP)
  296.         return (DWORD)-1;
  297.  
  298.     return HID_BASE_NCAREAS+iHit;
  299. }
  300.  
  301. BOOL CFrameWnd::ProcessHelpMsg(MSG& msg, DWORD* pContext)
  302. {
  303.     ASSERT(pContext != NULL);
  304.  
  305.     if (msg.message == WM_EXITHELPMODE ||
  306.         (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE))
  307.     {
  308.         PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
  309.         return FALSE;
  310.     }
  311.  
  312.     CPoint point;
  313.     if ((msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) ||
  314.         (msg.message >= WM_NCMOUSEFIRST && msg.message <= WM_NCMOUSELAST))
  315.     {
  316.         BOOL bDescendant;
  317.         HWND hWndHit = SetHelpCapture(msg.pt, &bDescendant);
  318.         if (hWndHit == NULL)
  319.             return TRUE;
  320.  
  321.         if (bDescendant)
  322.         {
  323.             if (msg.message != WM_LBUTTONDOWN)
  324.             {
  325.                 // Hit one of our owned windows -- eat the message.
  326.                 PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
  327.                 return TRUE;
  328.             }
  329.             int iHit = (int)::SendMessage(hWndHit, WM_NCHITTEST, 0,
  330.                 MAKELONG(msg.pt.x, msg.pt.y));
  331.             if (iHit == HTMENU || iHit == HTSYSMENU)
  332.             {
  333.                 ASSERT(::GetCapture() == m_hWnd);
  334.                 ReleaseCapture();
  335.                 // the message we peeked changes into a non-client because
  336.                 // of the release capture.
  337.                 GetMessage(&msg, NULL, WM_NCLBUTTONDOWN, WM_NCLBUTTONDOWN);
  338.                 DispatchMessage(&msg);
  339.                 GetCursorPos(&point);
  340.                 SetHelpCapture(point, NULL);
  341.             }
  342.             else if (iHit == HTCLIENT)
  343.             {
  344.                 *pContext = _AfxMapClientArea(hWndHit, msg.pt);
  345.                 PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
  346.                 return FALSE;
  347.             }
  348.             else
  349.             {
  350.                 *pContext = _AfxMapNonClientArea(iHit);
  351.                 PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
  352.                 return FALSE;
  353.             }
  354.         }
  355.         else
  356.         {
  357.             // Hit one of our apps windows (or desktop) -- dispatch the message.
  358.             PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE);
  359.  
  360.             // Dispatch mouse messages that hit the desktop!
  361.             DispatchMessage(&msg);
  362.         }
  363.     }
  364.     else if (msg.message == WM_SYSCOMMAND ||
  365.              (msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST))
  366.     {
  367.         if (::GetCapture() != NULL)
  368.         {
  369.             ReleaseCapture();
  370.             MSG msg;
  371.             while (PeekMessage(&msg, NULL, WM_MOUSEFIRST,
  372.                 WM_MOUSELAST, PM_REMOVE|PM_NOYIELD));
  373.         }
  374.         if (PeekMessage(&msg, NULL, msg.message, msg.message, PM_NOREMOVE))
  375.         {
  376.             GetMessage(&msg, NULL, msg.message, msg.message);
  377.             if (!PreTranslateMessage(&msg))
  378.             {
  379.                 TranslateMessage(&msg);
  380.                 if (msg.message == WM_SYSCOMMAND ||
  381.                   (msg.message >= WM_SYSKEYFIRST &&
  382.                     msg.message <= WM_SYSKEYLAST))
  383.                 {
  384.                     // only dispatch system keys and system commands
  385.                     ASSERT(msg.message == WM_SYSCOMMAND ||
  386.                          (msg.message >= WM_SYSKEYFIRST &&
  387.                           msg.message <= WM_SYSKEYLAST));
  388.                     DispatchMessage(&msg);
  389.                 }
  390.             }
  391.         }
  392.         GetCursorPos(&point);
  393.         SetHelpCapture(point, NULL);
  394.     }
  395.     else
  396.     {
  397.         // allow all other messages to go through (capture still set)
  398.         if (PeekMessage(&msg, NULL, msg.message, msg.message, PM_REMOVE))
  399.             DispatchMessage(&msg);
  400.     }
  401.  
  402.     return TRUE;
  403. }
  404.  
  405. /////////////////////////////////////////////////////////////////////////////
  406.