Files
FC1/Editor/Controls/PropertyCtrl.cpp
romkazvo 34d6c5d489 123
2023-08-07 19:29:24 +08:00

1531 lines
34 KiB
C++

////////////////////////////////////////////////////////////////////////////
//
// Crytek Engine Source File.
// Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
// File name: propertyctrl.cpp
// Version: v1.00
// Created: 5/6/2002 by Timur.
// Compilers: Visual Studio.NET
// Description: Implementation of CPropertyCtrl.
// -------------------------------------------------------------------------
// History:
//
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "PropertyCtrl.h"
#include "PropertyItem.h"
#include "MemDC.h"
#include "Clipboard.h"
#define PROPERTY_LEFT_BORDER 15
#define OFFSET_CHILD 14
#define DEFAULT_ITEM_HEIGHT 14
#define CATEGORY_LINE_COLOR RGB(140,140,140)
#define LINE_COLOR RGB(210,210,210)
#define KILLFOCUS_TIMER 1002
// CPropertyCtrl
IMPLEMENT_DYNAMIC(CPropertyCtrl, CWnd)
BEGIN_MESSAGE_MAP(CPropertyCtrl, CWnd)
ON_WM_GETDLGCODE()
ON_WM_DESTROY()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONDBLCLK()
ON_WM_MOUSEWHEEL()
ON_WM_RBUTTONUP()
ON_WM_RBUTTONDOWN()
ON_WM_VSCROLL()
ON_WM_KEYDOWN()
ON_WM_KILLFOCUS()
ON_WM_SETFOCUS()
ON_WM_SIZE()
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
ON_WM_SETCURSOR()
ON_WM_CREATE()
ON_MESSAGE(WM_GETFONT, OnGetFont)
ON_WM_TIMER()
END_MESSAGE_MAP()
//////////////////////////////////////////////////////////////////////////
CPropertyCtrl::CPropertyCtrl()
{
m_root = 0;
m_updateFunc = 0;
m_selChangeFunc = 0;
m_bEnableCallback = true;
m_bEnableSelChangeCallback = true;
m_selected = 0;
//CUndoProperties::m_sCurrentPropertyWindow = this;
//m_bgBrush.Attach( (HBRUSH)GetStockObject(WHITE_BRUSH) );
m_bgBrush.CreateSolidBrush( GetSysColor(COLOR_WINDOW) );
m_icons.Create(IDB_PROPERTIES, 14, 1, RGB (255,255,255));
m_leftRightCursor = AfxGetApp()->LoadCursor( IDC_LEFTRIGHT );
m_bSplitterDrag = false;
m_splitter = 30;
m_scrollOffset = CPoint(0,0);
m_bDisplayOnlyModified = false;
m_nTimer = 0;
m_pBoldFont = 0;
m_nFlags = 0;
m_nItemHeight = DEFAULT_ITEM_HEIGHT;
}
//////////////////////////////////////////////////////////////////////////
CPropertyCtrl::~CPropertyCtrl()
{
delete m_pBoldFont;
}
//////////////////////////////////////////////////////////////////////////
UINT CPropertyCtrl::OnGetDlgCode()
{
// Want to handle all Tab and arrow keys myself, not delegate it to dialog.
return DLGC_WANTALLKEYS;
}
/*
//////////////////////////////////////////////////////////////////////////
CSize CPropertyCtrl::GetListSize()
{
FRect ClientRect = GetClientRect();
FRect R(0,0,0,4);//!!why?
for( INT i=List.GetCount()-1; i>=0; i-- )
R.Max.Y += List.GetItemHeight( i );
AdjustWindowRect( R, GetWindowLongX(List,GWL_STYLE), 0 );
CSize size;
size.cx = ClientRect.Width();
size.cy = R.Height();
return size;
}
*/
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::OnItemChange( CPropertyItem *item )
{
// Called when item, gets modified.
if (m_updateFunc != 0 && m_bEnableCallback)
{
m_bEnableCallback = false;
m_updateFunc( item->GetXmlNode() );
m_bEnableCallback = true;
}
if (m_updateVarFunc != 0 && m_bEnableCallback)
{
m_bEnableCallback = false;
m_updateVarFunc( item->GetVariable() );
m_bEnableCallback = true;
}
//GetIEditor()->SetModifiedFlag();
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::ExpandAll()
{
if (m_root)
ExpandAllChilds( m_root,true );
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::ExpandAllChilds( CPropertyItem *item,bool bRecursive )
{
for (int i = 0; i < item->GetChildCount(); i++)
{
if (IsCategory( item->GetChild(i) ))
{
Expand( item->GetChild(i),true );
if (bRecursive)
ExpandAllChilds( item->GetChild(i),bRecursive );
}
}
CalcLayout();
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::ReloadValues()
{
bool prev = m_bEnableCallback;
m_bEnableCallback = false;
// Make sure No selection.
SelectItem(0);
if (m_root)
m_root->ReloadValues();
m_bEnableCallback = prev;
Invalidate();
}
bool CPropertyCtrl::EnableUpdateCallback( bool bEnable )
{
bool prev = m_bEnableCallback;
m_bEnableCallback = bEnable;
return prev;
};
bool CPropertyCtrl::EnableSelChangeCallback( bool bEnable )
{
bool prev = m_bEnableSelChangeCallback;
m_bEnableSelChangeCallback = bEnable;
return prev;
};
//////////////////////////////////////////////////////////////////////////
// Register your unique class name that you wish to use
void CPropertyCtrl::RegisterWindowClass()
{
WNDCLASS wndcls;
memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL
// defaults
wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
//you can specify your own window procedure
wndcls.lpfnWndProc = ::DefWindowProc;
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hIcon = NULL;
wndcls.hCursor = AfxGetApp()->LoadStandardCursor( IDC_ARROW );
wndcls.hbrBackground = NULL;
wndcls.lpszMenuName = NULL;
// Specify your own class name for using FindWindow later
wndcls.lpszClassName = _T("PropertyCtrl");
// Register the new class and exit if it fails
AfxRegisterClass(&wndcls);
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::Create( DWORD dwStyle,const CRect &rc,CWnd *pParent,UINT nID )
{
RegisterWindowClass();
CWnd::Create( _T("PropertyCtrl"),"",dwStyle,rc,pParent,nID );
ModifyStyle( 0,WS_VSCROLL );
}
int CPropertyCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
Init();
return 0;
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::PreSubclassWindow()
{
CWnd::PreSubclassWindow();
Init();
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::OnDestroy()
{
delete m_pBoldFont;
m_pBoldFont = NULL;
if (m_nTimer)
{
KillTimer(m_nTimer);
m_nTimer = 0;
}
SelectItem(0);
CWnd::OnDestroy();
// TODO: Add your message handler code here
}
void CPropertyCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
CWnd::OnLButtonDown(nFlags, point);
SetFocus();
m_mouseDownPos = point;
bool bSplitter = IsOverSplitter(point);
if (!bSplitter)
{
CPropertyItem *item = GetItemFromPoint(point);
if (item)
{
if (nFlags & MK_CONTROL)
{
if (item->IsSelected())
MultiUnselectItem(item);
else
MultiSelectItem(item);
}
else if (nFlags & MK_SHIFT)
{
MultiSelectRange(item);
}
else
{
// Select clicked item.
SelectItem( item );
Expand( m_selected,!m_selected->IsExpanded() );
item->OnLButtonDown( nFlags,point );
CRect itemRc;
GetItemRect(item,itemRc);
itemRc = GetItemValueRect(itemRc);
if (itemRc.PtInRect(point))
{
// Clicked in value rectangle.
item->SetFocus();
}
}
}
}
else
{
m_bSplitterDrag = true;
SetCapture();
}
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
if (m_bSplitterDrag)
{
m_bSplitterDrag = false;
ReleaseCapture();
}
CWnd::OnLButtonUp(nFlags, point);
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::OnLButtonDblClk(UINT nFlags, CPoint point)
{
CWnd::OnLButtonDblClk(nFlags, point);
CPropertyItem *item = GetItemFromPoint(point);
if (item)
{
// Select clicked item.
SelectItem( item );
item->OnLButtonDblClk( nFlags,point );
}
}
BOOL CPropertyCtrl::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
{
if (m_selected)
{
//m_selected->OnMouseWheel( nFlags,zDelta,pt );
}
return CWnd::OnMouseWheel(nFlags, zDelta, pt);
}
void CPropertyCtrl::OnRButtonUp(UINT nFlags, CPoint point)
{
CClipboard clipboard;
// Popup Menu with Event selection.
CMenu menu;
menu.CreatePopupMenu();
menu.AppendMenu( MF_STRING,1,_T("Copy") );
menu.AppendMenu( MF_STRING,2,_T("Copy Recursively") );
menu.AppendMenu( MF_STRING,3,_T("Copy All") );
menu.AppendMenu( MF_SEPARATOR,0,_T("") );
if (clipboard.IsEmpty())
menu.AppendMenu( MF_STRING|MF_GRAYED,4,_T("Paste") );
else
menu.AppendMenu( MF_STRING,4,_T("Paste") );
CPoint p;
::GetCursorPos(&p);
int res = ::TrackPopupMenuEx( menu.GetSafeHmenu(),TPM_LEFTBUTTON|TPM_RETURNCMD,p.x,p.y,GetSafeHwnd(),NULL );
switch (res)
{
case 1:
OnCopy(false);
break;
case 2:
OnCopy(true);
break;
case 3:
OnCopyAll();
break;
case 4:
OnPaste();
break;
}
// CWnd::OnRButtonUp(nFlags, point);
}
void CPropertyCtrl::OnRButtonDown(UINT nFlags, CPoint point)
{
SetFocus();
/*
CPropertyItem *item = GetItemFromPoint(point);
if (item && !item->IsSelected())
{
// Select clicked item.
SelectItem( item );
item->OnRButtonDown( nFlags,point );
}
CWnd::OnRButtonDown(nFlags, point);
*/
}
void CPropertyCtrl::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
if (nChar == VK_DOWN || nChar == VK_TAB)
{
Items items;
GetVisibleItems( m_root,items );
for (int i = 0; i < items.size(); i++)
{
if (items[i] == m_selected)
{
if (i < items.size()-1)
{
SelectItem( items[i+1] );
break;
}
break;
}
}
}
if (nChar == VK_UP)
{
Items items;
GetVisibleItems( m_root,items );
for (int i = 0; i < items.size(); i++)
{
if (items[i] == m_selected)
{
if (i > 0)
{
SelectItem( items[i-1] );
break;
}
break;
}
}
}
if (nChar == VK_LEFT || nChar == VK_RIGHT)
{
if (m_selected)
Expand( m_selected,!m_selected->IsExpanded() );
}
if (nChar == VK_RETURN)
{
if (m_selected)
m_selected->SetFocus();
}
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
void CPropertyCtrl::OnKillFocus(CWnd* pNewWnd)
{
CWnd::OnKillFocus(pNewWnd);
// TODO: Add your message handler code here
}
void CPropertyCtrl::OnSetFocus(CWnd* pOldWnd)
{
CWnd::OnSetFocus(pOldWnd);
// TODO: Add your message handler code here
}
void CPropertyCtrl::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
if (m_offscreenBitmap.GetSafeHandle() != NULL)
m_offscreenBitmap.DeleteObject();
CRect rc;
GetClientRect( rc );
m_splitter = rc.Width()/2;
CDC *dc = GetDC();
m_offscreenBitmap.CreateCompatibleBitmap( dc,rc.Width(),rc.Height() );
ReleaseDC(dc);
if (m_tooltip.m_hWnd)
{
m_tooltip.DelTool(this,1);
m_tooltip.AddTool( this,"",rc,1 );
}
CalcLayout();
}
BOOL CPropertyCtrl::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
// return CWnd::OnEraseBkgnd(pDC);
}
void CPropertyCtrl::OnPaint()
{
CPaintDC PaintDC(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CWnd::OnPaint() for painting messages
CRect rcClient;
GetClientRect( rcClient );
if (m_offscreenBitmap.GetSafeHandle() == NULL)
{
m_offscreenBitmap.CreateCompatibleBitmap( &PaintDC,rcClient.Width(),rcClient.Height() );
}
CMemDC dc( PaintDC,&m_offscreenBitmap );
dc.FillRect( &PaintDC.m_ps.rcPaint,&m_bgBrush );
dc.SelectObject( GetFont() );
CRect rc = rcClient;
int y = -m_scrollOffset.y;
Items items;
GetVisibleItems( m_root,items );
for (int i = 0; i < items.size(); i++)
{
int itemHeight = GetItemHeight( items[i] );
rc.top = y;
rc.bottom = y + itemHeight;
if (rc.bottom > 0 && rc.top < rcClient.bottom)
DrawItem( items[i],dc,rc );
y += itemHeight;
}
}
void CPropertyCtrl::DrawItem( CPropertyItem *item,CDC &dc,CRect &itemRect )
{
CRect rect = itemRect;
int nLeftBorder = rect.left + PROPERTY_LEFT_BORDER;
bool bCtrlDisabled = IsWindowEnabled() != TRUE;
bool bItemDisabled = item->IsDisabled() || bCtrlDisabled;
bool bItemBold = item->IsBold();
COLORREF crModifiedText = RGB(180,0,0);
COLORREF crBackground = ::GetSysColor(COLOR_BTNFACE);
//COLORREF crText = RGB(0,0,0); //::GetSysColor(nCrText);
COLORREF crText = ::GetSysColor(COLOR_WINDOWTEXT);
COLORREF crTextCategory = ::GetSysColor(COLOR_GRAYTEXT);
COLORREF crTextShadow = RGB(130,130,160); //::GetSysColor(nCrText);
COLORREF crTextDisabled = ::GetSysColor(COLOR_GRAYTEXT);
bool bDotNetStyle = m_nFlags & F_VS_DOT_NET_STYLE;
bool bCategory = IsCategory(item);
if (!m_pBoldFont)
{
m_pBoldFont = new CFont;
int height = -::MulDiv(8, GetDeviceCaps(dc, LOGPIXELSY),72);
m_pBoldFont->CreateFont(height, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH, "MS Sans Serif" );
}
if (bCategory)
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//int nCrBackground, nCrText;
//nCrBackground = COLOR_BTNFACE;
//nCrText = COLOR_WINDOWTEXT;
CRect rc1 = rect;
rc1.InflateRect(0,0,0,1);
//dc.FillSolidRect(rect, RGB(220,220,220));
//dc.FillSolidRect(rect, crBackground );
//dc.Draw3dRect( rc1,GetSysColor(COLOR_BTNHIGHLIGHT),GetSysColor(COLOR_BTNSHADOW));
//////////////////////////////////////////////////////////////////////////
// Draw category solid rectangle.
if (bDotNetStyle)
dc.FillSolidRect( rc1,crBackground );
else
{
CBrush gray( crBackground );
CPen pen(PS_SOLID, 1, CATEGORY_LINE_COLOR );
CBrush *pOldBrush = dc.SelectObject( &gray );
CPen* pOldPen = dc.SelectObject(&pen);
dc.Rectangle( rc1 );
dc.SelectObject( pOldBrush );
dc.SelectObject( pOldPen );
}
COLORREF crOldBkColor = dc.SetBkColor(crBackground);
COLORREF crOldTextColor = dc.SetTextColor(crText);
rect.left += PROPERTY_LEFT_BORDER;
rect.left += 2;
rect.right -= 2;
dc.SetBkMode( TRANSPARENT );
if (bItemDisabled)
{
crText = crTextDisabled;
}
//if (bDotNetStyle)
{
// Draw simple bold text.
dc.SetTextColor( crTextCategory );
CFont *pPrevFont = dc.SelectObject( m_pBoldFont );
rect.OffsetRect(1,1);
dc.DrawText(item->GetName(), &rect, DT_SINGLELINE);
dc.SelectObject( pPrevFont );
}
/*
else
{
// Draw shadowed text.
if (!bItemDisabled)
{
// Text shadow part.
dc.SetTextColor( crTextShadow );
rect.OffsetRect( CPoint(2,2) );
dc.DrawText(item->GetName(), &rect, DT_SINGLELINE);
}
dc.SetTextColor( crText );
rect.OffsetRect( CPoint(-1,-1) );
dc.DrawText(item->GetName(), &rect, DT_SINGLELINE);
}
*/
dc.SetTextColor(crOldTextColor);
dc.SetBkColor(crOldBkColor);
if (item->IsExpandable() && !item->IsExpanded())
// plus
DrawSign( dc,CPoint( (itemRect.left+PROPERTY_LEFT_BORDER)/2,(itemRect.top+itemRect.bottom)/2),true );
else
// minus
DrawSign( dc,CPoint( (itemRect.left+PROPERTY_LEFT_BORDER)/2,(itemRect.top+itemRect.bottom)/2),false );
}
else
{
//////////////////////////////////////////////////////////////////////////
// Draw normal item, (No category)
//////////////////////////////////////////////////////////////////////////
CPen pen(PS_SOLID, 1, LINE_COLOR );
CPen* pOldPen = dc.SelectObject(&pen);
if (bDotNetStyle)
{
dc.FillSolidRect( rect.left,rect.top,PROPERTY_LEFT_BORDER,rect.Height(),crBackground );
}
else
{
// Vertical separator line.
dc.MoveTo(nLeftBorder, rect.top);
dc.LineTo(nLeftBorder, rect.bottom);
}
rect = itemRect;
// Horizontal Line.
dc.MoveTo(rect.left, rect.bottom );
dc.LineTo(rect.right, rect.bottom );
rect.left += PROPERTY_LEFT_BORDER;
nLeftBorder += m_splitter;
dc.MoveTo(nLeftBorder, rect.top);
dc.LineTo(nLeftBorder, rect.bottom);
rect.left += 1;
rect.top += 1;
//rect.bottom -= 1;
rect.right = nLeftBorder;
int nCrBackground, nCrText;
if (item->IsSelected())
{
nCrBackground = COLOR_HIGHLIGHT;
nCrText = COLOR_HIGHLIGHTTEXT;
crBackground = ::GetSysColor(nCrBackground);
}
else
{
nCrBackground = COLOR_WINDOW;
nCrText = COLOR_WINDOWTEXT;
crBackground = ::GetSysColor(nCrBackground);
if (bItemBold && !bItemDisabled)
{
crBackground = RGB(240,240,240);
}
}
crText = ::GetSysColor(nCrText);
if (bItemDisabled)
{
crText = crTextDisabled;
}
else if (m_bDisplayOnlyModified && item->IsModified())
{
crText = crModifiedText;
}
dc.FillSolidRect(rect, crBackground);
COLORREF crOldBkColor = dc.SetBkColor(crBackground);
COLORREF crOldTextColor = dc.SetTextColor(crText);
int textOffset = CalcOffset( item );
rect.left += 2 + textOffset*OFFSET_CHILD;
rect.right -= 2;
/*
CFont* pOldFont = NULL;
CFont fontLabel;
if(bTabItem)
{
LOGFONT logFont;
CFont* pFont = GetFont();
pFont->GetLogFont(&logFont);
logFont.lfWeight = FW_BOLD;
fontLabel.CreateFontIndirect(&logFont);
pOldFont = dc.SelectObject(&fontLabel);
}
*/
CFont *pPrevFont = 0;
if (bItemBold && !bItemDisabled)
{
/*
CRect rc = rect;
// Text shadow part.
dc.SetTextColor( crTextShadow );
rc.OffsetRect( CPoint(1,1) );
dc.DrawText(item->GetName(), &rc, DT_SINGLELINE|DT_VCENTER);
dc.SetTextColor(crText);
*/
pPrevFont = dc.SelectObject( m_pBoldFont );
}
// Draw text label.
dc.DrawText(item->GetName(), &rect, DT_SINGLELINE|DT_VCENTER);
if (bItemBold && !bItemDisabled)
{
dc.SelectObject(pPrevFont);
}
dc.SelectObject(pOldPen);
dc.SetTextColor(crOldTextColor);
dc.SetBkColor(crOldBkColor);
//dc.SetBkMode(TRANSPARENT);
/*
if(pOldFont != NULL)
dc.SelectObject(pOldFont);
*/
if (item->IsExpandable())
{
if (!item->IsExpanded())
// plus
DrawSign( dc,CPoint( (itemRect.left+PROPERTY_LEFT_BORDER)/2,(itemRect.top+itemRect.bottom)/2),true );
else
// minus
DrawSign( dc,CPoint( (itemRect.left+PROPERTY_LEFT_BORDER)/2,(itemRect.top+itemRect.bottom)/2),false );
}
else
{
// Draw Item description icon.
if (!bDotNetStyle)
{
CPoint iconPnt( itemRect.left,itemRect.top+1 );
m_icons.Draw( &dc,item->GetImage(),iconPnt,ILD_TRANSPARENT );
}
}
if (!bCtrlDisabled)
{
CRect valueRect = GetItemValueRect(itemRect);
//if (!item->IsSelected())
{
bool bDisplayValue = true;
if (m_bDisplayOnlyModified && !item->IsModified())
{
bDisplayValue = false;
}
if (bDisplayValue)
{
//valueRect.left += 3;
item->DrawValue(&dc, valueRect);
//valueRect.left -= 3;
}
}
item->MoveInPlaceControl( valueRect );
}
}
}
void CPropertyCtrl::DrawSign( CDC &dc,CPoint point,bool plus )
{
CRect rcSign(point.x-4,point.y-4,point.x+5,point.y + 5);
dc.FillRect(rcSign, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
dc.FrameRect(rcSign, CBrush::FromHandle((HBRUSH)GetStockObject(BLACK_BRUSH)));
CPoint ptCenter(rcSign.CenterPoint());
// minus
dc.MoveTo(ptCenter.x - 2, ptCenter.y);
dc.LineTo(ptCenter.x + 3, ptCenter.y);
if (plus)
{
// plus
dc.MoveTo(ptCenter.x, ptCenter.y - 2);
dc.LineTo(ptCenter.x, ptCenter.y + 3);
}
}
CRect CPropertyCtrl::GetItemValueRect( const CRect &rc )
{
CRect rect = rc;
rect.left += PROPERTY_LEFT_BORDER;
rect.left += m_splitter;
rect.DeflateRect(3, 1, 0, 0);
return rect;
}
void CPropertyCtrl::GetItemRect( CPropertyItem *item,CRect &rect )
{
rect.SetRect( 0,0,0,0 );
CRect rc;
GetClientRect( rc );
int y = -m_scrollOffset.y;
Items items;
GetVisibleItems( m_root,items );
for (int i = 0; i < items.size(); i++)
{
int itemHeight = GetItemHeight( items[i] );
rc.top = y;
rc.bottom = y + itemHeight;
if (items[i] == item)
{
rect = rc;
return;
}
y += itemHeight;
}
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::SetItemHeight( int nItemHeight )
{
m_nItemHeight = nItemHeight;
}
//////////////////////////////////////////////////////////////////////////
int CPropertyCtrl::GetItemHeight( CPropertyItem *item ) const
{
if (m_nFlags & F_VARIABLE_HEIGHT)
{
return item->GetHeight();
}
return m_nItemHeight;
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::CreateItems( XmlNodeRef &node )
{
SelectItem(0);
m_root = 0;
m_xmlRoot = node;
m_root = new CPropertyItem( this );
m_root->SetXmlNode( node );
m_root->SetExpanded( true );
CalcLayout();
Invalidate();
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::SetRootName( const CString &rootName )
{
if (m_root)
m_root->SetName( rootName );
}
//////////////////////////////////////////////////////////////////////////
CPropertyItem* CPropertyCtrl::AddVarBlock( CVarBlock *varBlock,const char *szCategory )
{
assert( varBlock );
if (!m_root)
m_root = new CPropertyItem( this );
CPropertyItem *root = m_root;
if (szCategory && strlen(szCategory) > 0)
{
CPropertyItem *pCategory = new CPropertyItem( this );
pCategory->SetName(szCategory);
m_root->AddChild(pCategory);
root = pCategory;
}
for (int i = 0; i < varBlock->GetVarsCount(); i++)
{
CPropertyItem *childItem = new CPropertyItem( this );
root->AddChild(childItem);
IVariable *var = varBlock->GetVariable(i);
childItem->SetVariable(var);
}
m_root->SetExpanded( true );
CalcLayout();
Invalidate();
return root;
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::DeleteAllItems()
{
SelectItem(0);
m_root = 0;
CalcLayout();
Invalidate();
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::GetVisibleItems( CPropertyItem *item, std::vector<CPropertyItem*> &items )
{
if (!item)
return;
if (item == m_root)
{
if (!m_root->GetName().IsEmpty())
{
items.push_back(m_root);
}
}
if (item->IsExpanded())
{
for (int i = 0; i < item->GetChildCount(); i++)
{
CPropertyItem *child = item->GetChild(i);
items.push_back( child );
if (item->IsExpanded())
GetVisibleItems( child,items );
}
}
}
//////////////////////////////////////////////////////////////////////////
CPropertyItem* CPropertyCtrl::GetItemFromPoint( CPoint point )
{
CRect rc;
GetClientRect( rc );
int y = -m_scrollOffset.y;
Items items;
GetVisibleItems( m_root,items );
for (int i = 0; i < items.size(); i++)
{
int itemHeight = GetItemHeight( items[i] );
rc.top = y;
rc.bottom = y + itemHeight;
if (rc.PtInRect(point))
{
return items[i];
}
y += itemHeight;
}
return 0;
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::SelectItem( CPropertyItem *item )
{
if (m_selected)
{
m_selected->SetSelected(false);
m_selected->DestroyInPlaceControl();
}
if (!m_multiSelectedItems.empty())
{
// Clear multiple selected items.
for (int i = 0; i < m_multiSelectedItems.size(); i++)
{
m_multiSelectedItems[i]->SetSelected(false);
}
m_multiSelectedItems.clear();
}
m_selected = item;
if (m_selected)
{
m_multiSelectedItems.push_back(m_selected);
m_selected->SetSelected(true);
CreateInPlaceControl();
CRect rcClient;
CRect rc;
GetClientRect(rcClient);
GetItemRect( m_selected,rc );
if (rc.bottom > rcClient.bottom)
{
int y = m_scrollOffset.y + (rc.bottom - rcClient.bottom);
FlatSB_SetScrollPos( GetSafeHwnd(),SB_VERT,y,TRUE );
m_scrollOffset.y = y;
}
else if (rc.top < rcClient.top)
{
int y = m_scrollOffset.y - (rcClient.top - rc.top);
FlatSB_SetScrollPos( GetSafeHwnd(),SB_VERT,y,TRUE );
m_scrollOffset.y = y;
}
if (m_selChangeFunc!=NULL && m_bEnableSelChangeCallback)
{
m_selChangeFunc(m_selected->GetXmlNode());
}
}else
{
if (m_selChangeFunc!=NULL && m_bEnableSelChangeCallback)
{
m_selChangeFunc(NULL);
}
}
Invalidate();
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::CreateInPlaceControl()
{
if (!m_selected)
return;
CRect rc;
GetItemRect(m_selected,rc);
rc = GetItemValueRect(rc);
if (m_selected->IsDisabled())
return;
m_selected->CreateInPlaceControl( this,rc );
}
//////////////////////////////////////////////////////////////////////////
int CPropertyCtrl::CalcOffset( CPropertyItem *item )
{
if (item == m_root)
return 0;
int offset = 0;
while (item && item != m_root && !IsCategory(item))
{
item = item->GetParent();
offset++;
};
if (offset < 1)
return 0;
return offset-1;
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
CWnd::OnMouseMove(nFlags, point);
if (m_bSplitterDrag)
{
CRect rc;
GetClientRect( rc );
int x = point.x - PROPERTY_LEFT_BORDER;
if (x < 30)
x = 30;
if (x > rc.right - 30 - PROPERTY_LEFT_BORDER)
x = rc.right - 30 - PROPERTY_LEFT_BORDER;
if (x != m_splitter)
{
m_splitter = x;
Invalidate();
}
}
else
{
CPropertyItem *item = GetItemFromPoint(point);
if (nFlags & MK_LBUTTON && !(nFlags&(MK_SHIFT|MK_CONTROL)))
{
if (item != m_selected)
{
// Select clicked item.
SelectItem( item );
}
}
ProcessTooltip(item);
}
if (IsOverSplitter(point))
{
SetCursor( m_leftRightCursor );
}
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::ProcessTooltip( CPropertyItem *item )
{
if (m_tooltip.m_hWnd)
{
if (item && !IsCategory(item))
{
if (item != m_prevTooltipItem)
{
m_tooltip.UpdateTipText( item->GetTip(),this,1 );
m_tooltip.Activate(TRUE);
}
m_prevTooltipItem = item;
}
else
{
m_tooltip.Activate(FALSE);
}
}
}
//////////////////////////////////////////////////////////////////////////
bool CPropertyCtrl::IsCategory( CPropertyItem *item )
{
if (item && (item->GetParent() == m_root || item == m_root) && item->IsExpandable() && !item->IsNotCategory())
return true;
/*
// 2nd rule.
if (item && item->GetType() == ePropertyInvalid && item->IsExpandable())
return true;
*/
return false;
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::Expand( CPropertyItem *item,bool bExpand )
{
assert( item );
item->SetExpanded( bExpand );
Invalidate();
CalcLayout();
}
//////////////////////////////////////////////////////////////////////////
bool CPropertyCtrl::IsOverSplitter( CPoint point )
{
if (point.x >= PROPERTY_LEFT_BORDER+m_splitter-2 && point.x <= PROPERTY_LEFT_BORDER+m_splitter+2)
{
CPropertyItem *item = GetItemFromPoint(point);
if (item && IsCategory(item))
return false;
return true;
}
return false;
}
BOOL CPropertyCtrl::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// TODO: Add your message handler code here and/or call default
if (m_bSplitterDrag)
{
SetCursor( m_leftRightCursor );
return TRUE;
}
return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::CalcLayout()
{
CRect rc;
GetClientRect( rc );
// Set scroll info.
int nPage = rc.Height();
int h = GetVisibleHeight();
SCROLLINFO si;
ZeroStruct(si);
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nMax = h;
si.nPage = nPage;
si.nPos = m_scrollOffset.y;
//si.nPage = max(0,m_rcClient.Width() - LEFT_OFFSET*2);
//si.nPage = 1;
//si.nPage = 1;
FlatSB_SetScrollInfo( GetSafeHwnd(),SB_VERT,&si,TRUE );
FlatSB_GetScrollInfo( GetSafeHwnd(),SB_VERT,&si );
m_scrollOffset.y = si.nPos;
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
SCROLLINFO si;
ZeroStruct(si);
si.cbSize = sizeof(si);
si.fMask = SIF_ALL;
FlatSB_GetScrollInfo( GetSafeHwnd(),SB_VERT,&si );
// Get the minimum and maximum scroll-bar positions.
int minpos = si.nMin;
int maxpos = si.nMax;
int nPage = si.nPage;
// Get the current position of scroll box.
int curpos = si.nPos;
// Determine the new position of scroll box.
switch (nSBCode)
{
case SB_LEFT: // Scroll to far left.
curpos = minpos;
break;
case SB_RIGHT: // Scroll to far right.
curpos = maxpos;
break;
case SB_ENDSCROLL: // End scroll.
break;
case SB_LINELEFT: // Scroll left.
if (curpos > minpos)
curpos--;
break;
case SB_LINERIGHT: // Scroll right.
if (curpos < maxpos)
curpos++;
break;
case SB_PAGELEFT: // Scroll one page left.
if (curpos > minpos)
curpos = max(minpos, curpos - (int)nPage);
break;
case SB_PAGERIGHT: // Scroll one page right.
if (curpos < maxpos)
curpos = min(maxpos, curpos + (int)nPage);
break;
case SB_THUMBPOSITION: // Scroll to absolute position. nPos is the position
curpos = nPos; // of the scroll box at the end of the drag operation.
break;
case SB_THUMBTRACK: // Drag scroll box to specified position. nPos is the
curpos = nPos; // position that the scroll box has been dragged to.
break;
}
// Set the new position of the thumb (scroll box).
FlatSB_SetScrollPos( GetSafeHwnd(),SB_VERT,curpos,TRUE );
m_scrollOffset.y = curpos;
Invalidate();
//CWnd::OnHScroll(nSBCode, nPos, pScrollBar);
}
//////////////////////////////////////////////////////////////////////////
BOOL CPropertyCtrl::PreTranslateMessage(MSG* pMsg)
{
if (!m_tooltip.m_hWnd)
{
CRect rc;
GetClientRect(rc);
m_tooltip.Create( this );
m_tooltip.SetDelayTime( 500 );
m_tooltip.SetMaxTipWidth(600);
m_tooltip.AddTool( this,"",rc,1 );
m_tooltip.Activate(FALSE);
}
m_tooltip.RelayEvent(pMsg);
return CWnd::PreTranslateMessage(pMsg);
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::Init()
{
ModifyStyle( 0,WS_VSCROLL );
ModifyStyle( WS_CLIPCHILDREN|WS_CLIPSIBLINGS,0 );
InitializeFlatSB( GetSafeHwnd() );
FlatSB_SetScrollProp( GetSafeHwnd(),WSB_PROP_CXVSCROLL,14,FALSE );
FlatSB_SetScrollProp( GetSafeHwnd(),WSB_PROP_VSTYLE,FSB_ENCARTA_MODE,FALSE );
FlatSB_EnableScrollBar( GetSafeHwnd(),SB_VERT,ESB_ENABLE_BOTH );
CRect rc;
GetClientRect( rc );
m_splitter = rc.Width()/2;
if (!m_nTimer)
{
m_nTimer = SetTimer( KILLFOCUS_TIMER,500,NULL );
}
}
//////////////////////////////////////////////////////////////////////////
int CPropertyCtrl::GetVisibleHeight()
{
int y = 0;
Items items;
GetVisibleItems( m_root,items );
for (int i = 0; i < items.size(); i++)
{
y += GetItemHeight( items[i] );
}
return y - 2;
}
//////////////////////////////////////////////////////////////////////////
LRESULT CPropertyCtrl::OnGetFont(WPARAM wParam, LPARAM)
{
LRESULT res = Default();
if (!res)
{
res = (LRESULT)GetStockObject(DEFAULT_GUI_FONT);
}
return res;
}
//////////////////////////////////////////////////////////////////////////
BOOL CPropertyCtrl::EnableWindow( BOOL bEnable )
{
SelectItem(0);
return CWnd::EnableWindow( bEnable );
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::OnTimer(UINT_PTR nIDEvent)
{
if (nIDEvent == KILLFOCUS_TIMER)
{
if (m_selected)
{
// Check if need to kill focus.
CWnd *pFocusWnd = GetFocus();
if (pFocusWnd && pFocusWnd != this && !IsChild(pFocusWnd))
{
// And nothing should be captured.
//CWnd *pFocusWndOwner = pFocusWnd->GetOwner();
if (!GetCapture())
{
// Loose selection.
//@TODO: restore
//SelectItem(0);
}
}
}
}
CWnd::OnTimer(nIDEvent);
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::ClearSelection()
{
SelectItem(0);
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::DeleteItem( CPropertyItem *pItem )
{
ClearSelection();
assert( pItem );
// Find this item and delete.
CPropertyItem *pParentItem = pItem->GetParent();
if (pParentItem)
{
pParentItem->RemoveChild( pItem );
CalcLayout();
Invalidate();
}
}
//////////////////////////////////////////////////////////////////////////
CPropertyItem* CPropertyCtrl::FindItemByVar( IVariable *pVar )
{
return m_root->FindItemByVar(pVar);
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::MultiSelectItem( CPropertyItem *pItem )
{
if (!GetSelectedItem())
SelectItem(pItem);
pItem->SetSelected(true);
m_multiSelectedItems.push_back(pItem);
Invalidate();
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::MultiUnselectItem( CPropertyItem *pItem )
{
if (pItem != GetSelectedItem())
{
stl::find_and_erase( m_multiSelectedItems,pItem );
pItem->SetSelected(false);
}
Invalidate();
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::MultiSelectRange( CPropertyItem *pAnchorItem )
{
if (!GetSelectedItem())
{
SelectItem(pAnchorItem);
return;
}
if (pAnchorItem == GetSelectedItem())
return;
int i;
int p1 = -1;
int p2 = -1;
Items items;
GetVisibleItems( m_root,items );
for (i = 0; i < items.size(); i++)
{
if (items[i] == GetSelectedItem())
p1 = i;
if (items[i] == pAnchorItem)
p2 = i;
}
int start = min(p1,p2);
int end = max(p1,p2);
for (i = start; i <= end; i++)
{
MultiSelectItem( items[i] );
}
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::OnCopy( bool bRecursively )
{
if (!m_multiSelectedItems.empty())
{
CClipboard clipboard;
XmlNodeRef rootNode = new CXmlNode("PropertyCtrl");
for (int i = 0; i < m_multiSelectedItems.size(); i++)
{
CPropertyItem *pItem = m_multiSelectedItems[i];
CopyItem( rootNode,pItem,bRecursively );
}
clipboard.Put(rootNode);
}
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::OnCopyAll()
{
if (m_root)
{
CClipboard clipboard;
XmlNodeRef rootNode = new CXmlNode("PropertyCtrl");
for (int i = 0; i < m_root->GetChildCount(); i++)
{
CopyItem( rootNode,m_root->GetChild(i),true );
}
clipboard.Put(rootNode);
}
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::CopyItem( XmlNodeRef rootNode,CPropertyItem *pItem,bool bRecursively )
{
XmlNodeRef node = rootNode->newChild("PropertyItem");
node->setAttr( "Name",pItem->GetFullName() );
node->setAttr( "Value",pItem->GetValue() );
if (bRecursively)
{
for (int i = 0; i < pItem->GetChildCount(); i++)
{
CopyItem( rootNode,pItem->GetChild(i),bRecursively );
}
}
}
//////////////////////////////////////////////////////////////////////////
void CPropertyCtrl::OnPaste()
{
CClipboard clipboard;
CUndo undo( "Paste Properties" );
XmlNodeRef rootNode = clipboard.Get();
if (rootNode != NULL && rootNode->isTag("PropertyCtrl"))
{
for (int i = 0; i < rootNode->getChildCount(); i++)
{
XmlNodeRef node = rootNode->getChild(i);
CString value;
CString name;
node->getAttr( "Name",name );
node->getAttr( "Value",value );
CPropertyItem *pItem = m_root->FindItemByFullName(name);
if (pItem)
pItem->SetValue(value);
}
}
}