home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / mfc / src / barcore.cpp < prev    next >
C/C++ Source or Header  |  1998-06-16  |  26KB  |  997 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. #include <malloc.h>
  13.  
  14. #ifdef AFX_CORE3_SEG
  15. #pragma code_seg(AFX_CORE3_SEG)
  16. #endif
  17.  
  18. #ifdef _DEBUG
  19. #undef THIS_FILE
  20. static char THIS_FILE[] = __FILE__;
  21. #endif
  22.  
  23. #define new DEBUG_NEW
  24.  
  25. /////////////////////////////////////////////////////////////////////////////
  26. // CControlBar
  27.  
  28. // IMPLEMENT_DYNAMIC for CControlBar is in wincore.cpp for .OBJ granularity reasons
  29.  
  30. BEGIN_MESSAGE_MAP(CControlBar, CWnd)
  31.     //{{AFX_MSG_MAP(CControlBar)
  32.     ON_WM_TIMER()
  33.     ON_WM_PAINT()
  34.     ON_WM_CTLCOLOR()
  35.     ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
  36.     ON_MESSAGE(WM_SIZEPARENT, OnSizeParent)
  37.     ON_WM_WINDOWPOSCHANGING()
  38.     ON_WM_SHOWWINDOW()
  39.     ON_WM_LBUTTONDOWN()
  40.     ON_WM_LBUTTONDBLCLK()
  41.     ON_WM_MOUSEACTIVATE()
  42.     ON_WM_CANCELMODE()
  43.     ON_WM_CREATE()
  44.     ON_WM_DESTROY()
  45.     ON_MESSAGE_VOID(WM_INITIALUPDATE, OnInitialUpdate)
  46.     ON_MESSAGE(WM_HELPHITTEST, OnHelpHitTest)
  47.     ON_WM_ERASEBKGND()
  48.     //}}AFX_MSG_MAP
  49. #ifndef _AFX_NO_CTL3D_SUPPORT
  50.     ON_MESSAGE(WM_QUERY3DCONTROLS, OnQuery3dControls)
  51. #endif
  52. END_MESSAGE_MAP()
  53.  
  54. #ifdef AFX_INIT_SEG
  55. #pragma code_seg(AFX_INIT_SEG)
  56. #endif
  57.  
  58. CControlBar::CControlBar()
  59. {
  60.     // no elements contained in the control bar yet
  61.     m_nCount = 0;
  62.     m_pData = NULL;
  63.  
  64.     // set up some default border spacings
  65.     m_cxLeftBorder = m_cxRightBorder = 6;
  66.     m_cxDefaultGap = 2;
  67.     m_cyTopBorder = m_cyBottomBorder = 1;
  68.     m_bAutoDelete = FALSE;
  69.     m_hWndOwner = NULL;
  70.     m_nStateFlags = 0;
  71.     m_pDockSite = NULL;
  72.     m_pDockBar = NULL;
  73.     m_pDockContext = NULL;
  74.     m_dwStyle = 0;
  75.     m_dwDockStyle = 0;
  76.     m_nMRUWidth = 32767;
  77. }
  78.  
  79. void CControlBar::SetBorders(int cxLeft, int cyTop, int cxRight, int cyBottom)
  80. {
  81.     ASSERT(cxLeft >= 0);
  82.     ASSERT(cyTop >= 0);
  83.     ASSERT(cxRight >= 0);
  84.     ASSERT(cyBottom >= 0);
  85.  
  86.     m_cxLeftBorder = cxLeft;
  87.     m_cyTopBorder = cyTop;
  88.     m_cxRightBorder = cxRight;
  89.     m_cyBottomBorder = cyBottom;
  90. }
  91.  
  92. BOOL CControlBar::PreCreateWindow(CREATESTRUCT& cs)
  93. {
  94.     if (!CWnd::PreCreateWindow(cs))
  95.         return FALSE;
  96.  
  97.     // force clipsliblings (otherwise will cause repaint problems)
  98.     cs.style |= WS_CLIPSIBLINGS;
  99.  
  100.     // default border style translation for Win4
  101.     //  (you can turn off this translation by setting CBRS_BORDER_3D)
  102.     if (afxData.bWin4 && (m_dwStyle & CBRS_BORDER_3D) == 0)
  103.     {
  104.         DWORD dwNewStyle = 0;
  105.         switch (m_dwStyle & (CBRS_BORDER_ANY|CBRS_ALIGN_ANY))
  106.         {
  107.         case CBRS_LEFT:
  108.             dwNewStyle = CBRS_BORDER_TOP|CBRS_BORDER_BOTTOM;
  109.             break;
  110.         case CBRS_TOP:
  111.             dwNewStyle = CBRS_BORDER_TOP;
  112.             break;
  113.         case CBRS_RIGHT:
  114.             dwNewStyle = CBRS_BORDER_TOP|CBRS_BORDER_BOTTOM;
  115.             break;
  116.         case CBRS_BOTTOM:
  117.             dwNewStyle = CBRS_BORDER_BOTTOM;
  118.             break;
  119.         }
  120.  
  121.         // set new style if it matched one of the predefined border types
  122.         if (dwNewStyle != 0)
  123.         {
  124.             m_dwStyle &= ~(CBRS_BORDER_ANY);
  125.             m_dwStyle |= (dwNewStyle | CBRS_BORDER_3D);
  126.         }
  127.     }
  128.  
  129.     return TRUE;
  130. }
  131.  
  132. void CControlBar::SetBarStyle(DWORD dwStyle)
  133. {
  134.     ASSERT((dwStyle & CBRS_ALL) == dwStyle);
  135.  
  136.     EnableToolTips(dwStyle & CBRS_TOOLTIPS);
  137.  
  138.     if (m_dwStyle != dwStyle)
  139.     {
  140.         DWORD dwOldStyle = m_dwStyle;
  141.         m_dwStyle = dwStyle;
  142.         OnBarStyleChange(dwOldStyle, dwStyle);
  143.     }
  144. }
  145.  
  146. void CControlBar::OnBarStyleChange(DWORD, DWORD)
  147. {
  148.     // can be overridden in derived classes
  149. }
  150.  
  151. BOOL CControlBar::AllocElements(int nElements, int cbElement)
  152. {
  153.     ASSERT_VALID(this);
  154.     ASSERT(nElements >= 0 && cbElement >= 0);
  155.     ASSERT(m_pData != NULL || m_nCount == 0);
  156.  
  157.     // allocate new data if necessary
  158.     void* pData = NULL;
  159.     if (nElements > 0)
  160.     {
  161.         ASSERT(cbElement > 0);
  162.         if ((pData = calloc(nElements, cbElement)) == NULL)
  163.             return FALSE;
  164.     }
  165.     free(m_pData);      // free old data
  166.  
  167.     // set new data and elements
  168.     m_pData = pData;
  169.     m_nCount = nElements;
  170.  
  171.     return TRUE;
  172. }
  173.  
  174. #ifdef AFX_CORE3_SEG
  175. #pragma code_seg(AFX_CORE3_SEG)
  176. #endif
  177.  
  178. CControlBar::~CControlBar()
  179. {
  180.     ASSERT_VALID(this);
  181.  
  182.     DestroyWindow();    // avoid PostNcDestroy problems
  183.  
  184.     // also done in OnDestroy, but done here just in case
  185.     if (m_pDockSite != NULL)
  186.         m_pDockSite->RemoveControlBar(this);
  187.  
  188.     // free docking context
  189.     CDockContext* pDockContext = m_pDockContext;
  190.     m_pDockContext = NULL;
  191.     delete pDockContext;
  192.  
  193.     // free array
  194.     if (m_pData != NULL)
  195.     {
  196.         ASSERT(m_nCount != 0);
  197.         free(m_pData);
  198.     }
  199.  
  200.     _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
  201.     if (pThreadState->m_pLastStatus == this)
  202.     {
  203.         pThreadState->m_pLastStatus = NULL;
  204.         pThreadState->m_nLastStatus = -1;
  205.     }
  206. }
  207.  
  208. void CControlBar::PostNcDestroy()
  209. {
  210.     if (m_bAutoDelete)      // Automatic cleanup?
  211.         delete this;
  212. }
  213.  
  214. /////////////////////////////////////////////////////////////////////////////
  215. // Attributes
  216.  
  217. CSize CControlBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
  218. {
  219.     CSize size;
  220.     size.cx = (bStretch && bHorz ? 32767 : 0);
  221.     size.cy = (bStretch && !bHorz ? 32767 : 0);
  222.     return size;
  223. }
  224.  
  225. CSize CControlBar::CalcDynamicLayout(int, DWORD nMode)
  226. {
  227.     return CalcFixedLayout(nMode & LM_STRETCH, nMode & LM_HORZ);
  228. }
  229.  
  230. BOOL CControlBar::IsDockBar() const
  231. {
  232.     return FALSE;
  233. }
  234.  
  235. /////////////////////////////////////////////////////////////////////////////
  236. // Fly-by status bar help
  237.  
  238. #define ID_TIMER_WAIT   0xE000  // timer while waiting to show status
  239. #define ID_TIMER_CHECK  0xE001  // timer to check for removal of status
  240.  
  241. void CControlBar::ResetTimer(UINT nEvent, UINT nTime)
  242. {
  243.     KillTimer(ID_TIMER_WAIT);
  244.     KillTimer(ID_TIMER_CHECK);
  245.     VERIFY(SetTimer(nEvent, nTime, NULL));
  246. }
  247.  
  248. void CControlBar::OnTimer(UINT nIDEvent)
  249. {
  250.     if (GetKeyState(VK_LBUTTON) < 0)
  251.         return;
  252.  
  253.     _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
  254.  
  255.     // get current mouse position for hit test
  256.     CPoint point; GetCursorPos(&point);
  257.     ScreenToClient(&point);
  258.     int nHit = OnToolHitTest(point, NULL);
  259.     if (nHit >= 0)
  260.     {
  261.         // determine if status bar help should go away
  262.         CWnd* pParent = GetTopLevelParent();
  263.         if (!IsTopParentActive() || !pParent->IsWindowEnabled())
  264.             nHit = -1;
  265.  
  266.         // remove status help if capture is set
  267.         HWND hWndTip = pThreadState->m_pToolTip->GetSafeHwnd();
  268.         CWnd* pCapture = GetCapture();
  269.         if (pCapture != this && pCapture->GetSafeHwnd() != hWndTip &&
  270.             pCapture->GetTopLevelParent() == pParent)
  271.         {
  272.             nHit = -1;
  273.         }
  274.     }
  275.     else
  276.     {
  277.         pThreadState->m_nLastStatus = -1;
  278.     }
  279.  
  280.     // make sure it isn't over some other app's window
  281.     if (nHit >= 0)
  282.     {
  283.         ClientToScreen(&point);
  284.         HWND hWnd = ::WindowFromPoint(point);
  285.         if (hWnd == NULL || (hWnd != m_hWnd && !::IsChild(m_hWnd, hWnd) &&
  286.             pThreadState->m_pToolTip->GetSafeHwnd() != hWnd))
  287.         {
  288.             nHit = -1;
  289.             pThreadState->m_nLastStatus = -1;
  290.         }
  291.     }
  292.  
  293.     // handle the result
  294.     if (nHit < 0)
  295.     {
  296.         if (pThreadState->m_nLastStatus == -1)
  297.             KillTimer(ID_TIMER_CHECK);
  298.         SetStatusText(-1);
  299.     }
  300.  
  301.     // set status text after initial timeout
  302.     if (nIDEvent == ID_TIMER_WAIT)
  303.     {
  304.         KillTimer(ID_TIMER_WAIT);
  305.         if (nHit >= 0)
  306.             SetStatusText(nHit);
  307.     }
  308. }
  309.  
  310. BOOL CControlBar::SetStatusText(int nHit)
  311. {
  312.     CWnd* pOwner = GetOwner();
  313.  
  314.     _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
  315.     if (nHit == -1)
  316.     {
  317.         // handle reset case
  318.         pThreadState->m_pLastStatus = NULL;
  319.         if (m_nStateFlags & statusSet)
  320.         {
  321.             pOwner->SendMessage(WM_POPMESSAGESTRING, AFX_IDS_IDLEMESSAGE);
  322.             m_nStateFlags &= ~statusSet;
  323.             return TRUE;
  324.         }
  325.         KillTimer(ID_TIMER_WAIT);
  326.     }
  327.     else
  328.     {
  329.         // handle setnew case
  330.         if (!(m_nStateFlags & statusSet) || pThreadState->m_nLastStatus != nHit)
  331.         {
  332.             pThreadState->m_pLastStatus = this;
  333.             pOwner->SendMessage(WM_SETMESSAGESTRING, nHit);
  334.             m_nStateFlags |= statusSet;
  335.             ResetTimer(ID_TIMER_CHECK, 200);
  336.             return TRUE;
  337.         }
  338.     }
  339.     return FALSE;
  340. }
  341.  
  342. /////////////////////////////////////////////////////////////////////////////
  343. // Default control bar processing
  344.  
  345. BOOL CControlBar::PreTranslateMessage(MSG* pMsg)
  346. {
  347.     ASSERT_VALID(this);
  348.     ASSERT(m_hWnd != NULL);
  349.  
  350.     // allow tooltip messages to be filtered
  351.     if (CWnd::PreTranslateMessage(pMsg))
  352.         return TRUE;
  353.  
  354.     UINT message = pMsg->message;
  355.     CWnd* pOwner = GetOwner();
  356.  
  357.     // handle CBRS_FLYBY style (status bar flyby help)
  358.     if (((m_dwStyle & CBRS_FLYBY) ||
  359.         message == WM_LBUTTONDOWN || message == WM_LBUTTONUP) &&
  360.         ((message >= WM_MOUSEFIRST && message <= WM_MOUSELAST) ||
  361.          (message >= WM_NCMOUSEFIRST && message <= WM_NCMOUSELAST)))
  362.     {
  363.         _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
  364.  
  365.         // gather information about current mouse position
  366.         CPoint point = pMsg->pt;
  367.         ScreenToClient(&point);
  368.         TOOLINFO ti; memset(&ti, 0, sizeof(TOOLINFO));
  369.         ti.cbSize = sizeof(AFX_OLDTOOLINFO);
  370.         int nHit = OnToolHitTest(point, &ti);
  371.         if (ti.lpszText != LPSTR_TEXTCALLBACK)
  372.             free(ti.lpszText);
  373.         BOOL bNotButton =
  374.             message == WM_LBUTTONDOWN && (ti.uFlags & TTF_NOTBUTTON);
  375.         if (message != WM_LBUTTONDOWN && GetKeyState(VK_LBUTTON) < 0)
  376.             nHit = pThreadState->m_nLastStatus;
  377.  
  378.         // update state of status bar
  379.         if (nHit < 0 || bNotButton)
  380.         {
  381.             if (GetKeyState(VK_LBUTTON) >= 0 || bNotButton)
  382.             {
  383.                 SetStatusText(-1);
  384.                 KillTimer(ID_TIMER_CHECK);
  385.             }
  386.         }
  387.         else
  388.         {
  389.             if (message == WM_LBUTTONUP)
  390.             {
  391.                 SetStatusText(-1);
  392.                 ResetTimer(ID_TIMER_CHECK, 200);
  393.             }
  394.             else
  395.             {
  396.                 if ((m_nStateFlags & statusSet) || GetKeyState(VK_LBUTTON) < 0)
  397.                     SetStatusText(nHit);
  398.                 else if (nHit != pThreadState->m_nLastStatus)
  399.                     ResetTimer(ID_TIMER_WAIT, 300);
  400.             }
  401.         }
  402.         pThreadState->m_nLastStatus = nHit;
  403.     }
  404.  
  405.     // don't translate dialog messages when in Shift+F1 help mode
  406.     CFrameWnd* pFrameWnd = GetTopLevelFrame();
  407.     if (pFrameWnd != NULL && pFrameWnd->m_bHelpMode)
  408.         return FALSE;
  409.  
  410.     // since 'IsDialogMessage' will eat frame window accelerators,
  411.     //   we call all frame windows' PreTranslateMessage first
  412.     while (pOwner != NULL)
  413.     {
  414.         // allow owner & frames to translate before IsDialogMessage does
  415.         if (pOwner->PreTranslateMessage(pMsg))
  416.             return TRUE;
  417.  
  418.         // try parent frames until there are no parent frames
  419.         pOwner = pOwner->GetParentFrame();
  420.     }
  421.  
  422.     // filter both messages to dialog and from children
  423.     return PreTranslateInput(pMsg);
  424. }
  425.  
  426. LRESULT CControlBar::WindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam)
  427. {
  428.     ASSERT_VALID(this);
  429.  
  430.     LRESULT lResult;
  431.     switch (nMsg)
  432.     {
  433.     case WM_NOTIFY:
  434.     case WM_COMMAND:
  435.     case WM_DRAWITEM:
  436.     case WM_MEASUREITEM:
  437.     case WM_DELETEITEM:
  438.     case WM_COMPAREITEM:
  439.     case WM_VKEYTOITEM:
  440.     case WM_CHARTOITEM:
  441.         // send these messages to the owner if not handled
  442.         if (OnWndMsg(nMsg, wParam, lParam, &lResult))
  443.             return lResult;
  444.         else
  445.         {
  446.             // try owner next
  447.             lResult = GetOwner()->SendMessage(nMsg, wParam, lParam);
  448.  
  449.             // special case for TTN_NEEDTEXTA and TTN_NEEDTEXTW
  450.             if(nMsg == WM_NOTIFY)
  451.             {
  452.                 NMHDR* pNMHDR = (NMHDR*)lParam;
  453.                 if (pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW)
  454.                 {
  455.                     TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
  456.                     TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
  457.                     if ((pNMHDR->code == TTN_NEEDTEXTA && (!pTTTA->lpszText || !*pTTTA->lpszText)) ||
  458.                         (pNMHDR->code == TTN_NEEDTEXTW && (!pTTTW->lpszText || !*pTTTW->lpszText)))
  459.                     {
  460.                         // not handled by owner, so let bar itself handle it
  461.                         lResult = CWnd::WindowProc(nMsg, wParam, lParam);
  462.                     }
  463.                 }
  464.             }
  465.             return lResult;
  466.         }
  467.     }
  468.  
  469.     // otherwise, just handle in default way
  470.     lResult = CWnd::WindowProc(nMsg, wParam, lParam);
  471.     return lResult;
  472. }
  473.  
  474. LRESULT CControlBar::OnHelpHitTest(WPARAM, LPARAM lParam)
  475. {
  476.     ASSERT_VALID(this);
  477.  
  478.     int nID = OnToolHitTest((DWORD)lParam, NULL);
  479.     if (nID != -1)
  480.         return HID_BASE_COMMAND+nID;
  481.  
  482.     nID = _AfxGetDlgCtrlID(m_hWnd);
  483.     return nID != 0 ? HID_BASE_CONTROL+nID : 0;
  484. }
  485.  
  486. void CControlBar::OnWindowPosChanging(LPWINDOWPOS lpWndPos)
  487. {
  488.     // WINBUG: We call DefWindowProc here instead of CWnd::OnWindowPosChanging
  489.     //  (which calls CWnd::Default, which calls through the super wndproc)
  490.     //  because certain control bars that are system implemented (such as
  491.     //  CToolBar with TBSTYLE_FLAT) do not implement WM_WINDOWPOSCHANGING
  492.     //  correctly, causing repaint problems.  This code bypasses that whole
  493.     //  mess.
  494.     ::DefWindowProc(m_hWnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)lpWndPos);
  495.  
  496.     if (lpWndPos->flags & SWP_NOSIZE)
  497.         return;
  498.  
  499.     // invalidate borders on the right
  500.     CRect rect;
  501.     GetWindowRect(&rect);
  502.     CSize sizePrev = rect.Size();
  503.     int cx = lpWndPos->cx;
  504.     int cy = lpWndPos->cy;
  505.     if (cx != sizePrev.cx && (m_dwStyle & CBRS_BORDER_RIGHT))
  506.     {
  507.         rect.SetRect(cx-afxData.cxBorder2, 0, cx, cy);
  508.         InvalidateRect(&rect);
  509.         rect.SetRect(sizePrev.cx-afxData.cxBorder2, 0, sizePrev.cx, cy);
  510.         InvalidateRect(&rect);
  511.     }
  512.  
  513.     // invalidate borders on the bottom
  514.     if (cy != sizePrev.cy && (m_dwStyle & CBRS_BORDER_BOTTOM))
  515.     {
  516.         rect.SetRect(0, cy-afxData.cyBorder2, cx, cy);
  517.         InvalidateRect(&rect);
  518.         rect.SetRect(0, sizePrev.cy-afxData.cyBorder2, cx, sizePrev.cy);
  519.         InvalidateRect(&rect);
  520.     }
  521. }
  522.  
  523. int CControlBar::OnCreate(LPCREATESTRUCT lpcs)
  524. {
  525.     if (CWnd::OnCreate(lpcs) == -1)
  526.         return -1;
  527.  
  528.     if (m_dwStyle & CBRS_TOOLTIPS)
  529.         EnableToolTips();
  530.  
  531.     CFrameWnd *pFrameWnd = (CFrameWnd*)GetParent();
  532.     if (pFrameWnd->IsFrameWnd())
  533.     {
  534.         m_pDockSite = pFrameWnd;
  535.         m_pDockSite->AddControlBar(this);
  536.     }
  537.     return 0;
  538. }
  539.  
  540. void CControlBar::OnDestroy()
  541. {
  542.     _AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
  543.     if (pThreadState->m_pLastStatus == this)
  544.     {
  545.         SetStatusText(-1);
  546.         ASSERT(pThreadState->m_pLastStatus == NULL);
  547.     }
  548.  
  549.     if (m_pDockSite != NULL)
  550.     {
  551.         m_pDockSite->RemoveControlBar(this);
  552.         m_pDockSite = NULL;
  553.     }
  554.  
  555.     CWnd::OnDestroy();
  556. }
  557.  
  558. BOOL CControlBar::DestroyWindow()
  559. {
  560.     if (m_hWnd != NULL && IsFloating())
  561.         return GetDockingFrame()->DestroyWindow();
  562.     else
  563.         return CWnd::DestroyWindow();
  564. }
  565.  
  566. int CControlBar::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT nMsg)
  567. {
  568.     // call default when toolbar is not floating
  569.     if (!IsFloating())
  570.         return CWnd::OnMouseActivate(pDesktopWnd, nHitTest, nMsg);
  571.  
  572.     // special behavior when floating
  573.     ActivateTopParent();
  574.  
  575.     return MA_NOACTIVATE;   // activation already done
  576. }
  577.  
  578. void CControlBar::OnPaint()
  579. {
  580.     // background is already filled in gray
  581.     CPaintDC dc(this);
  582.  
  583.     // erase background now
  584.     if (IsVisible())
  585.         DoPaint(&dc);       // delegate to paint helper
  586. }
  587.  
  588. void CControlBar::EraseNonClient()
  589. {
  590.     // get window DC that is clipped to the non-client area
  591.     CWindowDC dc(this);
  592.     CRect rectClient;
  593.     GetClientRect(rectClient);
  594.     CRect rectWindow;
  595.     GetWindowRect(rectWindow);
  596.     ScreenToClient(rectWindow);
  597.     rectClient.OffsetRect(-rectWindow.left, -rectWindow.top);
  598.     dc.ExcludeClipRect(rectClient);
  599.  
  600.     // draw borders in non-client area
  601.     rectWindow.OffsetRect(-rectWindow.left, -rectWindow.top);
  602.     DrawBorders(&dc, rectWindow);
  603.  
  604.     // erase parts not drawn
  605.     dc.IntersectClipRect(rectWindow);
  606.     SendMessage(WM_ERASEBKGND, (WPARAM)dc.m_hDC);
  607.  
  608.     // draw gripper in non-client area
  609.     DrawGripper(&dc, rectWindow);
  610. }
  611.  
  612. HBRUSH CControlBar::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
  613. {
  614.     LRESULT lResult;
  615.     if (pWnd->SendChildNotifyLastMsg(&lResult))
  616.         return (HBRUSH)lResult;     // eat it
  617.  
  618.     // force black text on gray background all the time
  619.     if (!GrayCtlColor(pDC->m_hDC, pWnd->GetSafeHwnd(), nCtlColor,
  620.        afxData.hbrBtnFace, afxData.clrBtnText))
  621.         return (HBRUSH)Default();
  622.     return afxData.hbrBtnFace;
  623. }
  624.  
  625. void CControlBar::OnLButtonDown(UINT nFlags, CPoint pt)
  626. {
  627.     // only start dragging if clicked in "void" space
  628.     if (m_pDockBar != NULL && OnToolHitTest(pt, NULL) == -1)
  629.     {
  630.         // start the drag
  631.         ASSERT(m_pDockContext != NULL);
  632.         ClientToScreen(&pt);
  633.         m_pDockContext->StartDrag(pt);
  634.     }
  635.     else
  636.     {
  637.         CWnd::OnLButtonDown(nFlags, pt);
  638.     }
  639. }
  640.  
  641. void CControlBar::OnLButtonDblClk(UINT nFlags, CPoint pt)
  642. {
  643.     // only toggle docking if clicked in "void" space
  644.     if (m_pDockBar != NULL && OnToolHitTest(pt, NULL) == -1)
  645.     {
  646.         // start the drag
  647.         ASSERT(m_pDockContext != NULL);
  648.         m_pDockContext->ToggleDocking();
  649.     }
  650.     else
  651.     {
  652.         CWnd::OnLButtonDblClk(nFlags, pt);
  653.     }
  654. }
  655.  
  656. LRESULT CControlBar::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM)
  657. {
  658.     // handle delay hide/show
  659.     BOOL bVis = GetStyle() & WS_VISIBLE;
  660.     UINT swpFlags = 0;
  661.     if ((m_nStateFlags & delayHide) && bVis)
  662.         swpFlags = SWP_HIDEWINDOW;
  663.     else if ((m_nStateFlags & delayShow) && !bVis)
  664.         swpFlags = SWP_SHOWWINDOW;
  665.     m_nStateFlags &= ~(delayShow|delayHide);
  666.     if (swpFlags != 0)
  667.     {
  668.         SetWindowPos(NULL, 0, 0, 0, 0, swpFlags|
  669.             SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
  670.     }
  671.  
  672.     // the style must be visible and if it is docked
  673.     // the dockbar style must also be visible
  674.     if ((GetStyle() & WS_VISIBLE) &&
  675.         (m_pDockBar == NULL || (m_pDockBar->GetStyle() & WS_VISIBLE)))
  676.     {
  677.         CFrameWnd* pTarget = (CFrameWnd*)GetOwner();
  678.         if (pTarget == NULL || !pTarget->IsFrameWnd())
  679.             pTarget = GetParentFrame();
  680.         if (pTarget != NULL)
  681.             OnUpdateCmdUI(pTarget, (BOOL)wParam);
  682.     }
  683.     return 0L;
  684. }
  685.  
  686. void CControlBar::OnInitialUpdate()
  687. {
  688.     // update the indicators before becoming visible
  689.     OnIdleUpdateCmdUI(TRUE, 0L);
  690. }
  691.  
  692. DWORD CControlBar::RecalcDelayShow(AFX_SIZEPARENTPARAMS* lpLayout)
  693. {
  694.     ASSERT(lpLayout != NULL);
  695.  
  696.     // resize and reposition this control bar based on styles
  697.     DWORD dwStyle = (m_dwStyle & (CBRS_ALIGN_ANY|CBRS_BORDER_ANY)) |
  698.         (GetStyle() & WS_VISIBLE);
  699.  
  700.     // handle delay hide/show
  701.     if (m_nStateFlags & (delayHide|delayShow))
  702.     {
  703.         UINT swpFlags = 0;
  704.         if (m_nStateFlags & delayHide)
  705.         {
  706.             ASSERT((m_nStateFlags & delayShow) == 0);
  707.             if (dwStyle & WS_VISIBLE)
  708.                 swpFlags = SWP_HIDEWINDOW;
  709.         }
  710.         else
  711.         {
  712.             ASSERT(m_nStateFlags & delayShow);
  713.             if ((dwStyle & WS_VISIBLE) == 0)
  714.                 swpFlags = SWP_SHOWWINDOW;
  715.         }
  716.         if (swpFlags != 0)
  717.         {
  718.             // make the window seem visible/hidden
  719.             dwStyle ^= WS_VISIBLE;
  720.             if (lpLayout->hDWP != NULL)
  721.             {
  722.                 // clear delay flags
  723.                 m_nStateFlags &= ~(delayShow|delayHide);
  724.                 // hide/show the window if actually doing layout
  725.                 lpLayout->hDWP = ::DeferWindowPos(lpLayout->hDWP, m_hWnd, NULL,
  726.                     0, 0, 0, 0, swpFlags|
  727.                     SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
  728.             }
  729.         }
  730.         else
  731.         {
  732.             // clear delay flags -- window is already in correct state
  733.             m_nStateFlags &= ~(delayShow|delayHide);
  734.         }
  735.     }
  736.     return dwStyle; // return new style
  737. }
  738.  
  739. LRESULT CControlBar::OnSizeParent(WPARAM, LPARAM lParam)
  740. {
  741.     AFX_SIZEPARENTPARAMS* lpLayout = (AFX_SIZEPARENTPARAMS*)lParam;
  742.     DWORD dwStyle = RecalcDelayShow(lpLayout);
  743.  
  744.     if ((dwStyle & WS_VISIBLE) && (dwStyle & CBRS_ALIGN_ANY) != 0)
  745.     {
  746.         // align the control bar
  747.         CRect rect;
  748.         rect.CopyRect(&lpLayout->rect);
  749.  
  750.         CSize sizeAvail = rect.Size();  // maximum size available
  751.  
  752.         // get maximum requested size
  753.         DWORD dwMode = lpLayout->bStretch ? LM_STRETCH : 0;
  754.         if ((m_dwStyle & CBRS_SIZE_DYNAMIC) && m_dwStyle & CBRS_FLOATING)
  755.             dwMode |= LM_HORZ | LM_MRUWIDTH;
  756.         else if (dwStyle & CBRS_ORIENT_HORZ)
  757.             dwMode |= LM_HORZ | LM_HORZDOCK;
  758.         else
  759.             dwMode |=  LM_VERTDOCK;
  760.  
  761.         CSize size = CalcDynamicLayout(-1, dwMode);
  762.  
  763.         size.cx = min(size.cx, sizeAvail.cx);
  764.         size.cy = min(size.cy, sizeAvail.cy);
  765.  
  766.         if (dwStyle & CBRS_ORIENT_HORZ)
  767.         {
  768.             lpLayout->sizeTotal.cy += size.cy;
  769.             lpLayout->sizeTotal.cx = max(lpLayout->sizeTotal.cx, size.cx);
  770.             if (dwStyle & CBRS_ALIGN_TOP)
  771.                 lpLayout->rect.top += size.cy;
  772.             else if (dwStyle & CBRS_ALIGN_BOTTOM)
  773.             {
  774.                 rect.top = rect.bottom - size.cy;
  775.                 lpLayout->rect.bottom -= size.cy;
  776.             }
  777.         }
  778.         else if (dwStyle & CBRS_ORIENT_VERT)
  779.         {
  780.             lpLayout->sizeTotal.cx += size.cx;
  781.             lpLayout->sizeTotal.cy = max(lpLayout->sizeTotal.cy, size.cy);
  782.             if (dwStyle & CBRS_ALIGN_LEFT)
  783.                 lpLayout->rect.left += size.cx;
  784.             else if (dwStyle & CBRS_ALIGN_RIGHT)
  785.             {
  786.                 rect.left = rect.right - size.cx;
  787.                 lpLayout->rect.right -= size.cx;
  788.             }
  789.         }
  790.         else
  791.         {
  792.             ASSERT(FALSE);      // can never happen
  793.         }
  794.  
  795.         rect.right = rect.left + size.cx;
  796.         rect.bottom = rect.top + size.cy;
  797.  
  798.         // only resize the window if doing layout and not just rect query
  799.         if (lpLayout->hDWP != NULL)
  800.             AfxRepositionWindow(lpLayout, m_hWnd, &rect);
  801.     }
  802.     return 0;
  803. }
  804.  
  805. void CControlBar::DelayShow(BOOL bShow)
  806. {
  807.     m_nStateFlags &= ~(delayHide|delayShow);
  808.     if (bShow && (GetStyle() & WS_VISIBLE) == 0)
  809.         m_nStateFlags |= delayShow;
  810.     else if (!bShow && (GetStyle() & WS_VISIBLE) != 0)
  811.         m_nStateFlags |= delayHide;
  812. }
  813.  
  814. BOOL CControlBar::IsVisible() const
  815. {
  816.     if (m_nStateFlags & delayHide)
  817.         return FALSE;
  818.  
  819.     if ((m_nStateFlags & delayShow) || ((GetStyle() & WS_VISIBLE) != 0))
  820.         return TRUE;
  821.  
  822.     return FALSE;
  823. }
  824.  
  825. void CControlBar::DoPaint(CDC* pDC)
  826. {
  827.     ASSERT_VALID(this);
  828.     ASSERT_VALID(pDC);
  829.  
  830.     // paint inside client area
  831.     CRect rect;
  832.     GetClientRect(rect);
  833.     DrawBorders(pDC, rect);
  834.     DrawGripper(pDC, rect);
  835. }
  836.  
  837. void CControlBar::DrawBorders(CDC* pDC, CRect& rect)
  838. {
  839.     ASSERT_VALID(this);
  840.     ASSERT_VALID(pDC);
  841.  
  842.     DWORD dwStyle = m_dwStyle;
  843.     if (!(dwStyle & CBRS_BORDER_ANY))
  844.         return;
  845.  
  846.     // prepare for dark lines
  847.     ASSERT(rect.top == 0 && rect.left == 0);
  848.     CRect rect1, rect2;
  849.     rect1 = rect;
  850.     rect2 = rect;
  851.     COLORREF clr = afxData.bWin4 ? afxData.clrBtnShadow : afxData.clrWindowFrame;
  852.  
  853.     // draw dark line one pixel back/up
  854.     if (dwStyle & CBRS_BORDER_3D)
  855.     {
  856.         rect1.right -= CX_BORDER;
  857.         rect1.bottom -= CY_BORDER;
  858.     }
  859.     if (dwStyle & CBRS_BORDER_TOP)
  860.         rect2.top += afxData.cyBorder2;
  861.     if (dwStyle & CBRS_BORDER_BOTTOM)
  862.         rect2.bottom -= afxData.cyBorder2;
  863.  
  864.     // draw left and top
  865.     if (dwStyle & CBRS_BORDER_LEFT)
  866.         pDC->FillSolidRect(0, rect2.top, CX_BORDER, rect2.Height(), clr);
  867.     if (dwStyle & CBRS_BORDER_TOP)
  868.         pDC->FillSolidRect(0, 0, rect.right, CY_BORDER, clr);
  869.  
  870.     // draw right and bottom
  871.     if (dwStyle & CBRS_BORDER_RIGHT)
  872.         pDC->FillSolidRect(rect1.right, rect2.top, -CX_BORDER, rect2.Height(), clr);
  873.     if (dwStyle & CBRS_BORDER_BOTTOM)
  874.         pDC->FillSolidRect(0, rect1.bottom, rect.right, -CY_BORDER, clr);
  875.  
  876.     if (dwStyle & CBRS_BORDER_3D)
  877.     {
  878.         // prepare for hilite lines
  879.         clr = afxData.clrBtnHilite;
  880.  
  881.         // draw left and top
  882.         if (dwStyle & CBRS_BORDER_LEFT)
  883.             pDC->FillSolidRect(1, rect2.top, CX_BORDER, rect2.Height(), clr);
  884.         if (dwStyle & CBRS_BORDER_TOP)
  885.             pDC->FillSolidRect(0, 1, rect.right, CY_BORDER, clr);
  886.  
  887.         // draw right and bottom
  888.         if (dwStyle & CBRS_BORDER_RIGHT)
  889.             pDC->FillSolidRect(rect.right, rect2.top, -CX_BORDER, rect2.Height(), clr);
  890.         if (dwStyle & CBRS_BORDER_BOTTOM)
  891.             pDC->FillSolidRect(0, rect.bottom, rect.right, -CY_BORDER, clr);
  892.     }
  893.  
  894.     if (dwStyle & CBRS_BORDER_LEFT)
  895.         rect.left += afxData.cxBorder2;
  896.     if (dwStyle & CBRS_BORDER_TOP)
  897.         rect.top += afxData.cyBorder2;
  898.     if (dwStyle & CBRS_BORDER_RIGHT)
  899.         rect.right -= afxData.cxBorder2;
  900.     if (dwStyle & CBRS_BORDER_BOTTOM)
  901.         rect.bottom -= afxData.cyBorder2;
  902. }
  903.  
  904. #define CX_GRIPPER  3
  905. #define CY_GRIPPER  3
  906. #define CX_BORDER_GRIPPER 2
  907. #define CY_BORDER_GRIPPER 2
  908.  
  909. void CControlBar::DrawGripper(CDC* pDC, const CRect& rect)
  910. {
  911.     // only draw the gripper if not floating and gripper is specified
  912.     if ((m_dwStyle & (CBRS_GRIPPER|CBRS_FLOATING)) == CBRS_GRIPPER)
  913.     {
  914.         // draw the gripper in the border
  915.         if (m_dwStyle & CBRS_ORIENT_HORZ)
  916.         {
  917.             pDC->Draw3dRect(rect.left+CX_BORDER_GRIPPER,
  918.                 rect.top+m_cyTopBorder,
  919.                 CX_GRIPPER, rect.Height()-m_cyTopBorder-m_cyBottomBorder,
  920.                 afxData.clrBtnHilite, afxData.clrBtnShadow);
  921.         }
  922.         else
  923.         {
  924.             pDC->Draw3dRect(rect.left+m_cyTopBorder,
  925.                 rect.top+CY_BORDER_GRIPPER,
  926.                 rect.Width()-m_cyTopBorder-m_cyBottomBorder, CY_GRIPPER,
  927.                 afxData.clrBtnHilite, afxData.clrBtnShadow);
  928.         }
  929.     }
  930. }
  931.  
  932. // input CRect should be client rectangle size
  933. void CControlBar::CalcInsideRect(CRect& rect, BOOL bHorz) const
  934. {
  935.     ASSERT_VALID(this);
  936.     DWORD dwStyle = m_dwStyle;
  937.  
  938.     if (dwStyle & CBRS_BORDER_LEFT)
  939.         rect.left += afxData.cxBorder2;
  940.     if (dwStyle & CBRS_BORDER_TOP)
  941.         rect.top += afxData.cyBorder2;
  942.     if (dwStyle & CBRS_BORDER_RIGHT)
  943.         rect.right -= afxData.cxBorder2;
  944.     if (dwStyle & CBRS_BORDER_BOTTOM)
  945.         rect.bottom -= afxData.cyBorder2;
  946.  
  947.     // inset the top and bottom.
  948.     if (bHorz)
  949.     {
  950.         rect.left += m_cxLeftBorder;
  951.         rect.top += m_cyTopBorder;
  952.         rect.right -= m_cxRightBorder;
  953.         rect.bottom -= m_cyBottomBorder;
  954.         if ((m_dwStyle & (CBRS_GRIPPER|CBRS_FLOATING)) == CBRS_GRIPPER)
  955.             rect.left += CX_BORDER_GRIPPER+CX_GRIPPER+CX_BORDER_GRIPPER;
  956.     }
  957.     else
  958.     {
  959.         rect.left += m_cyTopBorder;
  960.         rect.top += m_cxLeftBorder;
  961.         rect.right -= m_cyBottomBorder;
  962.         rect.bottom -= m_cxRightBorder;
  963.         if ((m_dwStyle & (CBRS_GRIPPER|CBRS_FLOATING)) == CBRS_GRIPPER)
  964.             rect.top += CY_BORDER_GRIPPER+CY_GRIPPER+CY_BORDER_GRIPPER;
  965.     }
  966. }
  967.  
  968. /////////////////////////////////////////////////////////////////////////////
  969. // CControlBar diagnostics
  970.  
  971. #ifdef _DEBUG
  972. void CControlBar::AssertValid() const
  973. {
  974.     CWnd::AssertValid();
  975.  
  976.     ASSERT(m_nCount == 0 || m_pData != NULL);
  977.     ASSERT((m_dwStyle & CBRS_ALL) == m_dwStyle);
  978. }
  979.  
  980. void CControlBar::Dump(CDumpContext& dc) const
  981. {
  982.     CWnd::Dump(dc);
  983.  
  984.     dc << "\nm_cxLeftBorder = " << m_cxLeftBorder;
  985.     dc << "\nm_cxRightBorder = " << m_cxRightBorder;
  986.     dc << "\nm_cyTopBorder = " << m_cyTopBorder;
  987.     dc << "\nm_cyBottomBorder = " << m_cyBottomBorder;
  988.     dc << "\nm_cxDefaultGap = " << m_cxDefaultGap;
  989.     dc << "\nm_nCount = " << m_nCount;
  990.     dc << "\nm_bAutoDelete = " << m_bAutoDelete;
  991.  
  992.     dc << "\n";
  993. }
  994. #endif
  995.  
  996. /////////////////////////////////////////////////////////////////////////////
  997.