mirror of
https://github.com/dhewm/dhewm3.git
synced 2024-12-15 15:20:56 +00:00
635 lines
12 KiB
C++
635 lines
12 KiB
C++
// PropTreeList.cpp : implementation file
|
|
//
|
|
// Copyright (C) 1998-2001 Scott Ramsay
|
|
// sramsay@gonavi.com
|
|
// http://www.gonavi.com
|
|
//
|
|
// This material is provided "as is", with absolutely no warranty expressed
|
|
// or implied. Any use is at your own risk.
|
|
//
|
|
// Permission to use or copy this software for any purpose is hereby granted
|
|
// without fee, provided the above notices are retained on all copies.
|
|
// Permission to modify the code and to distribute modified code is granted,
|
|
// provided the above notices are retained, and a notice that the code was
|
|
// modified is included with the above copyright notice.
|
|
//
|
|
// If you use this code, drop me an email. I'd like to know if you find the code
|
|
// useful.
|
|
|
|
//#include "stdafx.h"
|
|
#include "../../../idlib/precompiled.h"
|
|
#pragma hdrstop
|
|
|
|
#include "PropTree.h"
|
|
#include "../../../sys/win32/rc/proptree_Resource.h"
|
|
#include "PropTreeList.h"
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#define PROPTREEITEM_EXPANDCOLUMN 16 // width of the expand column
|
|
#define PROPTREEITEM_COLRNG 5 // width of splitter
|
|
#define PROPTREEITEM_DEFHEIGHT 21 // default heigt of an item
|
|
|
|
extern HINSTANCE ghInst;
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CPropTreeList
|
|
|
|
CPropTreeList::CPropTreeList() :
|
|
m_pProp(NULL),
|
|
m_BackBufferSize(0,0),
|
|
m_bColDrag(FALSE),
|
|
m_nPrevCol(0)
|
|
{
|
|
}
|
|
|
|
CPropTreeList::~CPropTreeList()
|
|
{
|
|
}
|
|
|
|
|
|
BEGIN_MESSAGE_MAP(CPropTreeList, CWnd)
|
|
//{{AFX_MSG_MAP(CPropTreeList)
|
|
ON_WM_SIZE()
|
|
ON_WM_PAINT()
|
|
ON_WM_SETCURSOR()
|
|
ON_WM_LBUTTONDOWN()
|
|
ON_WM_LBUTTONUP()
|
|
ON_WM_LBUTTONDBLCLK()
|
|
ON_WM_MOUSEMOVE()
|
|
ON_WM_MOUSEWHEEL()
|
|
ON_WM_KEYDOWN()
|
|
ON_WM_GETDLGCODE()
|
|
ON_WM_VSCROLL()
|
|
//}}AFX_MSG_MAP
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CPropTreeList message handlers
|
|
|
|
void CPropTreeList::SetPropOwner(CPropTree* pProp)
|
|
{
|
|
m_pProp = pProp;
|
|
}
|
|
|
|
|
|
BOOL CPropTreeList::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
|
|
{
|
|
CWnd* pWnd = this;
|
|
|
|
LPCTSTR pszCreateClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, ::LoadCursor(NULL, IDC_ARROW));
|
|
|
|
return pWnd->Create(pszCreateClass, _T(""), dwStyle, rect, pParentWnd, nID);
|
|
}
|
|
|
|
|
|
void CPropTreeList::OnSize(UINT nType, int cx, int cy)
|
|
{
|
|
CWnd::OnSize(nType, cx, cy);
|
|
|
|
RecreateBackBuffer(cx, cy);
|
|
|
|
if (m_pProp)
|
|
{
|
|
UpdateResize();
|
|
Invalidate();
|
|
UpdateWindow();
|
|
|
|
// inform all items that a resize has been made
|
|
m_pProp->UpdateMoveAllItems();
|
|
}
|
|
}
|
|
|
|
|
|
void CPropTreeList::RecreateBackBuffer(int cx, int cy)
|
|
{
|
|
if (m_BackBufferSize.cx<cx || m_BackBufferSize.cy<cy)
|
|
{
|
|
m_BackBufferSize = CSize(cx, cy);
|
|
|
|
CWindowDC dc(NULL);
|
|
|
|
int nPlanes = dc.GetDeviceCaps(PLANES);
|
|
int nBitCount = dc.GetDeviceCaps(BITSPIXEL);
|
|
|
|
m_BackBuffer.DeleteObject();
|
|
m_BackBuffer.CreateBitmap(cx, cy, nPlanes, nBitCount, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
void CPropTreeList::UpdateResize()
|
|
{
|
|
SCROLLINFO si;
|
|
LONG nHeight;
|
|
CRect rc;
|
|
|
|
ASSERT(m_pProp!=NULL);
|
|
|
|
GetClientRect(rc);
|
|
nHeight = rc.Height() + 1;
|
|
|
|
ZeroMemory(&si, sizeof(SCROLLINFO));
|
|
si.cbSize = sizeof(SCROLLINFO);
|
|
si.fMask = SIF_RANGE|SIF_PAGE;
|
|
si.nMin = 0;
|
|
si.nMax = m_pProp->GetRootItem()->GetTotalHeight();
|
|
si.nPage = nHeight;
|
|
|
|
if ((int)si.nPage>si.nMax)
|
|
m_pProp->SetOriginOffset(0);
|
|
|
|
SetScrollInfo(SB_VERT, &si, TRUE);
|
|
|
|
// force set column for clipping
|
|
m_pProp->SetColumn(m_pProp->GetColumn());
|
|
}
|
|
|
|
|
|
void CPropTreeList::OnPaint()
|
|
{
|
|
CPaintDC dc(this);
|
|
CDC memdc;
|
|
CBitmap* pOldBitmap;
|
|
|
|
ASSERT(m_pProp!=NULL);
|
|
|
|
m_pProp->ClearVisibleList();
|
|
|
|
memdc.CreateCompatibleDC(&dc);
|
|
pOldBitmap = memdc.SelectObject(&m_BackBuffer);
|
|
|
|
CRect rc;
|
|
GetClientRect(rc);
|
|
|
|
// draw control background
|
|
memdc.SelectObject(GetSysColorBrush(COLOR_BTNFACE));
|
|
memdc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), PATCOPY);
|
|
|
|
// draw control inside fill color
|
|
rc.DeflateRect(2,2);
|
|
memdc.PatBlt(rc.left, rc.top, rc.Width(), rc.Height(), m_pProp->IsWindowEnabled() ? WHITENESS : PATCOPY);
|
|
rc.InflateRect(2,2);
|
|
|
|
// draw expand column
|
|
memdc.SelectObject(GetSysColorBrush(COLOR_BTNFACE));
|
|
memdc.PatBlt(0, 0, PROPTREEITEM_EXPANDCOLUMN, rc.Height(), PATCOPY);
|
|
|
|
// draw edge
|
|
memdc.DrawEdge(&rc, BDR_SUNKENOUTER, BF_RECT);
|
|
|
|
CPropTreeItem* pItem;
|
|
LONG nTotal = 0;
|
|
|
|
ASSERT(m_pProp->GetRootItem()!=NULL);
|
|
|
|
rc.DeflateRect(2,2);
|
|
|
|
// create clip region
|
|
HRGN hRgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
|
|
SelectClipRgn(memdc.m_hDC, hRgn);
|
|
|
|
// draw all items
|
|
for (pItem = m_pProp->GetRootItem()->GetChild(); pItem; pItem = pItem->GetSibling())
|
|
{
|
|
LONG nHeight = pItem->DrawItem(&memdc, rc, 0, nTotal);
|
|
nTotal += nHeight;
|
|
}
|
|
|
|
// remove clip region
|
|
SelectClipRgn(memdc.m_hDC, NULL);
|
|
DeleteObject(hRgn);
|
|
|
|
// copy back buffer to the display
|
|
dc.GetClipBox(&rc);
|
|
dc.BitBlt(rc.left, rc.top, rc.Width(), rc.Height(), &memdc, rc.left, rc.top, SRCCOPY);
|
|
memdc.DeleteDC();
|
|
}
|
|
|
|
|
|
BOOL CPropTreeList::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
|
|
{
|
|
if (nHitTest==HTCLIENT)
|
|
{
|
|
CPoint pt;
|
|
|
|
ASSERT(m_pProp!=NULL);
|
|
|
|
GetCursorPos(&pt);
|
|
ScreenToClient(&pt);
|
|
|
|
switch (m_pProp->HitTest(pt))
|
|
{
|
|
case HTCOLUMN:
|
|
SetCursor(LoadCursor(ghInst, MAKEINTRESOURCE(IDC_SPLITTER)));
|
|
return TRUE;
|
|
|
|
case HTCHECKBOX:
|
|
case HTBUTTON:
|
|
case HTEXPAND:
|
|
SetCursor(LoadCursor(ghInst, MAKEINTRESOURCE(IDC_FPOINT)));
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return CWnd::OnSetCursor(pWnd, nHitTest, message);
|
|
}
|
|
|
|
|
|
void CPropTreeList::OnLButtonDown(UINT, CPoint point)
|
|
{
|
|
ASSERT(m_pProp!=NULL);
|
|
|
|
if (m_pProp->IsDisableInput())
|
|
return;
|
|
|
|
m_pProp->SendNotify(NM_CLICK);
|
|
|
|
if (!m_pProp->IsWindowEnabled())
|
|
return;
|
|
|
|
SetFocus();
|
|
|
|
LONG nHit = m_pProp->HitTest(point);
|
|
|
|
CPropTreeItem* pItem;
|
|
CRect rc;
|
|
CDC* pDC;
|
|
|
|
switch (nHit)
|
|
{
|
|
case HTCOLUMN:
|
|
if (m_pProp->SendNotify(PTN_COLUMNCLICK))
|
|
break;
|
|
|
|
m_bColDrag = TRUE;
|
|
SetCapture();
|
|
|
|
m_nPrevCol = m_pProp->GetOrigin().x;
|
|
|
|
// paint drag line
|
|
pDC = GetDC();
|
|
GetClientRect(rc);
|
|
pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT);
|
|
ReleaseDC(pDC);
|
|
break;
|
|
|
|
case HTCHECKBOX:
|
|
if ((pItem = m_pProp->FindItem(point))!=NULL)
|
|
{
|
|
pItem->Check(!pItem->IsChecked());
|
|
m_pProp->SendNotify(PTN_CHECKCLICK, pItem);
|
|
Invalidate();
|
|
}
|
|
break;
|
|
case HTBUTTON:
|
|
if ((pItem = m_pProp->FindItem(point))!=NULL)
|
|
{
|
|
pItem->Check();
|
|
m_pProp->SendNotify(PTN_ITEMBUTTONCLICK, pItem);
|
|
Invalidate();
|
|
}
|
|
break;
|
|
case HTEXPAND:
|
|
if ((pItem = m_pProp->FindItem(point))!=NULL)
|
|
{
|
|
if (pItem->GetChild() && !m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem))
|
|
{
|
|
pItem->Expand(!pItem->IsExpanded());
|
|
|
|
UpdateResize();
|
|
Invalidate();
|
|
UpdateWindow();
|
|
CheckVisibleFocus();
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if ((pItem = m_pProp->FindItem(point))!=NULL)
|
|
{
|
|
CPropTreeItem* pOldFocus = m_pProp->GetFocusedItem();
|
|
|
|
m_pProp->SelectItems(NULL, FALSE);
|
|
m_pProp->SetFocusedItem(pItem);
|
|
|
|
pItem->Select();
|
|
|
|
Invalidate();
|
|
|
|
if (pItem!=pOldFocus)
|
|
m_pProp->SendNotify(PTN_SELCHANGE, pItem);
|
|
|
|
if (nHit==HTATTRIBUTE && !pItem->IsRootLevel())
|
|
{
|
|
if (!m_pProp->SendNotify(PTN_PROPCLICK, pItem) && !pItem->IsReadOnly())
|
|
pItem->Activate(CPropTreeItem::ACTIVATE_TYPE_MOUSE, point);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_pProp->SelectItems(NULL, FALSE);
|
|
m_pProp->SetFocusedItem(NULL);
|
|
m_pProp->SendNotify(PTN_SELCHANGE);
|
|
Invalidate();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void CPropTreeList::OnLButtonUp(UINT, CPoint point)
|
|
{
|
|
if (m_bColDrag)
|
|
{
|
|
CDC* pDC = GetDC();
|
|
CRect rc;
|
|
|
|
GetClientRect(rc);
|
|
pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT);
|
|
ReleaseDC(pDC);
|
|
|
|
m_bColDrag = FALSE;
|
|
ReleaseCapture();
|
|
|
|
m_pProp->SetColumn(point.x);
|
|
m_pProp->UpdateMoveAllItems();
|
|
Invalidate();
|
|
} else {
|
|
LONG nHit = m_pProp->HitTest(point);
|
|
CPropTreeItem* pItem;
|
|
|
|
switch (nHit)
|
|
{
|
|
case HTBUTTON:
|
|
if ((pItem = m_pProp->FindItem(point))!=NULL)
|
|
{
|
|
pItem->Check( FALSE );
|
|
Invalidate();
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CPropTreeList::OnLButtonDblClk(UINT, CPoint point)
|
|
{
|
|
ASSERT(m_pProp!=NULL);
|
|
|
|
m_pProp->SendNotify(NM_DBLCLK);
|
|
|
|
CPropTreeItem* pItem;
|
|
CPropTreeItem* pOldFocus;
|
|
|
|
if ((pItem = m_pProp->FindItem(point))!=NULL && pItem->GetChild())
|
|
{
|
|
switch (m_pProp->HitTest(point))
|
|
{
|
|
case HTCOLUMN:
|
|
break;
|
|
|
|
case HTCHECKBOX:
|
|
pItem->Check(!pItem->IsChecked());
|
|
m_pProp->SendNotify(PTN_CHECKCLICK, pItem);
|
|
Invalidate();
|
|
break;
|
|
|
|
case HTATTRIBUTE:
|
|
if (!pItem->IsRootLevel())
|
|
break;
|
|
|
|
// pass thru to default
|
|
|
|
default:
|
|
pOldFocus = m_pProp->GetFocusedItem();
|
|
m_pProp->SelectItems(NULL, FALSE);
|
|
m_pProp->SetFocusedItem(pItem);
|
|
pItem->Select();
|
|
|
|
if (pItem!=pOldFocus)
|
|
m_pProp->SendNotify(PTN_SELCHANGE, pItem);
|
|
|
|
// pass thru to HTEXPAND
|
|
|
|
case HTEXPAND:
|
|
if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem))
|
|
{
|
|
pItem->Expand(!pItem->IsExpanded());
|
|
|
|
UpdateResize();
|
|
Invalidate();
|
|
UpdateWindow();
|
|
CheckVisibleFocus();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CPropTreeList::OnMouseMove(UINT, CPoint point)
|
|
{
|
|
if (m_bColDrag)
|
|
{
|
|
CDC* pDC = GetDC();
|
|
CRect rc;
|
|
|
|
GetClientRect(rc);
|
|
pDC->PatBlt(m_nPrevCol - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT);
|
|
pDC->PatBlt(point.x - PROPTREEITEM_COLRNG/2, 0, PROPTREEITEM_COLRNG, rc.bottom, PATINVERT);
|
|
m_nPrevCol = point.x;
|
|
ReleaseDC(pDC);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL CPropTreeList::OnMouseWheel(UINT, short zDelta, CPoint)
|
|
{
|
|
SCROLLINFO si;
|
|
|
|
ZeroMemory(&si, sizeof(SCROLLINFO));
|
|
si.cbSize = sizeof(SCROLLINFO);
|
|
si.fMask = SIF_RANGE;
|
|
|
|
GetScrollInfo(SB_VERT, &si);
|
|
|
|
CRect rc;
|
|
GetClientRect(rc);
|
|
|
|
if (si.nMax - si.nMin < rc.Height())
|
|
return TRUE;
|
|
|
|
SetFocus();
|
|
OnVScroll(zDelta < 0 ? SB_LINEDOWN : SB_LINEUP, 0, NULL);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void CPropTreeList::OnKeyDown(UINT nChar, UINT, UINT)
|
|
{
|
|
|
|
CPropTreeItem* pItem;
|
|
|
|
ASSERT(m_pProp!=NULL);
|
|
|
|
if (m_pProp->IsDisableInput() || !m_pProp->IsWindowEnabled())
|
|
return;
|
|
|
|
switch (nChar)
|
|
{
|
|
case VK_RETURN:
|
|
if ((pItem = m_pProp->GetFocusedItem())!=NULL && !pItem->IsRootLevel() && !pItem->IsReadOnly())
|
|
{
|
|
pItem->Activate(CPropTreeItem::ACTIVATE_TYPE_KEYBOARD, CPoint(0,0));
|
|
}
|
|
break;
|
|
|
|
case VK_HOME:
|
|
if (m_pProp->FocusFirst())
|
|
Invalidate();
|
|
break;
|
|
|
|
case VK_END:
|
|
if (m_pProp->FocusLast())
|
|
Invalidate();
|
|
break;
|
|
|
|
case VK_LEFT:
|
|
if ((pItem = m_pProp->GetFocusedItem())!=NULL)
|
|
{
|
|
if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem))
|
|
{
|
|
if (pItem->GetChild() && pItem->IsExpanded())
|
|
{
|
|
pItem->Expand(FALSE);
|
|
UpdateResize();
|
|
Invalidate();
|
|
UpdateWindow();
|
|
CheckVisibleFocus();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
// pass thru to next case VK_UP
|
|
case VK_UP:
|
|
if (m_pProp->FocusPrev())
|
|
Invalidate();
|
|
break;
|
|
|
|
case VK_RIGHT:
|
|
if ((pItem = m_pProp->GetFocusedItem())!=NULL)
|
|
{
|
|
if (!m_pProp->SendNotify(PTN_ITEMEXPANDING, pItem))
|
|
{
|
|
if (pItem->GetChild() && !pItem->IsExpanded())
|
|
{
|
|
pItem->Expand();
|
|
UpdateResize();
|
|
Invalidate();
|
|
UpdateWindow();
|
|
CheckVisibleFocus();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
// pass thru to next case VK_DOWN
|
|
case VK_DOWN:
|
|
if (m_pProp->FocusNext())
|
|
Invalidate();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
UINT CPropTreeList::OnGetDlgCode()
|
|
{
|
|
return DLGC_WANTARROWS|DLGC_WANTCHARS|DLGC_WANTALLKEYS;
|
|
}
|
|
|
|
|
|
void CPropTreeList::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar*)
|
|
{
|
|
SCROLLINFO si;
|
|
CRect rc;
|
|
LONG nHeight;
|
|
|
|
SetFocus();
|
|
|
|
GetClientRect(rc);
|
|
nHeight = rc.Height() + 1;
|
|
|
|
ZeroMemory(&si, sizeof(SCROLLINFO));
|
|
si.cbSize = sizeof(SCROLLINFO);
|
|
si.fMask = SIF_RANGE;
|
|
|
|
GetScrollInfo(SB_VERT, &si);
|
|
|
|
LONG ny = m_pProp->GetOrigin().y;
|
|
|
|
switch (nSBCode)
|
|
{
|
|
case SB_LINEDOWN:
|
|
ny += PROPTREEITEM_DEFHEIGHT;
|
|
break;
|
|
|
|
case SB_LINEUP:
|
|
ny -= PROPTREEITEM_DEFHEIGHT;
|
|
break;
|
|
|
|
case SB_PAGEDOWN:
|
|
ny += nHeight;
|
|
break;
|
|
|
|
case SB_PAGEUP:
|
|
ny -= nHeight;
|
|
break;
|
|
|
|
case SB_THUMBTRACK:
|
|
ny = nPos;
|
|
break;
|
|
}
|
|
|
|
ny = __min(__max(ny, si.nMin), si.nMax - nHeight);
|
|
|
|
m_pProp->SetOriginOffset(ny);
|
|
si.fMask = SIF_POS;
|
|
si.nPos = ny;
|
|
|
|
SetScrollInfo(SB_VERT, &si, TRUE);
|
|
Invalidate();
|
|
}
|
|
|
|
|
|
void CPropTreeList::CheckVisibleFocus()
|
|
{
|
|
ASSERT(m_pProp!=NULL);
|
|
|
|
CPropTreeItem* pItem;
|
|
|
|
if ((pItem = m_pProp->GetFocusedItem())==NULL)
|
|
return;
|
|
|
|
if (!m_pProp->IsItemVisible(pItem))
|
|
{
|
|
if (m_pProp->IsSingleSelection())
|
|
pItem->Select(FALSE);
|
|
|
|
m_pProp->SetFocusedItem(NULL);
|
|
m_pProp->SendNotify(PTN_SELCHANGE, NULL);
|
|
|
|
Invalidate();
|
|
}
|
|
}
|