dhewm3/neo/tools/common/PropTree/PropTreeList.cpp
Daniel Gibson c3d480afe4 Rename GetWindowScalingFactor() to Win_GetW.., support older Win versions
Minimum required Windows version is XP again (instead of Win10).
Win_GetWindowScalingFactor() tries to use two dynamically loaded functions
from newer windows versions (8.1+, Win10 1607+) and has a fallback for
older versions that also seems to work (at least if all displays have
the same DPI).

Moved the function to win_main.cpp so the dynamically loaded functions
can be loaded at startup; so edit_gui_common.cpp could be removed again.
2021-05-10 03:18:24 +02:00

639 lines
13 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 "tools/edit_gui_common.h"
#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;
float scaling_factor = Win_GetWindowScalingFactor(GetSafeHwnd());
ASSERT(m_pProp!=NULL);
GetClientRect(rc);
nHeight = rc.Height() + scaling_factor;
ZeroMemory(&si, sizeof(SCROLLINFO));
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_RANGE|SIF_PAGE;
si.nMin = 0;
si.nMax = m_pProp->GetRootItem()->GetTotalHeight() * scaling_factor;
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);
float scaling_factor = Win_GetWindowScalingFactor(GetSafeHwnd());
rc.InflateRect(scaling_factor, scaling_factor);
// 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();
float scaling_factor = Win_GetWindowScalingFactor(GetSafeHwnd());
GetClientRect(rc);
nHeight = rc.Height();
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 * scaling_factor;
break;
case SB_LINEUP:
ny -= PROPTREEITEM_DEFHEIGHT * scaling_factor;
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();
}
}