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 + -
显示快捷键?