Caption.cpp
上传用户:kssdz899
上传日期:2007-01-08
资源大小:79k
文件大小:15k
- ////////////////////////////////////////////////////////////////
- //
- // class CCaption
- //
- // Generic caption painter. Handles WM_NCPAINT, WM_NCACTIVATE, etc. to
- // handle drawing custom captions. To use it:
- //
- // - call Install from your frame's OnCreate function.
- // - Set a custom CaptionBackground if desired
- // - Set custom TextAttributes if required
- //
- // Derive from this class for custom caption layouts.
- //
- // If you are drawing custom caption buttons, you must handle WM_NCLBUTTONDOWN & co.
- // yourself. CCaption does not handle the mouse for custom caption buttons.
- //
- // Author: Dave Lorde (dlorde@cix.compulink.co.uk)
- //
- // Copyright 1999
- //
- // - loosely based on a 1997 Microsoft Systems Journal
- // C++ Q&A article by Paul DiLascia.
- //
- ////////////////////////////////////////////////////////////////
- //
- #include "StdAfx.h"
- #include "Caption.h"
- #include "CaptionBackground.h"
- #include "CaptionTextAttributes.h"
- #include "SuppressStyle.h"
- #include <algorithm>
- using std::_cpp_max;
- using std::_cpp_min;
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #undef THIS_FILE
- static char THIS_FILE[] = __FILE__;
- #endif
- const int MinLuminosity = 90; // good from trial & error
- const COLORREF ColorWhite = RGB(255,255,255);
- IMPLEMENT_DYNAMIC(CCaption, CSubclassWnd);
- ////////
- // Default Constructor
- //
- CCaption::CCaption()
- :m_pBackground(0), m_pTextAttributes(0)
- {
- Invalidate();
- }
- CCaption::~CCaption()
- {
- delete m_pBackground, m_pBackground = 0;
- delete m_pTextAttributes, m_pTextAttributes = 0;
- }
- //////////////////
- // Install caption handler.
- //
- BOOL CCaption::Install(CFrameWnd* pFrameWnd)
- {
- ASSERT_KINDOF(CFrameWnd, pFrameWnd);
- return HookWindow(pFrameWnd);
- }
- //////////////////
- // Replace default background painter.
- //
- void CCaption::SetBackground(CCaptionBackground* pBackground)
- {
- if (m_pBackground)
- delete m_pBackground;
- m_pBackground = pBackground;
- Refresh();
- }
- //////////////////
- // Replace default text attributes
- //
- void CCaption::SetTextAttributes(CCaptionTextAttributes* pTextAttributes)
- {
- if (m_pTextAttributes)
- delete m_pTextAttributes;
- m_pTextAttributes = pTextAttributes;
- Refresh();
- }
- /////////////////
- // Regenerate and display caption
- //
- void CCaption::Refresh()
- {
- Invalidate();
- PaintCaption();
- }
- //////////////////
- // Ensure caption bitmaps are repainted next time through OnNcPaint()
- //
- void CCaption::Invalidate()
- {
- m_szCaption = CSize(0,0);
- }
- //////////////////
- // Message handler handles caption-related messages
- //
- LRESULT CCaption::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
- {
- switch (msg)
- {
- case WM_NCPAINT:
- OnNcPaint(HRGN(wp));
- return 0;
- case WM_NCACTIVATE:
- return OnNcActivate(wp);
- case WM_SETTEXT:
- OnSetText((LPCTSTR)lp);
- return 0;
- case WM_SYSCOLORCHANGE:
- case WM_SETTINGCHANGE:
- OnColorChange();
- return 0;
- // case WM_SIZE:
- // OnSize( (UINT)wp, HIWORD(lp), LOWORD(lp) );
- // return 0;
- }
- // We don't handle it: pass along
- return CSubclassWnd::WindowProc(msg, wp, lp);
- }
- /////////////////
- // Handle WM_NCPAINT for main window
- //
- void CCaption::OnNcPaint(HRGN hRgn)
- {
- ASSERT_VALID(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- CRect rc = GetCaptionRect(); // caption rectangle in window coords
- CRect rcWin;
- wnd.GetWindowRect(&rcWin); // .. get window rect
- rc += rcWin.TopLeft(); // convert caption rect to screen coords
- // Don't bother painting if the caption doesn't lie within the region.
- //
- if ((WORD)hRgn > 1 && !::RectInRegion(hRgn, &rc))
- {
- Default(); // just do default thing
- return; // and quit
- }
- // Exclude caption from update region
- //
- HRGN hRgnCaption = ::CreateRectRgnIndirect(&rc);
- HRGN hRgnNew = ::CreateRectRgnIndirect(&rc);
- if ((WORD)hRgn > 1)
- {
- // wParam is a valid region: subtract caption from it
- //
- ::CombineRgn(hRgnNew, hRgn, hRgnCaption, RGN_DIFF);
- }
- else
- {
- // wParam is not a valid region: create one that's the whole
- // window minus the caption bar
- //
- HRGN hRgnAll = ::CreateRectRgnIndirect(&rcWin);
- CombineRgn(hRgnNew, hRgnAll, hRgnCaption, RGN_DIFF);
- DeleteObject(hRgnAll);
- }
- // Call Windows to do WM_NCPAINT with altered update region
- //
- MSG& msg = AfxGetThreadState()->m_lastSentMsg;
- WPARAM savewp = msg.wParam; // save original wParam
- msg.wParam = (WPARAM)hRgnNew; // set new region for DefWindowProc
- Default(); // Normal message handling
- DeleteObject(hRgnCaption); // clean up
- DeleteObject(hRgnNew); // ...
- msg.wParam = savewp; // restore original wParam
- PaintCaption(); // Now paint our special caption
- }
- //////////////////
- // Handle WM_NCACTIVATE for main window
- //
- BOOL CCaption::OnNcActivate(BOOL bActive)
- {
- ASSERT_VALID(m_pWndHooked);
- CFrameWnd& frame = *((CFrameWnd*)m_pWndHooked);
- ASSERT_KINDOF(CFrameWnd, &frame);
- // Mimic MFC kludge to stay active if WF_STAYACTIVE bit is on
- //
- if (frame.m_nFlags & WF_STAYACTIVE)
- bActive = TRUE;
- if (!frame.IsWindowEnabled()) // but not if disabled
- bActive = FALSE;
- if (bActive == m_bActive)
- return TRUE; // nothing to do
- // In case this is a MDI app, manually activate/paint active MDI child
- // window, because Windows won't do it if parent frame is invisible.
- // Must do this BEFORE calling Default, or it will not work.
- //
- CFrameWnd* pActiveFrame = frame.GetActiveFrame();
- if (pActiveFrame != &frame)
- {
- pActiveFrame->SendMessage(WM_NCACTIVATE, bActive);
- pActiveFrame->SendMessage(WM_NCPAINT);
- }
- // Turn WS_VISIBLE off before calling DefWindowProc,
- // so DefWindowProc won't paint and thereby cause flicker.
- //
- {
- SuppressStyle ss(frame.GetSafeHwnd(), WS_VISIBLE);
- MSG& msg = AfxGetThreadState()->m_lastSentMsg;
- msg.wParam = bActive;
- Default(); // Normal message handling
- }
- // At this point, nothing has happened (since WS_VISIBLE was off).
- // Now it's time to paint.
- //
- m_bActive = bActive; // update state
- frame.SendMessage(WM_NCPAINT); // paint non-client area (frame too)
- return TRUE; // done OK
- }
- //////////////////
- // Handle WM_SETTEXT for main window
- //
- void CCaption::OnSetText(LPCTSTR)
- {
- ASSERT_VALID(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- // Turn WS_VISIBLE style off before calling Windows to
- // set the text. Reset to visible afterwards
- {
- SuppressStyle ss(wnd.GetSafeHwnd(), WS_VISIBLE);
- Default(); // Normal message handling
- }
- // wnd.SendMessage(WM_NCPAINT); // paint non-client (frame)
- Refresh();
- }
- //////////
- // Ensure caption is repainted when system colors change
- //
- void CCaption::OnColorChange()
- {
- Default(); // Normal message handling
- Refresh();
- }
- //////////////////
- // Paint custom caption. m_bActive flag tells whether frame is active or not.
- // Just blast the bitmap to the title bar.
- //
- void CCaption::PaintCaption()
- {
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- // Get caption DC and rectangle
- //
- CWindowDC dcWin(&wnd); // window DC
- CDC dc; // memory DC
- dc.CreateCompatibleDC(&dcWin); // ...create it
- CRect rc = GetCaptionRect(); // get caption rectangle
- if (rc.Size() != m_szCaption) // if size changed:
- {
- m_bmCaption[0].DeleteObject(); // invalidate bitmaps
- m_bmCaption[1].DeleteObject(); // ...
- m_szCaption = rc.Size(); // update new size
- }
- // Get active/inactive bitmap & determine if needs to be regenerated
- //
- CBitmap& bm = m_bmCaption[m_bActive != 0]; // get bitmap
- BOOL bPaintIt = FALSE; // paint new bitmap?
- if (!HBITMAP(bm))
- { // no bitmap, so create one :
- bm.CreateCompatibleBitmap(&dcWin, rc.Width(), rc.Height());
- bPaintIt = TRUE; // and paint it
- }
- CBitmap* pOldBitmap = dc.SelectObject(&bm); // select bitmap into memory DC
- // If bitmap needs painting, do it
- //
- if (bPaintIt)
- PaintBitmap(&dc);
- // blast bits to screen
- //
- dcWin.BitBlt(rc.left, rc.top, rc.Width(), rc.Height(), &dc, 0, 0, SRCCOPY);
- dc.SelectObject(pOldBitmap); // restore DC
- }
- /////////
- // Paint the caption bitmap. Override for custom caption painters.
- // Note: SDK DrawCaption() method not used, so as to provide useful
- // virtual functions for derived caption painters
- //
- void CCaption::PaintBitmap(CDC* pDC)
- {
- PaintBackground(pDC);
- PaintIcon(pDC);
- PaintButtons(pDC);
- PaintText(pDC);
- PaintLowerBorder(pDC);
- }
- ///////////
- // Delegate background painting to CCaptionBackground class
- //
- void CCaption::PaintBackground(CDC* pDC)
- {
- GetBackground()->Paint(pDC, m_szCaption, m_bActive);
- }
- ////////
- // Paint the border between the bottom of the caption rectangle and the
- // shadow on the top of the client rectangle
- //
- void CCaption::PaintLowerBorder(CDC* pDC)
- {
- int x = 0;
- int y = m_szCaption.cy - GetSystemMetrics(SM_CYBORDER);
- int h = m_szCaption.cy;
- int w = m_szCaption.cx;
- CCaptionBackground::PaintRect(pDC, x, y, w, h, GetSysColor(COLOR_3DFACE));
- }
- ////////
- // Calculate the caption text clipping rect
- //
- CRect CCaption::GetTextRect()
- {
- CRect textRect = GetCaptionRect();
- textRect.left += GetIconWidth();
- textRect.right -= GetButtonsWidth() + 4;
- textRect.top -= 2;
- return textRect;
- }
- ///////////
- // Draw the caption text onto the bitmap
- //
- void CCaption::PaintText(CDC* pDC)
- {
- pDC->SetBkMode(TRANSPARENT); // draw on top of our background
- CString text;
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- wnd.GetWindowText(text);
- // Set text color
- COLORREF textColor = GetTextColor(m_bActive);
- pDC->SetTextColor(textColor);
- // Get caption font and select into DC
- CFont* pOldFont = pDC->SelectObject(GetFont(m_bActive));
- CRect textRect = GetTextRect();
- pDC->DrawText(text, textRect, DT_LEFT|DT_END_ELLIPSIS);
- // Restore DC
- pDC->SelectObject(pOldFont);
- }
- ////////////////
- // Draw caption icon if valid DC is provided. Returns effective width of icon.
- //
- int CCaption::PaintIcon(CDC* pDC)
- {
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- ////////////
- // If there's no icon or system menu, don't draw one
- //
- if (!(wnd.GetStyle() & WS_SYSMENU))
- return 0;
- //////////////
- // Within the basic button rectangle, Windows 95 uses a 1 or 2 pixel border
- // Icon has 2 pixel border on left, 1 pixel on top/bottom, 0 right
- //
- int cxIcon = GetSystemMetrics(SM_CXSIZE);
- CRect rc(0, 0, cxIcon, GetSystemMetrics(SM_CYSIZE));
- rc.DeflateRect(0,1);
- rc.left += 2;
- if (pDC != 0)
- {
- DrawIconEx(pDC->m_hDC, rc.left, rc.top,
- (HICON)GetClassLong(wnd.m_hWnd, GCL_HICONSM),
- rc.Width(), rc.Height(), 0, NULL, DI_NORMAL);
- }
- return cxIcon;
- }
- //////////////
- // Helper
- //
- int CCaption::GetIconWidth()
- {
- return PaintIcon();
- }
- ////////////////
- // Draw min, max/restore, close buttons.
- // Returns total width of buttons drawn.
- //
- int CCaption::PaintButtons(CDC* pDC)
- {
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- DWORD dwStyle = wnd.GetStyle();
- if (!(dwStyle & WS_CAPTION))
- return 0;
- int cxIcon = GetSystemMetrics(SM_CXSIZE);
- int cyIcon = GetSystemMetrics(SM_CYSIZE);
- // Draw caption buttons. These are all drawn inside a rectangle
- // of dimensions SM_CXSIZE by SM_CYSIZE
- CRect captRect = GetCaptionRect();
- CRect rc(0, 0, cxIcon, cyIcon);
- rc += CPoint(captRect.Width() - cxIcon, 0); // move right
- //////////////
- // Close box has a 2 pixel border on all sides but left, which is zero
- //
- rc.DeflateRect(0,2);
- rc.right -= 2;
- if (pDC)
- pDC->DrawFrameControl(&rc, DFC_CAPTION, DFCS_CAPTIONCLOSE);
- //////////////
- // Max/restore button is like close box; just shift rectangle left
- // Also does help button, if any.
- //
- BOOL bMaxBox = dwStyle & WS_MAXIMIZEBOX;
- if (bMaxBox || (wnd.GetExStyle() & WS_EX_CONTEXTHELP))
- {
- rc -= CPoint(cxIcon, 0);
- if (pDC)
- pDC->DrawFrameControl(&rc, DFC_CAPTION,
- bMaxBox ? (wnd.IsZoomed() ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX) :
- DFCS_CAPTIONHELP);
- }
- ///////////////
- // Minimize button has 2 pixel border on all sides but right.
- //
- if (dwStyle & WS_MINIMIZEBOX)
- {
- rc -= CPoint(cxIcon - 2,0);
- if (pDC)
- pDC->DrawFrameControl(&rc, DFC_CAPTION,
- (wnd.IsIconic() ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMIN));
- }
- return captRect.Width() - (rc.left - 2);
- }
- int CCaption::GetButtonsWidth()
- {
- return PaintButtons();
- }
- ///////////////
- // Calculate the caption rectangle relative to the window frame
- //
- CRect CCaption::GetCaptionRect()
- {
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- CRect captionRect;
- wnd.GetWindowRect(captionRect);
- captionRect -= CPoint(captionRect.left, captionRect.top); // shift origin to (0,0)
- CSize szFrame = GetFrameSize();
- captionRect.InflateRect(-szFrame.cx, -szFrame.cy);
- captionRect.bottom = captionRect.top + GetSystemMetrics(SM_CYCAPTION); // height of caption
- return captionRect;
- }
- ///////
- // Return width and height of window frame elements
- //
- CSize CCaption::GetFrameSize() const
- {
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- ////////////
- // Get size of frame around window
- //
- DWORD dwStyle = wnd.GetStyle();
- CSize szFrame = (dwStyle & WS_THICKFRAME) ?
- CSize(GetSystemMetrics(SM_CXSIZEFRAME),
- GetSystemMetrics(SM_CYSIZEFRAME)) :
- CSize(GetSystemMetrics(SM_CXFIXEDFRAME),
- GetSystemMetrics(SM_CYFIXEDFRAME));
- return szFrame;
- }
- COLORREF CCaption::GetTextColor(BOOL bActive)
- {
- COLORREF textColor = bActive ? GetTextAttributes()->GetActiveColor() :
- GetTextAttributes()->GetInactiveColor();
- //////////////////
- // Uncomment these lines to automatically set the text colour to white
- // when the background is too dark
- //
- // if (GetLuminosity(textColor) < MinLuminosity)
- // textColor = ColorWhite;
- return textColor;
- }
- ///////////////
- // Delegate to CCaptionTextAttributes for appropriate font
- //
- CFont* CCaption::GetFont(BOOL m_bActive)
- {
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- if (!wnd.IsIconic())
- return m_bActive ? GetTextAttributes()->GetActiveFont() : GetTextAttributes()->GetInactiveFont();
- else
- return CCaptionTextAttributes::GetSystemFont();
- }
- ////////////
- // Lazy construction of text attributes object
- //
- CCaptionTextAttributes* CCaption::GetTextAttributes()
- {
- if (!m_pTextAttributes)
- m_pTextAttributes = new CCaptionTextAttributes();
-
- return m_pTextAttributes;
- }
- ////////////
- // Lazy construction of background object
- //
- CCaptionBackground* CCaption::GetBackground()
- {
- if (!m_pBackground)
- m_pBackground = new CCaptionBackground();
- return m_pBackground;
- }
- //////////////////
- // Helper function to compute the luminosity for an RGB color.
- // Measures how bright the color is. I use this so I can draw the caption
- // text using the user's chosen color, unless it's too dark. See MSDN for
- // definition of luminosity and how to compute it.
- //
- int CCaption::GetLuminosity(COLORREF color) const
- {
- const int HlsMax = 240; // This is what Display Properties uses
- const int RgbMax = 255; // max r/g/b value is 255
- int r = GetRValue(color);
- int g = GetGValue(color);
- int b = GetBValue(color);
- int rgbMax = _MAX( _MAX(r, g), b);
- int rgbMin = _MIN( _MIN(r, g), b);
- return (((rgbMax + rgbMin) * HlsMax) + RgbMax ) / (2 * RgbMax);
- }