2025-11-27 16:46:48 +09:00

439 lines
11 KiB
C++

// This software along with its related components, documentation and files ("The Libraries")
// is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office. For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
// OXTreeHeader.cpp : implementation file
//
// Version: 9.3
#include "stdafx.h"
#include "OXTreeHeader.h"
#include "OXTreeCtrl.h"
#pragma warning (disable : 4102)
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// COXTreeHeader
HHOOK COXTreeHeader::m_hMouseHook = NULL;
HWND COXTreeHeader::m_hwndPrevMouseMoveWnd = NULL;
IMPLEMENT_DYNAMIC(COXTreeHeader, CHeaderCtrl)
COXTreeHeader::COXTreeHeader()
{
m_nSortCol=-1;
m_nSortOrder=0;
m_bLButtonDown=FALSE;
}
COXTreeHeader::~COXTreeHeader()
{
}
BEGIN_MESSAGE_MAP(COXTreeHeader, CHeaderCtrl)
//{{AFX_MSG_MAP(COXTreeHeader)
ON_WM_LBUTTONDBLCLK()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
ON_WM_DESTROY()
//}}AFX_MSG_MAP
ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// COXTreeHeader message handlers
void COXTreeHeader::OnLButtonDblClk(UINT nFlags, CPoint point)
{
HD_HITTESTINFO hti;
hti.pt = point;
SendMessage(HDM_HITTEST,0,(LPARAM)(&hti));
if(hti.flags & HHT_ONDIVIDER && hti.iItem != -1)
{
// the user double-clicked on column divider
((COXTreeCtrl*)GetParent())->ResizeColToFit(hti.iItem);
}
else
CHeaderCtrl::OnLButtonDblClk(nFlags, point);
}
void COXTreeHeader::OnLButtonDown(UINT nFlags, CPoint point)
{
Invalidate();
if(GetFocus() != GetParent())
GetParent()->SetFocus();
m_bLButtonDown=TRUE;
CHeaderCtrl::OnLButtonDown(nFlags, point);
}
void COXTreeHeader::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
CHeaderCtrl::OnLButtonUp(nFlags, point);
if(m_bLButtonDown && GetFocus()==GetParent())
{
CWnd* pParentWnd=GetParent();
ASSERT(pParentWnd->IsKindOf(RUNTIME_CLASS(COXTreeCtrl)));
if(((COXTreeCtrl*)pParentWnd)->IsHeaderSorting())
{
CHeaderCtrl::OnLButtonDown(nFlags, point);
HD_HITTESTINFO hti;
hti.pt = point;
SendMessage(HDM_HITTEST,0,(LPARAM)(&hti));
if(hti.flags & HHT_ONHEADER && hti.iItem != -1 &&
!(hti.flags & HHT_ONDIVIDER))
{
// the user clicked on column
int nSortOrder=(hti.iItem==m_nSortCol) ?
(m_nSortOrder!=0 ? m_nSortOrder*-1 : 1) : 1;
((COXTreeCtrl*)pParentWnd)->
SortChildren(NULL,hti.iItem,nSortOrder==1 ? TRUE : FALSE);
}
}
}
m_bLButtonDown=FALSE;
}
BOOL COXTreeHeader::SortColumn(int nCol, int nSortOrder)
{
ASSERT(nCol==-1 || (nCol>=0 && nCol<=GetItemCount()));
if(nCol==-1)
nSortOrder=0;
CWnd* pParentWnd=GetParent();
ASSERT(pParentWnd->IsKindOf(RUNTIME_CLASS(CListCtrl)));
#ifdef _UT_UXTHEME
if (::GetWindowTheme(m_hWnd) != NULL)
{
// Change all items to owner draw
for (int i = 0; i < GetItemCount(); i++)
{
HD_ITEM hditem;
hditem.mask=HDI_FORMAT;
GetItem(i,&hditem);
hditem.fmt|=HDF_OWNERDRAW;
SetItem(i,&hditem);
}
goto out;
}
#endif // _UT_UXTHEME
if(nCol!=m_nSortCol)
{
// Change the item from ownder drawn
HD_ITEM hditem;
hditem.mask=HDI_FORMAT;
GetItem(m_nSortCol,&hditem);
hditem.fmt&=~HDF_OWNERDRAW;
hditem.fmt|=HDF_STRING;
SetItem(m_nSortCol,&hditem);
if(nSortOrder!=0)
{
// Change the item to ownder drawn
HD_ITEM hditem;
hditem.mask=HDI_FORMAT;
GetItem(nCol,&hditem);
hditem.fmt|=HDF_OWNERDRAW;
SetItem(nCol,&hditem);
}
// Invalidate header control so that it gets redrawn
Invalidate();
}
out:
m_nSortCol=nCol;
m_nSortOrder=nSortOrder;
return TRUE;
}
void COXTreeHeader::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct )
{
if (m_UxTheme.IsUxThemeLoaded())
{
HTHEME hTheme = m_UxTheme.GetWindowTheme(m_hWnd);
if (hTheme != NULL)
{
// Draw the item background
int iState = HIS_NORMAL;
if (lpDrawItemStruct->itemState == ODS_SELECTED)
iState = HIS_PRESSED;
else
{
POINT ptCursor;
::GetCursorPos(&ptCursor);
ScreenToClient(&ptCursor);
CRect rectItem(lpDrawItemStruct->rcItem);
if (rectItem.PtInRect(ptCursor))
iState = HIS_HOT;
}
m_UxTheme.DrawThemeBackground(hTheme,
lpDrawItemStruct->hDC,
HP_HEADERITEM,
iState,
&lpDrawItemStruct->rcItem,
NULL);
BOOL bDrawArrow;
if (m_nSortOrder!=0 && lpDrawItemStruct->itemID == (UINT)m_nSortCol)
bDrawArrow = TRUE;
else
bDrawArrow = FALSE;
// Get the column text and format
TCHAR buf[256];
HD_ITEM hditem;
hditem.mask = HDI_TEXT | HDI_FORMAT;
hditem.pszText = buf;
hditem.cchTextMax = 255;
GetItem( lpDrawItemStruct->itemID, &hditem );
RECT rectText = lpDrawItemStruct->rcItem;
rectText.left += 9;
rectText.right -= 9;
// Determine format for drawing column label
UINT uFormat = DT_SINGLELINE | DT_NOPREFIX |
DT_VCENTER | DT_END_ELLIPSIS ;
UINT uArrowFormat = DT_SINGLELINE | DT_VCENTER;
if( hditem.fmt & HDF_CENTER)
{
uFormat |= DT_CENTER;
uArrowFormat |= DT_RIGHT;
if (bDrawArrow)
rectText.right -= 12;
}
else if( hditem.fmt & HDF_RIGHT)
{
uFormat |= DT_RIGHT;
uArrowFormat |= DT_LEFT;
if (bDrawArrow)
rectText.left += 12;
}
else
{
uFormat |= DT_LEFT;
uArrowFormat |= DT_RIGHT;
if (bDrawArrow)
rectText.right -= 12;
}
m_UxTheme.DrawThemeText(hTheme,
lpDrawItemStruct->hDC,
HP_HEADERITEM,
HIS_NORMAL,
buf,
-1,
uFormat,
0,
&rectText);
// Draw the Sort arrow
if (bDrawArrow)
{
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);
int nSavedDC = dc.SaveDC();
CFont fontMarlett;
fontMarlett.CreatePointFont(120, _T("Marlett"));
dc.SelectObject(&fontMarlett);
dc.SetBkMode(TRANSPARENT);
dc.SetTextColor(RGB(128, 128, 128));
CRect rectArrow = lpDrawItemStruct->rcItem;
rectArrow.DeflateRect(5, 0, 5, 0);
if (m_nSortOrder == 1)
dc.DrawText(_T("5"), -1, &rectArrow, uArrowFormat);
else
dc.DrawText(_T("6"), -1, &rectArrow, uArrowFormat);
// Restore dc
dc.RestoreDC(nSavedDC);
// Detach the dc before returning
dc.Detach();
}
return;
}
}
CDC dc;
dc.Attach( lpDrawItemStruct->hDC );
// Get the column rect
CRect rcLabel( lpDrawItemStruct->rcItem );
// Save DC
int nSavedDC = dc.SaveDC();
// Set clipping region to limit drawing within column
CRgn rgn;
rgn.CreateRectRgnIndirect( &rcLabel );
dc.SelectObject( &rgn );
rgn.DeleteObject();
// Draw the background
CBrush brush(::GetSysColor(COLOR_3DFACE));
dc.FillRect(rcLabel,&brush);
// Labels are offset by a certain amount
// This offset is related to the width of a space character
int offset = dc.GetTextExtent(_T(" "), 1 ).cx*2;
// Get the column text and format
TCHAR buf[256];
HD_ITEM hditem;
hditem.mask = HDI_TEXT | HDI_FORMAT;
hditem.pszText = buf;
hditem.cchTextMax = 255;
GetItem( lpDrawItemStruct->itemID, &hditem );
// Determine format for drawing column label
UINT uFormat = DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP |
DT_VCENTER | DT_END_ELLIPSIS ;
if( hditem.fmt & HDF_CENTER)
uFormat |= DT_CENTER;
else if( hditem.fmt & HDF_RIGHT)
uFormat |= DT_RIGHT;
else
uFormat |= DT_LEFT;
// Adjust the rect if the mouse button is pressed on it
if( lpDrawItemStruct->itemState == ODS_SELECTED )
{
rcLabel.left++;
rcLabel.top += 2;
rcLabel.right++;
}
// Adjust the rect further if Sort arrow is to be displayed
if( lpDrawItemStruct->itemID == (UINT)m_nSortCol )
{
rcLabel.right -= 3 * offset;
}
rcLabel.left += offset;
rcLabel.right -= offset;
// Draw column label
if( rcLabel.left < rcLabel.right )
dc.DrawText(buf,-1,rcLabel, uFormat);
// Draw the Sort arrow
if( m_nSortOrder!=0 && lpDrawItemStruct->itemID == (UINT)m_nSortCol )
{
CRect rcIcon( lpDrawItemStruct->rcItem );
// Set up pens to use for drawing the triangle
CPen penLight(PS_SOLID, 1, GetSysColor(COLOR_3DHILIGHT));
CPen penShadow(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW));
CPen *pOldPen = dc.SelectObject( &penLight );
if( m_nSortOrder==1 )
{
// Draw triangle pointing upwards
dc.MoveTo( rcIcon.right - 2*offset, offset-1);
dc.LineTo( rcIcon.right - 3*offset/2, rcIcon.bottom - offset );
dc.LineTo( rcIcon.right - 5*offset/2-2, rcIcon.bottom - offset );
dc.MoveTo( rcIcon.right - 5*offset/2-1, rcIcon.bottom - offset-1 );
dc.SelectObject( &penShadow );
dc.LineTo( rcIcon.right - 2*offset, offset-2);
}
else
{
// Draw triangle pointing downwords
dc.MoveTo( rcIcon.right - 3*offset/2, offset-1);
dc.LineTo( rcIcon.right - 2*offset-1, rcIcon.bottom - offset + 1 );
dc.MoveTo( rcIcon.right - 2*offset-1, rcIcon.bottom - offset );
dc.SelectObject( &penShadow );
dc.LineTo( rcIcon.right - 5*offset/2-1, offset -1 );
dc.LineTo( rcIcon.right - 3*offset/2, offset -1);
}
// Restore the pen
dc.SelectObject( pOldPen );
}
// Restore dc
dc.RestoreDC( nSavedDC );
// Detach the dc before returning
dc.Detach();
}
void COXTreeHeader::PreSubclassWindow()
{
// Hook the mouse
if (m_hMouseHook == NULL)
m_hMouseHook = ::SetWindowsHookEx(WH_MOUSE, MouseProc, 0, AfxGetApp()->m_nThreadID);
}
void COXTreeHeader::OnDestroy()
{
// Unhook the mouse
if (m_hMouseHook)
{
::UnhookWindowsHookEx(m_hMouseHook);
m_hMouseHook = NULL;
}
CHeaderCtrl::OnDestroy();
}
LRESULT CALLBACK COXTreeHeader::MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode < 0)
return ::CallNextHookEx(m_hMouseHook, nCode, wParam, lParam);
if (nCode == HC_ACTION && wParam == WM_MOUSEMOVE)
{
MOUSEHOOKSTRUCT* pMH = (MOUSEHOOKSTRUCT*) lParam;
// If the previous message was for COXSizableMiniDockFrameWnd and the current is not
// we need to update the caption buttons
COXTreeHeader* pPrev = DYNAMIC_DOWNCAST(COXTreeHeader,
CWnd::FromHandlePermanent(m_hwndPrevMouseMoveWnd));
if (pPrev == NULL)
pPrev = DYNAMIC_DOWNCAST(COXTreeHeader,
CWnd::FromHandlePermanent(::GetParent(m_hwndPrevMouseMoveWnd)));
COXTreeHeader* pCurrent = DYNAMIC_DOWNCAST(COXTreeHeader,
CWnd::FromHandlePermanent(pMH->hwnd));
if (pCurrent == NULL)
pCurrent = DYNAMIC_DOWNCAST(COXTreeHeader,
CWnd::FromHandlePermanent(::GetParent(pMH->hwnd)));
if (pPrev != NULL && pCurrent != pPrev)
{
// The mouse just left the header control
pPrev->Invalidate();
}
else if (pCurrent != NULL && pCurrent != pPrev)
{
// The mouse just entered the header control
pCurrent->Invalidate();
}
m_hwndPrevMouseMoveWnd = pMH->hwnd;
}
return ::CallNextHookEx(m_hMouseHook, nCode, wParam, lParam);
}
void COXTreeHeader::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
#ifdef _UT_UXTHEME
if (::GetWindowTheme(m_hWnd) != NULL)
Invalidate();
#endif
CHeaderCtrl::OnMouseMove(nFlags, point);
}