MultiLineCaptionXX.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 <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)
- {
- }
- //////////
- // Adjust maximum lines for title wrapping, and refresh
- //
- void CMultiLineCaption::SetMaxLines(int maxLines)
- {
- m_MaxLines = maxLines > 0 ? maxLines : 1;
- Refresh();
- }
- ////////////////
- // 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)
- {
- ///////////
- // Adjust height of client area according to required caption height
- //
- case WM_NCCALCSIZE:
- OnNcCalcSize((BOOL)wp, (LPNCCALCSIZE_PARAMS)lp);
- return 0;
- ///////////
- // Resize before first displaying window
- //
- case WM_SHOWWINDOW:
- OnShowWindow((BOOL) wp, (int) lp);
- return 0;
- ///////////
- // Resize to fit new text
- //
- case WM_SETTEXT:
- OnSetText((LPCTSTR)lp);
- return 0;
- ///////////
- // Prevent multi-line caption being overdrawn by client area
- //
- case WM_GETMINMAXINFO:
- OnGetMinMaxInfo((LPMINMAXINFO)lp);
- return 0;
- ///////////
- // Refresh caption when window is tiled and on restore from
- // maximized & minimized
- //
- case WM_WINDOWPOSCHANGED:
- OnWindowPosChanged( (WINDOWPOS*) lp);
- return 0;
- ///////////
- // Refresh caption when activated
- //
- case WM_MDIACTIVATE:
- OnMDIActivate();
- return 0;
- case WM_EXITSIZEMOVE:
- OnExitSizeMove();
- return 0;
- // case WM_NCACTIVATE:
- // return OnNcActivate(wp);
- }
- ////////////
- // We don't handle it: pass to base class
- //
- return CCaption::WindowProc(msg, wp, lp);
- }
- //BOOL CMultiLineCaption::OnNcActivate(BOOL bActive)
- //{
- //}
- void CMultiLineCaption::OnExitSizeMove()
- {
- Default();
- Refresh();
- }
- /////////////
- // Paint wrapped header text in rectangle
- //
- void CMultiLineCaption::PaintText(CDC* pDC)
- {
- ASSERT(m_pWndHooked);
- // For minimised windows, forget all the multi-line stuff
- if (m_pWndHooked->IsIconic())
- {
- CCaption::PaintText(pDC);
- return;
- }
- pDC->SetBkMode(TRANSPARENT); // draw on top of our background
- // Get window text to paint
- //
- CString text;
- m_pWndHooked->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));
- paintRect.top -= DrawTextFudgeFactor; // ? offset by fudge factor...
- // Get caption font and select into DC
- //
- CFont* pOldFont = pDC->SelectObject(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) + DrawTextFudgeFactor);
- pDC->DrawText(ellipsis, paintRect, DT_SINGLELINE|DT_LEFT);
- }
- }
- else // m_MaxLines == 1
- {
- pDC->DrawText(text, paintRect, SingleLineTextFormat);
- }
- // Restore DC
- //
- pDC->SelectObject(pOldFont);
- }
- //////////////
- // 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) ||
- lpwndpos->flags == RestoreFromMinimized) // Window restored from minimized
- {
- Refresh();
- ASSERT_VALID(m_pWndHooked);
- m_pWndHooked->Invalidate(); // Repaint client area too
- }
- }
- 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 = GetCaptionHeight() + GetFrameSize().cy;
- }
- void CMultiLineCaption::OnShowWindow( BOOL bShow, UINT nStatus )
- {
- // Refresh before showing window the first time
- //
- if (m_InitialShow)
- {
- m_InitialShow = false;
- Refresh();
- }
- Default(); // Let Windows handle it
- }
- void CMultiLineCaption::OnSetText(LPCTSTR lpText)
- {
- ASSERT_VALID(m_pWndHooked);
- CWnd& wnd = *m_pWndHooked;
- CString text;
- wnd.GetWindowText(text);
- CCaption::OnSetText(lpText);
- CRect wndRect;
- wnd.GetWindowRect(wndRect);
- if (wndRect.Height() < GetCaptionHeight())
- {
- wnd.SetWindowPos(&CWnd::wndTopMost, 0,0,wndRect.Width(),GetCaptionHeight(), SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE);
- }
- wnd.Invalidate();
- UpdateWindow(wnd.GetSafeHwnd()); // paint client (frame)
- ////////////
- // Refresh caption size if new text
- //
- // if (text != CString(lpText))
- // Refresh();
- }
- ////////////////
- // Refresh on activate/deactivate for windows with different font
- // sizes for active and inactive caption
- //
- void CMultiLineCaption::OnMDIActivate()
- {
- ASSERT(m_pWndHooked);
- // Size window to caption if caption is taller, otherwise just force a repaint
- //
- CRect wndRect;
- m_pWndHooked->GetWindowRect(wndRect);
- if (wndRect.Height() < GetCaptionHeight())
- m_pWndHooked->SetWindowPos(&CWnd::wndTopMost, 0,0,wndRect.Width(),GetCaptionHeight(), SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE);
- else
- m_pWndHooked->SetWindowPos(&CWnd::wndTopMost, 0,0,0,0, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE);
- Refresh();
- 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
- //
- void CMultiLineCaption::OnNcCalcSize( BOOL bCalcValidRects, LPNCCALCSIZE_PARAMS lpncsp )
- {
- Default(); // Let Windows handle it
- CString text;
- ASSERT(m_pWndHooked);
- m_pWndHooked->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 = m_pWndHooked->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 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);
- CFont* pOldFont = dc.SelectObject(GetFont(m_bActive));
- // Size caption to exclude buttons & icon
- //
- CRect textRect = GetCaptionRect();
- textRect.left += GetIconWidth(); // start after icon
- textRect.right -= GetButtonsWidth() + 4; // don't draw past 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;
- // DrawText word-wrap fails to clip for very narrow rects, so ignore if necessary
- //
- if (textRect.Width() > width)
- textRect.right = textRect.left + width;
- // Restore DC
- //
- dc.SelectObject(pOldFont);
-
- 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* pParent = m_pWndHooked->GetParent();
- if (pParent)
- {
- pParent->GetClientRect(parentRect );
- pParent->ClientToScreen(parentRect);
- }
- CRect hookedRect;
- m_pWndHooked->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::Refresh()
- {
- // Trigger an OnNcCalcSize() to resize caption
- //
- // if (m_pWndHooked)
- // m_pWndHooked->SetWindowPos(&CWnd::wndTopMost, 0,0,0,0, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE);
- m_pWndHooked->Invalidate();
- CRect wndRect;
- m_pWndHooked->GetWindowRect(wndRect);
- // if (wndRect.Height() < GetCaptionHeight())
- // m_pWndHooked->SetWindowPos(&CWnd::wndTopMost, 0,0,wndRect.Width(),GetCaptionHeight(), SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE);
- // else
- m_pWndHooked->SetWindowPos(&CWnd::wndTopMost, 0,0,0,0, SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOSIZE|SWP_NOMOVE);
- // m_pWndHooked->Invalidate();
- // UpdateWindow(m_pWndHooked->GetSafeHwnd()); // paint client (frame)
- }