439 lines
11 KiB
C++
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);
|
|
}
|
|
|