MultiLineCaption.cpp
上传用户:zhoushen
上传日期:2022-06-15
资源大小:84k
文件大小:12k
- ////////////////////////////////////////////////////////////////
- //
- // class CMultiLineCaption
- //
- // Multi-line auto-wrap caption painter.
- //
- // To use:
- // - call Install from your frame's OnCreate function.
- // - Set a custom CaptionBackground if desired
- // - Set custom TextAttributes if required
- //
- // If you are drawing custom caption buttons, you must handle WM_NCLBUTTONDOWN & co.
- // yourself. CMultiLineCaption does not handle the mouse for custom caption buttons.
- //
- // Author: Dave Lorde (dlorde@cix.compulink.co.uk)
- //
- // Copyright 1999
- //
- ////////////////////////////////////////////////////////////////
- //
- #include "StdAfx.h"
- #include "MultiLineCaption.h"
- #include "CaptionBackground.h"
- #include "CaptionTextAttributes.h"
- #include "SuppressStyle.h"
- #include "AutoSelector.h"
- #include <algorithm>
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #undef THIS_FILE
- static char THIS_FILE[] = __FILE__;
- #endif
- using std::_cpp_max;
- using std::_cpp_min;
- IMPLEMENT_DYNAMIC(CMultiLineCaption, CCaption);
- namespace
- {
- const int TextFormat = DT_CENTER|DT_WORDBREAK;
- const int SingleLineTextFormat = DT_LEFT|DT_END_ELLIPSIS;
- const int DrawTextFudgeFactor = 2;
- const int TextLowerBorder = 2;
- const UINT RestoreFromMinimized = 0x8124; // WindowPosChanged flags
- const CString ellipsis("...");
- }
- ///////////////
- // Constructor
- //
- CMultiLineCaption::CMultiLineCaption(int maxLines)
- :m_MaxLines(maxLines > 0 ? maxLines : 1),
- m_InitialShow(true),
- m_ActiveSysColor(GetSysColor(COLOR_ACTIVECAPTION)),
- m_InactiveSysColor(GetSysColor(COLOR_INACTIVECAPTION))
- {
- }
- //////////
- // Adjust maximum lines for title wrapping, and refresh
- //
- void CMultiLineCaption::SetMaxLines(int maxLines)
- {
- m_MaxLines = maxLines > 0 ? maxLines : 1;
- AdjustHeight();
- Repaint(); // Redraw caption
- }
- ////////////////
- // Return maximum permitted lines for title
- //
- int CMultiLineCaption::GetMaxLines() const
- {
- return m_MaxLines;
- }
- //////////////////
- // Message handler handles caption size-related messages
- //
- LRESULT CMultiLineCaption::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
- {
- switch (msg)
- {
- case WM_NCACTIVATE:
- return OnNcActivate(wp);
- ///////////
- // Adjust height of client area according to text wrapping
- //
- case WM_SETTEXT:
- OnSetText((LPCTSTR)lp);
- return 0;
- ///////////
- // Change colours if using system colours and SysColorChange
- case WM_NCPAINT:
- OnNcPaint(HRGN(wp));
- return 0;
-
- ///////////
- // Adjust height of client area according to required caption height
- //
- case WM_NCCALCSIZE:
- return OnNcCalcSize((BOOL)wp, (LPNCCALCSIZE_PARAMS)lp);
- ////////////
- // Ensure caption repaints smoothly when sized
- //
- case WM_SIZE:
- OnSize( (UINT)wp, LOWORD(lp), HIWORD(lp) );
- return 0;
- ///////////
- // Prevent multi-line caption being overdrawn by client area
- //
- case WM_GETMINMAXINFO:
- OnGetMinMaxInfo((LPMINMAXINFO)lp);
- return 0;
- ///////////
- // Refresh when activated or inactivated
- //
- case WM_MDIACTIVATE:
- OnMDIActivate();
- return 0;
- ///////////
- // Refresh when sizing stopped
- //
- case WM_EXITSIZEMOVE:
- OnExitSizeMove();
- return 0;
- ///////////
- // Refresh caption when window is tiled
- //
- case WM_WINDOWPOSCHANGED:
- OnWindowPosChanged( (WINDOWPOS*) lp);
- return 0;
- }
- ////////////
- // We don't handle it: pass to base class
- //
- return CCaption::WindowProc(msg, wp, lp);
- }
- //////////////////
- // Handle WM_NCACTIVATE for main window
- //
- BOOL CMultiLineCaption::OnNcActivate(BOOL bActive)
- {
- BOOL result = CCaption::OnNcActivate(bActive);
- Repaint(); // Redraw caption
- return result;
- }
- void CMultiLineCaption::OnSetText(LPCTSTR lpStr)
- {
- CCaption::OnSetText(lpStr);
-
- AdjustHeight();
- Repaint(); // Redraw caption
- }
- void CMultiLineCaption::OnNcPaint(HRGN hRgn)
- {
- if (GetBackground()->IsUsingSystemColors())
- {
- if (m_ActiveSysColor != GetSysColor(COLOR_ACTIVECAPTION) ||
- m_InactiveSysColor != GetSysColor(COLOR_INACTIVECAPTION))
- {
- m_ActiveSysColor = GetSysColor(COLOR_ACTIVECAPTION);
- m_InactiveSysColor = GetSysColor(COLOR_INACTIVECAPTION);
- Repaint();
- }
- }
- CCaption::OnNcPaint(hRgn);
- }
- void CMultiLineCaption::OnSize( UINT nType, int cx, int cy )
- {
- Default();
- Refresh();
- }
- void CMultiLineCaption::OnExitSizeMove()
- {
- Default();
- Refresh();
- }
- /////////////
- // Paint wrapped header text in rectangle
- //
- void CMultiLineCaption::PaintText(CDC* pDC)
- {
- ASSERT(m_pWndHooked);
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- // For minimised windows, forget all the multi-line stuff
- if (wnd.IsIconic())
- {
- CCaption::PaintText(pDC);
- return;
- }
- pDC->SetBkMode(TRANSPARENT); // draw on top of our background
- // Get window text to paint
- //
- CString text;
- wnd.GetWindowText(text);
- // Set text color into device context
- //
- COLORREF textColor = GetTextColor(m_bActive);
- pDC->SetTextColor(textColor);
- // Get the rect to paint the text in
- //
- CRect paintRect(CalcTextRect(m_MaxLines > 1 ? TextFormat : SingleLineTextFormat));
- // Get caption font and select into DC
- //
- AutoSelector a(pDC, GetFont(m_bActive));
- // Paint text into rectangle
- //
- if (m_MaxLines > 1)
- {
- int height = pDC->DrawText(text, paintRect, TextFormat|DT_TOP);
- // Unfortunately, DT_END_ELLIPSIS doesn't work with DT_WORDBREAK, so we must handle
- // ellipsis manually... (unless you know better)
- //
- if (height > paintRect.Height()) // Text won't fit, so output truncation ellipsis
- {
- int lenEllipsis = pDC->GetTextExtent(ellipsis).cx;
- paintRect.left = paintRect.right;
- paintRect.right += lenEllipsis;
- paintRect.top = paintRect.bottom - (GetLineHeight(pDC) + 2);
- pDC->DrawText(ellipsis, paintRect, DT_SINGLELINE|DT_LEFT);
- }
- }
- else // m_MaxLines == 1
- {
- pDC->DrawText(text, paintRect, SingleLineTextFormat);
- }
- }
- //////////////
- // Refresh caption when window is tiled
- //
- void CMultiLineCaption::OnWindowPosChanged( WINDOWPOS* lpwndpos )
- {
- Default(); // Let Windows handle it
- // These flags together seem unique to windows being tiled
- //
- UINT tileFlags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOCOPYBITS;
-
- if ((lpwndpos->flags & tileFlags) == tileFlags)
- {
- Refresh();
- }
- }
- void CMultiLineCaption::OnGetMinMaxInfo( MINMAXINFO FAR* lpMMI )
- {
- Default(); // Let Windows handle it
- // Ensure min height is correct - we mustn't allow a height less
- // than the caption height
- //
- lpMMI->ptMinTrackSize.y =
- max(lpMMI->ptMinTrackSize.y, GetCaptionHeight() + GetFrameSize().cy);
- }
- ////////////////
- // Refresh on activate/deactivate for windows with different font
- // sizes for active and inactive caption
- //
- void CMultiLineCaption::OnMDIActivate()
- {
- AdjustHeight();
- Repaint(); // Redraw caption
- Default(); // Let Windows handle it
- }
- // Call Windows to do WM_NCCALCSIZE then set the top of the new client area to allow for
- // multi-line caption
- //
- LRESULT CMultiLineCaption::OnNcCalcSize( BOOL bCalcValidRects, LPNCCALCSIZE_PARAMS lpncsp )
- {
- LRESULT res = Default(); // Let Windows handle it
- CString text;
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- wnd.GetWindowText(text);
- // When restoring from minimised (old client area left and right are both zero), CalcTextRect()
- // won't calculate the new client area correctly because the current client area is not valid.
- // In this case, skip it and let OnSize() trigger this function when the client area is valid.
- //
- BOOL wasIconic = lpncsp->rgrc[2].left == lpncsp->rgrc[2].right;
- BOOL isZoomed = wnd.IsZoomed();
- if (text.GetLength() > 0 && !isZoomed && !wasIconic)
- {
- // LPNCCALCSIZE_PARAMS are relative to parent client area (or screen origin if no parent),
- // so add vertical offset of child frame into parent client area
- //
- lpncsp->rgrc[0].top = GetChildOffset() + GetCaptionHeight();
- }
- return res;
- }
- ///////////////
- // Return the rectangle occupied by caption text with the specified DrawText() format
- //
- CRect CMultiLineCaption::CalcTextRect(int textFormat)
- {
- CString text;
- ASSERT(m_pWndHooked);
- m_pWndHooked->GetWindowText(text);
- // Get caption font and select into window DC
- //
- CWindowDC dc(m_pWndHooked);
- AutoSelector a(&dc, GetFont(m_bActive));
- // Size caption to exclude buttons & icon
- //
- CRect textRect = GetCaptionRect();
- textRect.left += GetIconWidth(); // start text after icon
- textRect.right -= GetButtonsWidth() + 4; // end text before buttons
- // Use DrawText to calculate the height necessary to contain the text
- //
- int width = textRect.Width();
- int height = dc.DrawText(text, textRect, textFormat | DT_CALCRECT | DT_EXTERNALLEADING);
- // Restrict to specified maximun height
- //
- int maxHeight = GetLineHeight(&dc) * m_MaxLines;
- if (height > maxHeight)
- textRect.bottom = textRect.top + maxHeight;
- // Restrict to minimum height
- //
- int minTextHeight = GetSystemMetrics(SM_CYCAPTION) - 6;
- if (textRect.Height() < minTextHeight)
- textRect.bottom = textRect.top + minTextHeight;
- // I'll work out why I need this when I get more time... :-)
- textRect.top -= DrawTextFudgeFactor;
- // DrawText word-wrap fails to clip for very narrow rects, so ignore if necessary
- //
- if (textRect.Width() > width)
- textRect.right = textRect.left + width;
- return textRect;
- }
- ///////////
- // Returns the height of a line of text in the specified device context
- //
- LONG CMultiLineCaption::GetLineHeight(CDC* pDC)
- {
- TEXTMETRIC textMetric;
- pDC->GetOutputTextMetrics(&textMetric);
- return textMetric.tmHeight;
- }
- ///////////
- // Returns the total caption height needed to contain the text
- //
- int CMultiLineCaption::GetCaptionHeight()
- {
- return GetFrameSize().cy +
- CalcTextRect(m_MaxLines > 1 ? TextFormat : SingleLineTextFormat).bottom +
- TextLowerBorder;
- }
- ///////////
- // Return vertical offset of child frame into parent client area (for OnNcCalcSize).
- //
- int CMultiLineCaption::GetChildOffset()
- {
- CRect parentRect(0,0,0,0);
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- CWnd* pParent = wnd.GetParent();
- if (pParent)
- {
- pParent->GetClientRect(parentRect );
- pParent->ClientToScreen(parentRect);
- }
- CRect hookedRect;
- wnd.GetWindowRect( &hookedRect );
- return hookedRect.top - parentRect.top;
- }
- /////////////
- // Override of base class function:
- // Return caption drawing rectangle, the lower edge abutting the
- // client area (as adjusted in OnNcCalcSize())
- //
- CRect CMultiLineCaption::GetCaptionRect()
- {
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- // When minimised, draw normal size caption
- if (wnd.IsIconic())
- return CCaption::GetCaptionRect();
- // Get client rect in screen co-ordinates
- //
- CRect captionRect;
- wnd.GetClientRect(captionRect);
- wnd.ClientToScreen(captionRect);
- // Convert client rect to window co-ordinates
- //
- CRect windowRect;
- wnd.GetWindowRect(windowRect);
- captionRect -= windowRect.TopLeft();
- // Set top and botton for caption
- //
- captionRect.bottom = captionRect.top; // bottom of caption is top of client window
- captionRect.top = GetFrameSize().cy; // top of caption is window rect top + frame thickness
- return captionRect;
- }
- ///////////
- // Ensure caption size is recalculated by triggering an OnNcCalcSize()
- //
- void CMultiLineCaption::Repaint()
- {
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- // Trigger an OnNcCalcSize() to resize caption
- //
- Invalidate();
- wnd.SetWindowPos(&CWnd::wndTopMost, 0,0,0,0,
- SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE);
- }
- ///////////
- // Redraw caption and client area, sizing window as required
- void CMultiLineCaption::Refresh()
- {
- AdjustHeight();
- Repaint(); // Redraw caption
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- wnd.Invalidate(); // paint client area
- UpdateWindow(wnd.GetSafeHwnd());
- }
- void CMultiLineCaption::AdjustHeight()
- {
- // Size window to caption if caption is taller
- //
- ASSERT(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- CRect wndRect;
- wnd.GetWindowRect(wndRect);
- if (wndRect.Height() < GetCaptionHeight() + GetFrameSize().cy)
- {
- wnd.SetWindowPos(&CWnd::wndTopMost, 0,0,wndRect.Width(),
- GetCaptionHeight() + GetFrameSize().cy,
- SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE);
- }
- }