ncgutter.cpp
来自「管理项目进度工具的原代码」· C++ 代码 · 共 2,008 行 · 第 1/3 页
CPP
2,008 行
if (!bCancel)
bCancel = SendMessage(WM_NCG_WANTRECALC, nID, (LPARAM)pMsg);
return !bCancel;
}
BOOL CNcGutter::ParentWantRedraw()
{
// ask parent/derived if they still want to redraw
UINT nID = GetDlgCtrlID();
const MSG* pMsg = GetCurrentMessage();
BOOL bCancel = ::SendMessage(GetParent(), WM_NCG_WANTREDRAW, nID, (LPARAM)pMsg);
if (!bCancel)
bCancel = SendMessage(WM_NCG_WANTREDRAW, nID, (LPARAM)pMsg);
return !bCancel;
}
BOOL CNcGutter::ProcessRecalcMsg(UINT nMsg, WPARAM wp, LPARAM lp, LRESULT& lr)
{
if (!m_bSetRedraw)
return FALSE;
// convert message to lookup key
WORD nNotification = 0;
if (nMsg == WM_COMMAND)
nNotification = HIWORD(wp);
else if (nMsg == WM_NOTIFY)
{
NMHDR* pNMHDR = (NMHDR*)lp;
nNotification = (WORD)pNMHDR->code;
}
DWORD dwKey = MAKELONG((WORD)nMsg, nNotification);
char dummy;
if (m_mapRecalcMessages.Lookup(dwKey, dummy))
{
lr = Default(); // always
if (!ParentWantRecalc())
return TRUE;
RecalcGutter();
return TRUE;
}
return FALSE;
}
BOOL CNcGutter::ProcessRedrawMsg(UINT nMsg, WPARAM wp, LPARAM lp, LRESULT& lr)
{
if (!m_bSetRedraw)
return FALSE;
// convert message to lookup key
WORD nNotification = 0;
if (nMsg == WM_COMMAND)
nNotification = HIWORD(wp);
else if (nMsg == WM_NOTIFY)
{
NMHDR* pNMHDR = (NMHDR*)lp;
nNotification = (WORD)pNMHDR->code;
}
DWORD dwKey = MAKELONG((WORD)nMsg, nNotification);
char dummy;
if (m_mapRedrawMessages.Lookup(dwKey, dummy))
{
lr = Default(); // always
// ask parent/derived if they still want to redraw
if (!ParentWantRedraw())
return TRUE;
#ifdef _DEBUG
CString sTrace;
sTrace.Format("WantsRedraw(msg = 0x%x, %d)", nMsg, nMsg);
CNcRedraw hr(GetHwnd(), sTrace);
#else
CNcRedraw hr(GetHwnd());
#endif
return TRUE;
}
return FALSE;
}
void CNcGutter::OnPaint()
{
ASSERT (HasStyle(NCGS_DOUBLEBUFFERCLIENT));
PAINTSTRUCT ps;
HDC hdc = ::BeginPaint(GetHwnd(), &ps); // device context for painting
if (m_bSetRedraw)
{
UINT nFlags = PRF_CLIENT;
OnPrint(hdc, nFlags);
}
::EndPaint(GetHwnd(), &ps);
}
LRESULT CNcGutter::OnNcHitTest(CPoint point)
{
LRESULT lHitTest = Default();
switch (lHitTest)
{
case HTVSCROLL:
// there seems to be a bug in windows that decides that if a window has a
// vertical scrollbar then everything to the right of the client area must
// be the scrollbar so we need to double check if we have right side columns
if (HasStyle(NCGS_RIGHTCOLUMNS) && (GetStyle() & WS_VSCROLL))
{
CRect rClient;
GetWindowClientRect(rClient, TRUE);
CRect rVScroll(rClient);
rVScroll.left = rVScroll.right;
rVScroll.right += ::GetSystemMetrics(SM_CXVSCROLL);
if (!rVScroll.PtInRect(point))
return HTBORDER;
}
break;
case HTNOWHERE:
return HTBORDER;
case HTCAPTION:
if (HitTest(point) == NCGHT_HEADER)
return HTBORDER;
break;
}
// everything else
return lHitTest;
}
void CNcGutter::OnButtonDown(UINT nMsg, CPoint point)
{
m_dwButtonDownItem = ItemHitTest(point);
SetFocus(GetHwnd());
// notify parent then hook window
NCGITEMCLICK ngic = { m_dwButtonDownItem,
0,
NCG_CLIENTCOLUMNID,
nMsg, { point.x, point.y } };
UINT nID = GetDlgCtrlID();
if (!::SendMessage(GetParent(), WM_NCG_NOTIFYITEMCLICK, nID, (LPARAM)&ngic))
SendMessage(WM_NCG_NOTIFYITEMCLICK, nID, (LPARAM)&ngic);
CNcRedraw hr(GetHwnd(), "4");
}
void CNcGutter::OnButtonUp(UINT nMsg, CPoint point)
{
CPoint ptScreen(point);
ClientToScreen(&ptScreen);
// before we proceed, check whether we should process the message
// as a non-client message. we check for any header button that is
// down as well as out of client area clicks
CRect rClient;
GetClientRect(rClient);
if (m_nHeaderColDown >= 0 || !rClient.PtInRect(point))
{
if (nMsg == WM_LBUTTONUP)
OnNcButtonUp(WM_NCLBUTTONUP, ptScreen);
else
OnNcButtonUp(WM_NCRBUTTONUP, ptScreen);
return;
}
DWORD dwItem;
int nCol;
NCG_HITTEST nHitTest = HitTest(ptScreen, dwItem, nCol);
if (nMsg == WM_LBUTTONUP && nHitTest == NCGHT_ITEM)
{
// check its the same item
if (dwItem == m_dwButtonDownItem)
{
// notify parent then hook window
NCGITEMCLICK ngic = { dwItem,
m_dwButtonDownItem,
NCG_CLIENTCOLUMNID,
nMsg, { point.x, point.y } };
UINT nID = GetDlgCtrlID();
if (!::SendMessage(GetParent(), WM_NCG_NOTIFYITEMCLICK, nID, (LPARAM)&ngic))
SendMessage(WM_NCG_NOTIFYITEMCLICK, nID, (LPARAM)&ngic);
}
}
}
void CNcGutter::OnNcButtonDown(UINT nMsg, CPoint point)
{
SetFocus(GetHwnd());
DWORD dwItem;
int nCol;
NCG_HITTEST nHitTest = HitTest(point, dwItem, nCol);
if (nHitTest == NCGHT_NOWHERE)
return;
COLUMNDESC* pCD = m_aColumns[nCol];
// reset
m_dwButtonDownItem = 0;
switch (nHitTest)
{
case NCGHT_HEADER:
// check its a clickable column
if (nMsg == WM_NCLBUTTONDOWN && nCol >= 0 && pCD->bClickable)
{
SetCapture(GetHwnd());
pCD->bPressed = TRUE;
m_nHeaderColDown = nCol;
}
break;
case NCGHT_ITEM:
{
m_dwButtonDownItem = dwItem;
NCGITEMCLICK ngic = { m_dwButtonDownItem, 0, pCD->nColID, nMsg, { point.x, point.y } };
// try parent then hook window
UINT nID = GetDlgCtrlID();
if (!::SendMessage(GetParent(), WM_NCG_NOTIFYITEMCLICK, nID, (LPARAM)&ngic))
SendMessage(WM_NCG_NOTIFYITEMCLICK, nID, (LPARAM)&ngic);
}
break;
}
// redraw for good measure
Redraw();
}
void CNcGutter::OnNcButtonUp(UINT nMsg, CPoint point)
{
if (nMsg == WM_NCRBUTTONUP)
{
PostMessage(WM_CONTEXTMENU, (WPARAM)GetHwnd(), MAKELPARAM(point.x, point.y));
}
else if (nMsg == WM_NCLBUTTONUP)
{
DWORD dwItem = 0;
int nCol;
NCG_HITTEST nHitTest = HitTest(point, dwItem, nCol);
// if the header column is set it means that we're in the middle
// of a column click. note: we don't check hittest here as the cursor
// may not still be on the header
if (m_nHeaderColDown >= 0)
{
UINT nColID = m_aColumns[m_nHeaderColDown]->nColID;
// send notification if same column header currently pressed
if (nHitTest == NCGHT_HEADER && nCol == m_nHeaderColDown)
{
NCGHDRCLICK nghc = { nColID, nMsg, FALSE };
// try parent then hook window
UINT nID = GetDlgCtrlID();
if (!::SendMessage(GetParent(), WM_NCG_NOTIFYHEADERCLICK, nID, (LPARAM)&nghc))
SendMessage(WM_NCG_NOTIFYHEADERCLICK, nID, (LPARAM)&nghc);
m_nHeaderColDown = -1;
PressHeader(nColID, nghc.bPressed);
}
else
{
m_nHeaderColDown = -1;
PressHeader(nColID, FALSE);
}
ReleaseCapture();
}
// else item click, so check its the same item
else if (nHitTest == NCGHT_ITEM && dwItem && dwItem == m_dwButtonDownItem)
{
// notify parent then hook window
NCGITEMCLICK ngic = { dwItem,
m_dwButtonDownItem,
m_aColumns[nCol]->nColID,
nMsg, { point.x, point.y } };
UINT nID = GetDlgCtrlID();
if (!::SendMessage(GetParent(), WM_NCG_NOTIFYITEMCLICK, nID, (LPARAM)&ngic))
SendMessage(WM_NCG_NOTIFYITEMCLICK, nID, (LPARAM)&ngic);
}
}
}
void CNcGutter::OnHotChange(int nPrevHot, int nHot)
{
ASSERT (HasStyle(NCGS_SHOWHEADER));
if (HasStyle(NCGS_SHOWHEADER))
{
CRect rWindow, rClient, rHeader;
// get rects in window coords
GetWindowRectEx(rWindow, FALSE);
GetWindowClientRect(rClient, FALSE);
CWindowDC dc(GetCWnd());
CPoint ptCursor;
GetCursorPos(ptCursor, FALSE);
// nc portion
int nClientCol = m_aColumns.GetSize() - 1;
BOOL bDrawNonClient = FALSE;
if (nPrevHot >= 0 && nPrevHot != nClientCol)
{
const COLUMNDESC* pCD = m_aColumns[nPrevHot];
bDrawNonClient = (pCD->bClickable);
}
if (!bDrawNonClient && nHot >= 0 && nHot != nClientCol)
{
const COLUMNDESC* pCD = m_aColumns[nHot];
bDrawNonClient = (pCD->bClickable);
}
CFont* pOldFont = PrepareFont(&dc, TRUE);
if (bDrawNonClient)
{
GetHeaderRect(rHeader, GHR_NONCLIENT, FALSE);
NcDrawHeader(&dc, rHeader, NONCLIENT, &ptCursor);
}
// client bit
if (nPrevHot == nClientCol || nHot == nClientCol)
{
const COLUMNDESC* pCD = m_aColumns[nClientCol];
if (pCD->bClickable)
{
GetHeaderRect(rHeader, GHR_CLIENT, FALSE);
NcDrawHeader(&dc, rHeader, CLIENT, &ptCursor);
}
}
dc.SelectObject(pOldFont);
}
}
CFont* CNcGutter::PrepareFont(CDC* pDC, BOOL bHeader, HFONT hFont)
{
if (!hFont && IsHooked())
{
if (bHeader)
hFont = (HFONT)::SendMessage(GetParent(), WM_GETFONT, 0, 0);
else
hFont = (HFONT)::SendMessage(GetHwnd(), WM_GETFONT, 0, 0);
}
if (hFont)
{
CFont* pFont = CFont::FromHandle(hFont);
if (pFont)
return pDC->SelectObject(pFont);
}
// else
return (CFont*)pDC->SelectStockObject(DEFAULT_GUI_FONT);
}
DWORD CNcGutter::ItemHitTest(CPoint ptClient) const
{
UINT nID = GetDlgCtrlID();
LONG lPoint = MAKELONG(ptClient.x, ptClient.y);
// try parent then hook window
DWORD dwItem = (DWORD)::SendMessage(GetParent(), WM_NCG_HITTEST, nID, lPoint);
if (!dwItem)
dwItem = (DWORD)SendMessage(WM_NCG_HITTEST, nID, lPoint);
// else
return dwItem;
}
int CNcGutter::ColumnHitTest(CPoint ptScreen) const
{
CRect rHeader;
GetHeaderRect(rHeader, GHR_CLIENT, TRUE);
if (rHeader.PtInRect(ptScreen))
return m_aColumns.GetSize() - 1; // last column == client
// else must be a gutter column
GetHeaderRect(rHeader, GHR_NONCLIENT, TRUE);
// adjust ptScreen to be relative to start of gutter
ptScreen.Offset(-rHeader.TopLeft());
if (ptScreen.x <= 0)
return 0;
int nCol = m_aColumns.GetSize() - 1; // omit last column == client
int nGutter = GetGutterWidth();
while (nCol--)
{
int nColWidth = m_aColumns[nCol]->nWidth;
if (ptScreen.x >= (LONG)(nGutter - nColWidth) && ptScreen.x <= (LONG)nGutter + 1)
return nCol;
nGutter -= nColWidth;
}
return nCol; // == -1 ??
}
CNcGutter::NCG_HITTEST CNcGutter::HitTest(CPoint ptScreen) const
{
DWORD dwItem;
int nColumn;
return HitTest(ptScreen, dwItem, nColumn);
}
BOOL CNcGutter::PtInHeader(CPoint ptScreen) const
{
return (HitTest(ptScreen) == NCGHT_HEADER);
}
void CNcGutter::GetCursorPos(CPoint& ptCursor, BOOL bScreen) const
{
ptCursor = CPoint(::GetMessagePos());
if (!bScreen)
{
CRect rWindow;
::GetWindowRect(GetHwnd(), rWindow);
ptCursor.Offset(-rWindow.TopLeft());
}
}
void CNcGutter::GetHeaderRect(CRect& rHeader, GHR_WHAT nWhat, BOOL bScreen) const
{
CRect rClient, rWindow;
GetWindowRectEx(rWindow, bScreen);
GetWindowClientRect(rClient, bScreen);
rWindow.DeflateRect(BORDER, BORDER);
rHeader.bottom = rClient.top;
rHeader.top = rClient.top - HEADERHEIGHT; // HEADERHEIGHT will be zero if the header is hidden
switch (nWhat)
{
case GHR_CLIENT:
rHeader.left = rClient.left;
rHeader.right = rClient.right;
// check for vert scroll bar
if (GetStyle() & WS_VSCROLL)
rHeader.right += ::GetSystemMetrics(SM_CXVSCROLL);
break;
case GHR_NONCLIENT:
if (HasStyle(NCGS_RIGHTCOLUMNS))
{
rHeader.left = rClient.right;
rHeader.right = rWindow.right;
// check for vert scroll bar
if (GetStyle() & WS_VSCROLL)
rHeader.left += ::GetSystemMetrics(SM_CXVSCROLL);
}
else
{
rHeader.left = rWindow.left;
rHeader.right = rClient.left;
}
break;
case GHR_ALL:
default:
rHeader.left = rWindow.left;
rHeader.right = rWindow.right;
break;
}
}
void CNcGutter::GetWindowRectEx(CRect& rWindow, BOOL bScreen) const
{
CWnd* pWnd = GetCWnd();
ASSERT(pWnd);
if (pWnd)
{
pWnd->GetWindowRect(rWindow);
if (!bScreen)
rWindow.OffsetRect(-rWindow.TopLeft());
}
}
void CNcGutter::GetWindowClientRect(CRect& rClient, BOOL bScreen) const
{
CWnd* pWnd = GetCWnd();
ASSERT(pWnd);
if (pWnd)
{
pWnd->GetClientRect(rClient);
pWnd->ClientToScreen(rClient); // always
if (!bScreen) // convert to window coords
{
CRect rWindow;
pWnd->GetWindowRect(rWindow);
rClient.OffsetRect(-rWindow.TopLeft());
}
}
}
void CNcGutter::GetGutterRect(CRect& rGutter, BOOL bScreen) const
{
CRect rWindow, rClient;
GetWindowRectEx(rWindow, bScreen);
GetWindowClientRect(rClient, bScreen);
rWindow.DeflateRect(BORDER, BORDER);
rGutter = rWindow;
if (HasStyle(NCGS_RIGHTCOLUMNS))
{
rGutter.left = rClient.right;
// handle vertical scrollbar
if (GetStyle() & WS_VSCROLL)
rGutter.left += ::GetSystemMetrics(SM_CXVSCROLL);
}
else
rGutter.right = rClient.left;
}
CNcGutter::NCG_HITTEST CNcGutter::HitTest(CPoint ptScreen, DWORD& dwItem, int& nColumn) const
{
CRect rClient, rHeader, rWindow, rGutter;
GetWindowClientRect(rClient, TRUE);
GetGutterRect(rGutter, TRUE);
GetHeaderRect(rHeader, GHR_ALL, TRUE);
if (rHeader.PtInRect(ptScreen))
{
nColumn = ColumnHitTest(ptScreen);
return NCGHT_HEADER;
}
else if (rGutter.PtInRect(ptScreen) || rClient.PtInRect(ptScreen))
{
nColumn = ColumnHitTest(ptScreen);
// get item
CPoint point(ptScreen);
ScreenToClient(&point);
// must ensure that point is within client rect
if (HasStyle(NCGS_RIGHTCOLUMNS))
{
if (point.x > rClient.Width()) // in the gutter
point.x = rClient.Width(); // client coords
}
else // columns left
{
if (point.x < 0) // in the gutter
point.x = 0; // client coords
}
dwItem = ItemHitTest(point);
return NCGHT_ITEM;
}
// else
return NCGHT_NOWHERE;
}
BOOL CNcGutter::OnSetCursor()
{
CPoint ptScreen(::GetMessagePos());
DWORD dwItem;
int nCol;
NCG_HITTEST nHitTest = HitTest(ptScreen, dwItem, nCol);
if (nHitTest == NCGHT_ITEM && dwItem && nCol >= 0)
{
NCGGETCURSOR ncgsc = { m_aColumns[nCol]->nColID, dwItem };
UINT nID = GetDlgCtrlID();
HCURSOR hCursor = (HCURSOR)::SendMessage(GetParent(), WM_NCG_GETCURSOR, nID, (LPARAM)&ncgsc);
if (!hCursor)
hCursor = (HCURSOR)SendMessage(WM_NCG_GETCURSOR, nID, (LPARAM)&ncgsc);
if (hCursor)
{
::SetCursor(hCursor);
return TRUE;
}
}
return FALSE;
}
void CNcGutter::OnNcPaint()
{
// force client repaints to happen first because
// of the time it takes to perform the non-client paint
::UpdateWindow(GetHwnd());
// see if we can avoid any unnecessary drawing
if (GetGutterWidth() == 0 && !HasStyle(NCGS_SHOWHEADER))
return;
CRect rWindow, rClient, rGutter, rHeader;
GetWindowClientRect(rClient, FALSE);
GetGutterRect(rGutter, FALSE);
GetHeaderRect(rHeader, GHR_ALL, FALSE);
// get cursor pos in window coords for header drawing
CPoint ptCursor;
if (HasStyle(NCGS_SHOWHEADER))
GetCursorPos(ptCursor, FALSE);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?