📄 paintcap.cpp
字号:
////////////////////////////////////////////////////////////////
// 1997 Microsoft Systems Journal.
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
// Modify 2003.03.19 WHD
//
#include "StdAfx.h"
#include "..\nms.h"
#include "PaintCap.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
////////////////
// Class used to get the caption rectangle of a window in window coords.
// This is the area of the title bar inside the window frame, including
// the icon and min/max/close buttons.
//
class CCaptionRect : public CRect {
public:
CCaptionRect(const CWnd& wnd); // use reference to deny NULL ptr
};
IMPLEMENT_DYNAMIC(CCaptionPainter, CSubclassWnd);
CCaptionPainter::CCaptionPainter()
{
Invalidate();
}
CCaptionPainter::~CCaptionPainter()
{
}
//////////////////
// Install caption handler. nPaintMsg is message I will send too frame
// when its caption needs painting.
//
BOOL CCaptionPainter::Install(CDialog* pFrameWnd,UINT nPaintMsg,BOOL bMod)
{
ASSERT_KINDOF(CDialog, pFrameWnd);
m_nPaintMsg = nPaintMsg;
m_bModified=bMod;
return HookWindow(pFrameWnd);
}
//////////////////
// Message handler handles caption-related messages
//
LRESULT CCaptionPainter::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:
Invalidate();
m_pWndHooked->SendMessage(m_nPaintMsg, 0, 0L);
}
// I don't handle it: pass along
return CSubclassWnd::WindowProc(msg, wp, lp);
}
/////////////////
// Window Send WM_NCPAINT
// when its frame must be painted.
//
void CCaptionPainter::OnNcPaint(HRGN hRgn)
{
ASSERT_VALID(m_pWndHooked);
CWnd& wnd = *m_pWndHooked;
// 得到标题栏Rect
CCaptionRect rc(wnd);
CRect rcWin;
wnd.GetWindowRect(&rcWin);
rc += rcWin.TopLeft();
// 重绘区域不包含标题栏(不用重画标题栏)
if ((WORD)hRgn > 1 && !::RectInRegion(hRgn, &rc))
{
Default(); // just do default thing
return; // and quit
}
// 标题栏的部分区域需要重画
// 首先找出除标题栏外应该重画的系统区域(Frame)
HRGN hRgnCaption = ::CreateRectRgnIndirect(&rc);
HRGN hRgnNew = ::CreateRectRgnIndirect(&rc);
if ((WORD)hRgn > 1)
{
::CombineRgn(hRgnNew, hRgn, hRgnCaption, RGN_DIFF);
}
else
{
HRGN hRgnAll = ::CreateRectRgnIndirect(&rcWin);
CombineRgn(hRgnNew, hRgnAll, hRgnCaption, RGN_DIFF);
DeleteObject(hRgnAll);
}
// Ok , 让系统去重画 除标题栏外应该重画的系统区域
//
MSG& msg = AfxGetThreadState()->m_lastSentMsg;
WPARAM savewp = msg.wParam;
msg.wParam = (WPARAM)hRgnNew;
Default();
DeleteObject(hRgnCaption);
DeleteObject(hRgnNew);
msg.wParam = savewp;
// 现在重画自己的标题栏
PaintCaption();
}
//////////////////
// Handle WM_NCACTIVATE for main window
//
BOOL CCaptionPainter::OnNcActivate(BOOL bActive)
{
ASSERT_VALID(m_pWndHooked);
if (bActive==m_bActive)
{
// 不用重绘
return TRUE;
}
// 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.
//
/*
CDialog* 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.
//
DWORD dwStyle = m_pWndHooked->GetStyle();
if (dwStyle & WS_VISIBLE)
::SetWindowLong(m_pWndHooked->GetSafeHwnd() , GWL_STYLE, (dwStyle & ~ WS_VISIBLE));
MSG& msg = AfxGetThreadState()->m_lastSentMsg;
msg.wParam = bActive;
Default();
if (dwStyle & WS_VISIBLE)
::SetWindowLong(m_pWndHooked->GetSafeHwnd(), GWL_STYLE, dwStyle);
// 重绘
m_bActive = bActive; // update state
m_pWndHooked->SendMessage(WM_NCPAINT); // paint non-client area (frame too)
return TRUE; // done OK
}
//////////////////
// Handle WM_SETTEXT for main window
//
void CCaptionPainter::OnSetText(LPCTSTR lpText)
{
return ;
ASSERT_VALID(m_pWndHooked);
CWnd& wnd = *m_pWndHooked;
// Turn WS_VISIBLE style off before calling Windows to
// set the text, then turn it back on again after.
//
DWORD dwStyle = wnd.GetStyle();
if (dwStyle & WS_VISIBLE)
SetWindowLong(wnd.m_hWnd, GWL_STYLE, dwStyle & ~ WS_VISIBLE);
Default();
if (dwStyle & WS_VISIBLE)
SetWindowLong(wnd.m_hWnd, GWL_STYLE, dwStyle);
wnd.SendMessage(WM_NCPAINT); // paint non-client (frame)
Invalidate(); // force new bitmap
//PaintCaption();
}
//////////////////
// 重画标题栏
void CCaptionPainter::PaintCaption()
{
ASSERT(m_pWndHooked);
CWnd& wnd = *m_pWndHooked;
// 窗口最小化,当然不用重绘
if (wnd.IsIconic())
{
return ;
}
CWindowDC dcWin(&wnd);
CDC dc;
dc.CreateCompatibleDC(&dcWin);
CCaptionRect rc(wnd);
// 选中拥有焦点/没有焦点时位图
CBitmap& bm = m_bmCaption[m_bActive!=0];
BOOL bPaintIt = FALSE;
if (!(HBITMAP)bm)
{
bm.CreateCompatibleBitmap(&dcWin, rc.Width(), rc.Height());
//bm.LoadBitmap(IDB_BITMAP1);
bPaintIt = TRUE;
}
CBitmap* pOldBitmap = dc.SelectObject(&bm);
if (bPaintIt)
{
// 标题栏位图尚未作图
PAINTCAP pc;
pc.m_pDC = &dc;
pc.m_szCaption = rc.Size();
pc.m_bActive=m_bActive;
if(m_nPaintMsg)
{
wnd.SendMessage(m_nPaintMsg , m_bActive, (LPARAM)&pc);
}
else
DrawNormalCaption(pc);
}
// 将内存位图拷贝
dcWin.BitBlt(rc.left,rc.top , rc.Width() , rc.Height() , &dc,0,0,SRCCOPY);
dc.SelectObject(pOldBitmap);
}
void CCaptionPainter::DrawNormalCaption(const PAINTCAP& pc)
{
ASSERT(m_pWndHooked);
CWnd& wnd = *m_pWndHooked;
UINT uFlags = DC_TEXT|DC_ICON;
if(pc.m_bActive)
uFlags|=DC_ACTIVE;
// 利用系统函数画标题栏
CRect rc(CPoint(0,0) , pc.m_szCaption);
::DrawCaption(wnd,*pc.m_pDC,&rc,uFlags);
DrawButtons(pc);
}
////////////////
// Draw caption icon. Returns width of icon.
//
int CCaptionPainter::DrawIcon(const PAINTCAP& pc)
{
return 1;
ASSERT(m_pWndHooked);
CWnd& wnd = *m_pWndHooked;
// 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;
DrawIconEx(pc.m_pDC->m_hDC, rc.left, rc.top,
pApp->LoadIcon(IDR_MAINFRAME),
rc.Width(), rc.Height(), 0, NULL, DI_NORMAL);
return cxIcon;
}
////////////////
// 最大化、最小化、关闭按钮
int CCaptionPainter::DrawButtons(const PAINTCAP& pc)
{
return 1;
ASSERT(m_pWndHooked);
CWnd& wnd = *m_pWndHooked;
DWORD dwStyle = wnd.GetStyle();
if (!(dwStyle & WS_CAPTION))
{
return 0;
}
ASSERT(pc.m_pDC);
CDC& dc = *pc.m_pDC;
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 rc(0, 0, cxIcon, cyIcon);
rc += CPoint(pc.m_szCaption.cx-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;
dc.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);
dc.DrawFrameControl(&rc, DFC_CAPTION,
//bMaxBox ? (wnd.IsZoomed() ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX) :
//DFCS_CAPTIONHELP);
(wnd.IsZoomed() ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX));
//}
// Minimize button has 2 pixel border on all sides but right.
if (dwStyle & WS_MINIMIZEBOX) {
rc -= CPoint(cxIcon,0);
dc.DrawFrameControl(&rc, DFC_CAPTION, DFCS_CAPTIONMIN);
}
// 返回值作为其他重画操作坐标
return pc.m_szCaption.cx - rc.left - 2;
}
//////////////////
// CCaptionRect Constructor computes caption rectangle in window coords.
// 得到wnd的标题栏CRect
CCaptionRect::CCaptionRect(const CWnd& wnd)
{
// 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));
int cxIcon = GetSystemMetrics(SM_CXSIZE); // width of caption icon/button
// Compute rectangle
wnd.GetWindowRect(this); // window rect in screen coords
*this -= CPoint(left, top); // shift origin to (0,0)
left += szFrame.cx; // frame
right -= szFrame.cx; // frame
top += szFrame.cy; // top = end of frame
bottom = top + GetSystemMetrics(SM_CYCAPTION) // height of caption
- GetSystemMetrics(SM_CYBORDER); // minus gray shadow border
}
//////////////////
// 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 CCaptionPainter::GetLuminosity(COLORREF color)
{
#define HLSMAX 240 // This is what Display Properties uses
#define 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);
}
#define COLOR_WHITE RGB(255,255,255)
#define COLOR_BLACK RGB(0,0,0)
#define NCOLORSHADES 64 // this many shades in gradient
//////////////////
// Helper to paint rectangle with a color.
//
void CCaptionPainter::PaintRect(CDC& dc, int x, int y, int w, int h, COLORREF color)
{
CBrush brush(color);
CBrush* pOldBrush = dc.SelectObject(&brush);
dc.PatBlt(x, y, w, h, PATCOPY);
dc.SelectObject(pOldBrush);
}
//////////////////
//
void CCaptionPainter::PaintMyCaption(WPARAM bActive, LPARAM lParam, CString m_strTitle)
{
if (lParam == 0)
{
// lParam = 0 means system setting change: invalidate fonts.
m_fontCaption.DeleteObject();
return;
}
const PAINTCAP& pc = *((PAINTCAP*)lParam);
ASSERT(pc.m_pDC);
CDC& dc = *pc.m_pDC;
int cxCap = pc.m_szCaption.cx;
int cyCap = pc.m_szCaption.cy;
COLORREF clrFrom; // the beginning color
COLORREF clrTo; // the ending color
// These are the default colors in Windows(R) 98
// Modify them to suit your needs.
if (bActive)
{
// Active caption
clrFrom = GetSysColor(COLOR_ACTIVECAPTION);
clrTo = RGB(0,127,255);
}
else
{
// Inactive caption
clrFrom = GetSysColor(COLOR_INACTIVECAPTION);
clrTo = MY_SYS_COLOR;
}
// Get the intensity values for the ending color
int r1 = GetRValue(clrTo); // red
int g1 = GetGValue(clrTo); // green
int b1 = GetBValue(clrTo); // blue
// Get the intensity values for the begining color
int r2 = GetRValue(clrFrom); // red
int g2 = GetGValue(clrFrom); // green
int b2 = GetBValue(clrFrom); // blue
int x = 5*cxCap/6; // start 5/6 of the way right
int w = x; // width of area to shade
int xDelta= max(w/NCOLORSHADES,1); // width of one shade band
// Paint Color
// Paint far right 1/6 of caption the background color
PaintRect(dc, x, 0, cxCap-x, cyCap, clrTo);
int r, g, b;
while (x > xDelta) {
x -= xDelta;
if (r1 > r2)
r = r1 - (r1-r2)*(w-x)/w;
else
r = r1 + (r2-r1)*(w-x)/w;
if (g1 > g2)
g = g1 - (g1-g2)*(w-x)/w;
else
g = g1 + (g2-g1)*(w-x)/w;
if (b1 > b2)
b = b1 - (b1-b2)*(w-x)/w;
else
b = b1 + (b2-b1)*(w-x)/w;
// Paint bands right to left
PaintRect(dc, x, 0, xDelta, cyCap, RGB(r, g, b));
}
// Paint what's left of the caption with the beginning color
PaintRect(dc, 0, 0, x, cyCap, clrFrom);
// 图标、按钮
int cxIcon = DrawIcon(pc);
int cxButns = DrawButtons(pc);
// 改变字体(标题栏显示的字体)
if (!m_fontCaption.m_hObject)
{
CreateFonts();
}
CString s("NMS-【网管】");
CRect rc(CPoint(0,0), pc.m_szCaption); // text rectangle
rc.left += cxIcon+2; // start after icon
rc.right -= cxButns; // don't draw past buttons
dc.SetBkMode(TRANSPARENT); // draw on top of our shading
// This is a trial and error value that sets the point
// where the caption background is too light/dark in
// order to display the text with white/black.
// 根据背景选择字体的颜色
#define LUMINOSITY_MARK 120
if (GetLuminosity(clrFrom) > LUMINOSITY_MARK)
dc.SetTextColor(COLOR_BLACK);
else
dc.SetTextColor(COLOR_WHITE);
CFont* pOldFont = dc.SelectObject(&m_fontCaption);
dc.DrawText(s, &rc, DT_CENTER|DT_VCENTER |DT_SINGLELINE);//DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);
// Restore DC
dc.SelectObject(pOldFont);
}
//////////////////
// Helper function to build the fonts I need.
//
void CCaptionPainter::CreateFonts()
{
// Get current system caption font, just to get its size
//
NONCLIENTMETRICS ncm;
ncm.cbSize = sizeof(ncm);
VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0));
ncm.lfCaptionFont.lfHeight -= 2;
m_fontCaption.CreateFontIndirect(&ncm.lfCaptionFont);
}
// Private MFC function only sets the title if it's different
//
extern void AFXAPI AfxSetWindowText(HWND, LPCTSTR);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -