Aegisub/assdraw/libpropgrid/propgrid.cpp
Amar Takhar dc404a954e Add UNIX (and with luck, OS X) support to assdraw. This (unfortunatly)
includes propgrid, the build system for propgrid is brutal rather than fend of
users trying to build it themselves it's worth it to just include it with
assdraw for now.

Originally committed to SVN as r2177.
2008-04-13 06:57:09 +00:00

13299 lines
376 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: propgrid.cpp
// Purpose: wxPropertyGrid
// Author: Jaakko Salli
// Modified by:
// Created: Sep-25-2004
// RCS-ID: $Id:
// Copyright: (c) Jaakko Salli
// Licence: wxWindows license
/////////////////////////////////////////////////////////////////////////////
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
#pragma implementation "propgrid.h"
#endif
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/defs.h"
#include "wx/object.h"
#include "wx/hash.h"
#include "wx/string.h"
#include "wx/log.h"
#include "wx/event.h"
#include "wx/window.h"
#include "wx/panel.h"
#include "wx/dc.h"
#include "wx/dcclient.h"
#include "wx/dcmemory.h"
#include "wx/button.h"
#include "wx/pen.h"
#include "wx/brush.h"
#include "wx/cursor.h"
#include "wx/dialog.h"
#include "wx/settings.h"
#include "wx/msgdlg.h"
#include "wx/choice.h"
#include "wx/stattext.h"
#include "wx/scrolwin.h"
#include "wx/dirdlg.h"
#include "wx/combobox.h"
#include "wx/layout.h"
#include "wx/sizer.h"
#include "wx/textdlg.h"
#include "wx/filedlg.h"
#include "wx/statusbr.h"
#include "wx/intl.h"
#include "wx/frame.h"
#endif
#include "wx/timer.h"
#include "wx/dcbuffer.h"
// This define is necessary to prevent macro clearing
#define __wxPG_SOURCE_FILE__
#include <wx/propgrid/propgrid.h>
#include <wx/propgrid/propdev.h>
#ifdef __WXPYTHON__
#include <wx/propgrid/advprops.h>
#include <wx/propgrid/extras.h>
#endif
#if wxPG_USE_RENDERER_NATIVE
#include <wx/renderer.h>
#endif
#include <wx/propgrid/odcombo.h>
#ifdef __WXMSW__
#include <wx/msw/private.h>
#endif
// Two pics for the expand / collapse buttons.
// Files are not supplied with this project (since it is
// recommended to use either custom or native rendering).
// If you want them, get wxTreeMultiCtrl by Jorgen Bodde,
// and copy xpm files from archive to wxPropertyGrid src directory
// (and also comment/undef wxPG_ICON_WIDTH in propGrid.h
// and set wxPG_USE_RENDERER_NATIVE to 0).
#ifndef wxPG_ICON_WIDTH
#if defined(__WXMAC__)
#include "mac_collapse.xpm"
#include "mac_expand.xpm"
#elif defined(__WXGTK__)
#include "linux_collapse.xpm"
#include "linux_expand.xpm"
#else
#include "default_collapse.xpm"
#include "default_expand.xpm"
#endif
#endif
//#define wxPG_TEXT_INDENT 4 // For the wxComboControl
#define wxPG_ALLOW_CLIPPING 1 // If 1, GetUpdateRegion() in OnPaint event handler is not ignored
#define wxPG_GUTTER_DIV 3 // gutter is max(iconwidth/gutter_div,gutter_min)
#define wxPG_GUTTER_MIN 3 // gutter before and after image of [+] or [-]
#define wxPG_YSPACING_MIN 1
#define wxPG_BUTTON_SIZEDEC 0
#define wxPG_DEFAULT_VSPACING 2 // This matches .NET propertygrid's value,
// but causes normal combobox to spill out under MSW
#define wxPG_OPTIMAL_WIDTH 200 // Arbitrary
#define wxPG_CAPRECTXMARGIN 2 // space between caption and selection rectangle,
#define wxPG_CAPRECTYMARGIN 1 // horizontally and vertically
#define PWC_CHILD_SUMMARY_LIMIT 16 // Maximum number of children summarized in a parent property's
// value field.
#define PWC_CHILD_SUMMARY_CHAR_LIMIT 64 // Character limit of summary field when not editing
#define wxPG_MIN_SCROLLBAR_WIDTH 10 // Smallest scrollbar width on any platform
// Must be larger than largest control border
// width * 2.
#define wxPG_DEFAULT_CURSOR wxNullCursor
#define RedrawAllVisible Refresh
//
// Here are some extra platform dependent defines.
//
#if defined(__WXMSW__)
// tested
#define wxPG_DEFAULT_SPLITTERX 110 // default splitter position
#define wxPG_CREATE_CONTROLS_HIDDEN 0 // 1 to create controls out of sight, hide them, and then move them into correct position
#define wxPG_NO_CHILD_EVT_MOTION 0 // 1 if splitter drag detect margin and control cannot overlap
#define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
#define wxPG_ALLOW_EMPTY_TOOLTIPS 1 // If 1, then setting empty tooltip actually hides it
#define wxPG_NAT_TEXTCTRL_BORDER_X 0 // Unremovable border of native textctrl.
#define wxPG_NAT_TEXTCTRL_BORDER_Y 0 // Unremovable border of native textctrl.
#define wxPG_NAT_BUTTON_BORDER_ANY 1
#define wxPG_NAT_BUTTON_BORDER_X 1
#define wxPG_NAT_BUTTON_BORDER_Y 1
#define wxPG_TEXTCTRLYADJUST (m_spacingy+0)
#define wxPG_CHOICEXADJUST (-1) // Extra pixels next to wxChoice/ComboBox.
#define wxPG_CHOICEYADJUST 0 // Extra pixels above wxChoice/ComboBox.
#define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 0 // If 1 then controls are refreshed after selected was drawn.
#define wxPG_CHECKMARK_XADJ 1
#define wxPG_CHECKMARK_YADJ (-1)
#define wxPG_CHECKMARK_WADJ 0
#define wxPG_CHECKMARK_HADJ 0
#define wxPG_CHECKMARK_DEFLATE 0
#elif defined(__WXGTK__)
// tested
#define wxPG_DEFAULT_SPLITTERX 110
#define wxPG_CREATE_CONTROLS_HIDDEN 0 // 1 to create controls out of sight, hide them, and then move them into correct position
#define wxPG_NO_CHILD_EVT_MOTION 1 // 1 if splitter drag detect margin and control cannot overlap
#define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
#define wxPG_ALLOW_EMPTY_TOOLTIPS 0 // If 1, then setting empty tooltip actually hides it
#define wxPG_NAT_TEXTCTRL_BORDER_X 3 // Unremovable border of native textctrl.
#define wxPG_NAT_TEXTCTRL_BORDER_Y 3 // Unremovable border of native textctrl.
#define wxPG_NAT_BUTTON_BORDER_ANY 1
#define wxPG_NAT_BUTTON_BORDER_X 1
#define wxPG_NAT_BUTTON_BORDER_Y 1
#define wxPG_TEXTCTRLYADJUST 0
#define wxPG_CHOICEXADJUST 2 // Extra pixels next to wxChoice/ComboBox.
#define wxPG_CHOICEYADJUST 0
#define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 1 // If 1 then controls are refreshed after selected was drawn.
#define wxPG_CHECKMARK_XADJ 0
#define wxPG_CHECKMARK_YADJ 0
#define wxPG_CHECKMARK_WADJ (-1)
#define wxPG_CHECKMARK_HADJ (-1)
#define wxPG_CHECKMARK_DEFLATE 3
#elif defined(__WXMAC__)
// *not* tested
#define wxPG_DEFAULT_SPLITTERX 110
#define wxPG_CREATE_CONTROLS_HIDDEN 0 // 1 to create controls out of sight, hide them, and then move them into correct position
#define wxPG_NO_CHILD_EVT_MOTION 0 // 1 if splitter drag detect margin and control cannot overlap
#define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
#define wxPG_ALLOW_EMPTY_TOOLTIPS 1 // If 1, then setting empty tooltip actually hides it
#define wxPG_NAT_TEXTCTRL_BORDER_X 0 // Unremovable border of native textctrl.
#define wxPG_NAT_TEXTCTRL_BORDER_Y 0 // Unremovable border of native textctrl.
#define wxPG_NAT_BUTTON_BORDER_ANY 0
#define wxPG_NAT_BUTTON_BORDER_X 0
#define wxPG_NAT_BUTTON_BORDER_Y 0
#define wxPG_TEXTCTRLYADJUST 3
#define wxPG_CHOICEXADJUST 0 // Extra pixels next to wxChoice/ComboBox.
#define wxPG_CHOICEYADJUST 0
#define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 0 // If 1 then controls are refreshed after selected was drawn.
#define wxPG_CHECKMARK_XADJ 0
#define wxPG_CHECKMARK_YADJ 0
#define wxPG_CHECKMARK_WADJ 0
#define wxPG_CHECKMARK_HADJ 0
#define wxPG_CHECKMARK_DEFLATE 0
#else
// defaults
#define wxPG_DEFAULT_SPLITTERX 110
#define wxPG_CREATE_CONTROLS_HIDDEN 0 // 1 to create controls out of sight, hide them, and then move them into correct position
#define wxPG_NO_CHILD_EVT_MOTION 1 // 1 if splitter drag detect margin and control cannot overlap
#define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
#define wxPG_ALLOW_EMPTY_TOOLTIPS 0 // If 1, then setting empty tooltip actually hides it
#define wxPG_NAT_TEXTCTRL_BORDER_X 0 // Unremovable border of native textctrl.
#define wxPG_NAT_TEXTCTRL_BORDER_Y 0 // Unremovable border of native textctrl.
#define wxPG_NAT_BUTTON_BORDER_ANY 0
#define wxPG_NAT_BUTTON_BORDER_X 0
#define wxPG_NAT_BUTTON_BORDER_Y 0
#define wxPG_TEXTCTRLYADJUST 0
#define wxPG_CHOICEXADJUST 0 // Extra pixels next to wxChoice/ComboBox.
#define wxPG_CHOICEYADJUST 0
#define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 1 // If 1 then controls are refreshed after selected was drawn.
#define wxPG_CHECKMARK_XADJ 0
#define wxPG_CHECKMARK_YADJ 0
#define wxPG_CHECKMARK_WADJ 0
#define wxPG_CHECKMARK_HADJ 0
#define wxPG_CHECKMARK_DEFLATE 0
#endif
#if wxPG_NO_CHILD_EVT_MOTION
#define wxPG_SPLITTERX_DETECTMARGIN1 3 // this much on left
#define wxPG_SPLITTERX_DETECTMARGIN2 2 // this much on right
#define wxPG_CONTROL_MARGIN 0 // space between splitter and control
#else
#define wxPG_SPLITTERX_DETECTMARGIN1 3 // this much on left
#define wxPG_SPLITTERX_DETECTMARGIN2 2 // this much on right
#define wxPG_CONTROL_MARGIN 0 // space between splitter and control
#endif
#define wxCC_CUSTOM_IMAGE_MARGIN1 4 // before image
#define wxCC_CUSTOM_IMAGE_MARGIN2 5 // after image
#if (!wxPG_NAT_TEXTCTRL_BORDER_X && !wxPG_NAT_TEXTCTRL_BORDER_Y)
#define wxPG_ENABLE_CLIPPER_WINDOW 0
#else
#define wxPG_ENABLE_CLIPPER_WINDOW 1
#endif
//#define wxPG_NAT_CHOICE_BORDER_ANY 0
// for odcombo
#undef wxPG_CHOICEXADJUST
#define wxPG_CHOICEXADJUST 0
#undef wxPG_CHOICEYADJUST
#define wxPG_CHOICEYADJUST 0
#define wxPG_DRAG_MARGIN 30
#define wxPG_CUSTOM_IMAGE_SPACINGY 1 // space between vertical sides of a custom image
// Use this macro to generate standard custom image height from
#define wxPG_STD_CUST_IMAGE_HEIGHT(LINEHEIGHT) (LINEHEIGHT-3)
// How many pixels between textctrl and button
#ifdef __WXMAC__
#define wxPG_TEXTCTRL_AND_BUTTON_SPACING 8
#else
#define wxPG_TEXTCTRL_AND_BUTTON_SPACING 2
#endif
#define wxPG_HIDER_BUTTON_HEIGHT 25
// m_expanded of wxPGPropertyWithChildren is set to this code if children should
// not be deleted in destructor.
#define wxPG_EXP_OF_COPYARRAY 127
#define wxPG_PIXELS_PER_UNIT m_lineHeight
#ifdef wxPG_ICON_WIDTH
#define m_iconHeight m_iconWidth
#endif
#define wxPG_TOOLTIP_DELAY 1000
// Colour for the empty but visible space below last property.
#define wxPG_SLACK_BACKROUND m_colPropBack
// Milliseconds to wait for two mouse-ups after focus inorder
// to trigger a double-click.
#define DOUBLE_CLICK_CONVERSION_TRESHOLD 500
//
// Parenting types
enum
{
PT_CUSTOMPROPERTY = -2,
PT_FIXEDCHILDREN = -1,
PT_NONE = 0,
PT_CAPTION = 1,
PT_ROOT = 2
};
// Helper to decide which way is better (ie. first macro clears
// "unspecified" state of siblings of child properties as well, while the latter is
// more precise).
//#define CLEAR_PROPERTY_UNSPECIFIED_FLAG(p) wxPropertyGridState::ClearPropertyAndChildrenFlags(p,wxPG_PROP_UNSPECIFIED)
#define CLEAR_PROPERTY_UNSPECIFIED_FLAG(p) p->ClearFlag(wxPG_PROP_UNSPECIFIED)
#define __INTENSE_DEBUGGING__ 0
#define __PAINT_DEBUGGING__ 0
#define __MOUSE_DEBUGGING__ 0
// -----------------------------------------------------------------------
#if wxUSE_INTL
void wxPropertyGrid::AutoGetTranslation ( bool enable )
{
WX_PG_GLOBALS_LOCKER()
wxPGGlobalVars->m_autoGetTranslation = enable;
}
#else
void wxPropertyGrid::AutoGetTranslation ( bool ) { }
#endif
// -----------------------------------------------------------------------
// This was needed to make quicker progress towards wxPropertyGridState
#define FROM_STATE(X) m_pState->X
// -----------------------------------------------------------------------
#if !wxCHECK_VERSION(2, 7, 1)
#if defined(__WXMSW__)
#ifndef WS_EX_COMPOSITED
#define WS_EX_COMPOSITED 0x02000000L
#endif
static bool wxPGIsWindowBuffered( const wxWindow* wnd )
{
while ( wnd )
{
if ( GetWindowLong((HWND)wnd->GetHWND(), GWL_EXSTYLE) & WS_EX_COMPOSITED )
return true;
if ( wnd->IsTopLevel() )
break;
wnd = wnd->GetParent();
}
return false;
}
#elif defined(__WXGTK20__)
#include <gtk/gtk.h>
static bool wxPGIsWindowBuffered( const wxWindow* wnd )
{
return GTK_WIDGET_DOUBLE_BUFFERED(wnd->GetHandle());
}
#elif defined(__WXMAC_OSX__) || defined(__WXCOCOA__) || defined(__WXDFB__)
static bool wxPGIsWindowBuffered( const wxWindow* WXUNUSED(wnd) )
{
return true;
}
#else
static bool wxPGIsWindowBuffered( const wxWindow* WXUNUSED(wnd) )
{
return false;
}
#endif
#else
static bool wxPGIsWindowBuffered( const wxWindow* wnd )
{
return wnd->IsDoubleBuffered();
}
#endif
// -----------------------------------------------------------------------
// DeviceContext Init Macros.
#define wxPG_CLIENT_DC_INIT() \
wxClientDC dc(this); \
PrepareDC(dc);
#define wxPG_CLIENT_DC_INIT_R(RETVAL) \
wxClientDC dc(this); \
PrepareDC(dc);
#define wxPG_PAINT_DC_INIT() \
wxPaintDC dc(this); \
PrepareDC(dc);
// -----------------------------------------------------------------------
// For wxMSW cursor consistency, we must do mouse capturing even
// when using custom controls.
#define BEGIN_MOUSE_CAPTURE \
if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) ) \
{ \
CaptureMouse(); \
m_iFlags |= wxPG_FL_MOUSE_CAPTURED; \
}
#define END_MOUSE_CAPTURE \
if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) \
{ \
ReleaseMouse(); \
m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED); \
}
// -----------------------------------------------------------------------
// NOTES
// -----------------------------------------------------------------------
//
// -----------------------------------------------------------------------
// TODO
// -----------------------------------------------------------------------
//
//
// For Next Release:
// * Fix NULL(?) focus after odcombo closed.
//
// -----------------------------------------------------------------------
const wxChar *wxPropertyGridNameStr = wxT("wxPropertyGrid");
const wxChar *wxPGTypeName_long = wxT("long");
const wxChar *wxPGTypeName_bool = wxT("bool");
const wxChar *wxPGTypeName_double = wxT("double");
const wxChar *wxPGTypeName_wxString = wxT("string");
const wxChar *wxPGTypeName_void = wxT("void*");
const wxChar *wxPGTypeName_wxArrayString = wxT("arrstring");
#ifdef __WXPYTHON__
const wxChar *wxPGTypeName_PyObject = wxT("PyObject");
#endif
// -----------------------------------------------------------------------
static void wxPGDrawFocusRect( wxDC& dc, const wxRect& rect )
{
#if defined(__WXMSW__) && !defined(__WXWINCE__)
/*
RECT mswRect;
mswRect.left = rect.x;
mswRect.top = rect.y;
mswRect.right = rect.x + rect.width;
mswRect.bottom = rect.y + rect.height;
HDC hdc = (HDC) dc.GetHDC();
SetMapMode(hdc,MM_TEXT); // Just in case...
DrawFocusRect(hdc,&mswRect);
*/
// FIXME: Use DrawFocusRect code above (currently it draws solid line
// for caption focus but works ok for other stuff).
// Also, it seems that this code may not work in future wx versions.
dc.SetLogicalFunction(wxINVERT);
wxPen pen(*wxBLACK,1,wxDOT);
pen.SetCap(wxCAP_BUTT);
dc.SetPen(pen);
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(rect);
dc.SetLogicalFunction(wxCOPY);
#else
dc.SetLogicalFunction(wxINVERT);
dc.SetPen(wxPen(*wxBLACK,1,wxDOT));
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(rect);
dc.SetLogicalFunction(wxCOPY);
#endif
}
// -----------------------------------------------------------------------
// Choice related methods from various classes
// -----------------------------------------------------------------------
void wxPropertyContainerMethods::AddPropertyChoice( wxPGId id,
const wxString& label,
int value )
{
wxPG_PROP_ID_CALL_PROLOG()
p->InsertChoice(label,-1,value);
}
void wxPropertyContainerMethods::InsertPropertyChoice( wxPGId id,
const wxString& label,
int index,
int value )
{
wxPG_PROP_ID_CALL_PROLOG()
p->InsertChoice(label,index,value);
}
void wxPropertyContainerMethods::DeletePropertyChoice( wxPGId id,
int index )
{
wxPG_PROP_ID_CALL_PROLOG()
p->DeleteChoice(index);
}
// -----------------------------------------------------------------------
// Statics in one class for easy destruction.
// NB: We prefer to use wxModule, as it offers more consistent behaviour
// across platforms. However, for those rare problem situations, we
// also need to offer option to use simpler approach.
// -----------------------------------------------------------------------
#ifndef wxPG_USE_WXMODULE
#define wxPG_USE_WXMODULE 1
#endif
#if wxPG_USE_WXMODULE
#include <wx/module.h>
class wxPGGlobalVarsClassManager : public wxModule
{
DECLARE_DYNAMIC_CLASS(wxPGGlobalVarsClassManager)
public:
wxPGGlobalVarsClassManager() {}
virtual bool OnInit() { wxPGGlobalVars = new wxPGGlobalVarsClass(); return true; }
virtual void OnExit() { delete wxPGGlobalVars; wxPGGlobalVars = NULL; }
};
IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager, wxModule)
#else // !wxPG_USE_WXMODULE
class wxPGGlobalVarsClassManager
{
public:
wxPGGlobalVarsClassManager() {}
~wxPGGlobalVarsClassManager() { delete wxPGGlobalVars; }
};
static wxPGGlobalVarsClassManager gs_pgGlobalVarsClassManager;
#endif
wxPGGlobalVarsClass* wxPGGlobalVars = (wxPGGlobalVarsClass*) NULL;
wxPGGlobalVarsClass::wxPGGlobalVarsClass()
{
m_boolChoices[0] = _("False");
m_boolChoices[1] = _("True");
m_boolChoices[2] = _("Unspecified");
m_numBoolChoices = 2;
m_fontFamilyChoices = (wxPGChoices*) NULL;
m_autoGetTranslation = false;
m_offline = 0;
}
wxPGGlobalVarsClass::~wxPGGlobalVarsClass()
{
size_t i;
// This will always have one ref
delete m_fontFamilyChoices;
#if wxUSE_VALIDATORS
for ( i=0; i<m_arrValidators.GetCount(); i++ )
delete ((wxValidator*)m_arrValidators[i]);
#endif
//
// Destroy value type class instances.
wxPGHashMapS2P::iterator vt_it;
for( vt_it = m_dictValueType.begin(); vt_it != m_dictValueType.end(); ++vt_it )
{
wxPGValueType* pcls = (wxPGValueType*) vt_it->second;
wxASSERT( pcls );
delete pcls;
}
// Destroy editor class instances.
// iterate over all the elements in the class
for( vt_it = m_mapEditorClasses.begin(); vt_it != m_mapEditorClasses.end(); ++vt_it )
{
delete ((wxPGEditor*)vt_it->second);
}
}
// -----------------------------------------------------------------------
// wxPGProperty
// -----------------------------------------------------------------------
wxPGPropertyClassInfo wxBasePropertyClassInfo = {wxT("wxBaseProperty"),
(const wxPGPropertyClassInfo*) NULL,
(wxPGPropertyConstructor) NULL};
void wxPGProperty::Init()
{
#ifdef __WXPYTHON__
m_scriptObject = NULL;
#endif
m_y = -3;
m_arrIndex = 0xFFFF;
m_parent = (wxPGPropertyWithChildren*) NULL;
#if wxPG_USE_CLIENT_DATA
m_clientData = NULL;
#endif
m_dataExt = (wxPGPropertyDataExt*) NULL;
m_maxLen = 0; // infinite maximum length
m_flags = 0;
m_depth = 1;
m_parentingType = 0;
m_bgColIndex = 0;
m_fgColIndex = 0;
}
void wxPGProperty::Init( const wxString& label, const wxString& name )
{
m_label = label;
#ifndef __WXPYTHON__
if ( &name != ((wxString*)NULL) )
#else
if ( (&name != ((wxString*)NULL)) && name != wxT("_LABEL_AS_NAME") )
#endif
DoSetName( name );
else
DoSetName( label );
Init();
}
wxPGProperty::wxPGProperty()
#if wxPG_INCLUDE_WXOBJECT
: wxObject()
#endif
{
Init();
}
wxPGProperty::wxPGProperty( const wxString& label, const wxString& name )
#if wxPG_INCLUDE_WXOBJECT
: wxObject()
#endif
{
Init( label, name );
}
wxPGProperty::~wxPGProperty()
{
#ifdef __WXPYTHON__
#if wxPG_USE_CLIENT_DATA
if ( m_clientData )
Py_DECREF( m_clientData );
#endif
#endif
delete m_dataExt;
}
bool wxPGProperty::IsSomeParent( wxPGProperty* candidate ) const
{
wxPGPropertyWithChildren* parent = m_parent;
do
{
if ( parent == (wxPGPropertyWithChildren*)candidate )
return true;
parent = parent->m_parent;
} while ( parent );
return false;
}
wxPropertyGridState* wxPGProperty::GetParentState() const
{
wxASSERT( m_parent );
return m_parent->GetParentState();
}
size_t wxPGProperty::GetChildCount() const
{
int cc = GetParentingType();
if ( cc == 0 ) return 0;
return ((wxPGPropertyWithChildren*)this)->GetCount();
}
void wxPGProperty::ShowError( const wxString& msg )
{
if ( !msg.length() )
return;
#if wxUSE_STATUSBAR
if ( !wxPGGlobalVars->m_offline )
{
wxPropertyGrid* pg = GetParentState()->m_pPropGrid;
wxASSERT(pg);
wxWindow* topWnd = ::wxGetTopLevelParent(pg);
if ( topWnd )
{
wxFrame* pFrame = wxDynamicCast(topWnd,wxFrame);
if ( pFrame )
{
wxStatusBar* pStatusBar = pFrame->GetStatusBar();
if ( pStatusBar )
{
pStatusBar->SetStatusText(msg);
return;
}
}
}
}
#endif
::wxLogError(msg);
}
wxPropertyGrid* wxPGProperty::GetGrid() const
{
return GetParentState()->GetGrid();
}
void wxPGProperty::UpdateControl( wxWindow* primary )
{
if ( primary )
GetEditorClass()->UpdateControl(this,primary);
}
void wxPGProperty::DoSetValue( wxPGVariant )
{
// Actually, this should never get called
wxFAIL_MSG( wxT("must be overridden") );
}
// wxPGRootPropertyClass, at least, should make use of this.
wxPGVariant wxPGProperty::DoGetValue() const
{
return wxPGVariant((long)0);
}
wxString wxPGProperty::GetValueAsString( int ) const
{
wxFAIL_MSG( wxT("must be overridden") );
return m_name;
}
wxVariant wxPGProperty::GetValueAsVariant() const
{
// Return NULL variant for unspecified value
//if ( HasFlag(wxPG_PROP_UNSPECIFIED) )
// return wxVariant();
wxPGVariant value = DoGetValue();
const wxPGValueType* typeClass = GetValueTypePtr();
wxASSERT_MSG( typeClass, wxT("Did you forgot to use wxPG_INIT_REQUIRED_TYPE(T) in constructor?") );
return typeClass->GenerateVariant(value,m_name);
}
bool wxPGProperty::SetValueFromString( const wxString&, int )
{
wxFAIL_MSG( wxT("must be overridden") );
return false;
}
bool wxPGProperty::SetValueFromInt( long, int )
{
wxFAIL_MSG ( wxT("must be overridden") );
return false;
}
wxSize wxPGProperty::GetImageSize() const
{
if ( m_dataExt && m_dataExt->m_valueBitmap )
return wxSize(m_dataExt->m_valueBitmap->GetWidth(),-1);
return wxSize(0,0);
}
void wxPGProperty::OnCustomPaint( wxDC& dc,
const wxRect& rect,
wxPGPaintData& )
{
wxCHECK_RET( m_dataExt, wxT("m_dataExt is mandatory") );
wxBitmap* bmp = m_dataExt->m_valueBitmap;
wxCHECK_RET( bmp && bmp->Ok(), wxT("invalid bitmap") );
wxCHECK_RET( rect.x >= 0, wxT("unexpected measure call") );
dc.DrawBitmap(*bmp,rect.x,rect.y);
}
const wxPGEditor* wxPGProperty::DoGetEditorClass() const
{
return wxPG_EDITOR(TextCtrl);
}
#ifdef __WXPYTHON__
wxString wxPGProperty::GetEditor() const
{
return wxEmptyString;
}
#endif
#ifdef __WXPYTHON__
wxString wxPGProperty::GetType() const
{
return wxString();
}
const wxPGValueType* wxPGProperty::GetValueType() const
{
wxString s = GetType();
const wxPGValueType* p = wxPropertyContainerMethods::GetValueType(s);
wxCHECK_MSG( p, wxPG_VALUETYPE(none),
wxT("GetType must return string that identifies a valid type") );
return p;
}
#endif
#if wxPG_VALUETYPE_IS_STRING
const wxPGValueType* wxPGProperty::GetValueTypePtr() const
{
return wxPropertyContainerMethods::GetValueTypeByName(GetValueType());
}
#endif
// Default extra property event handling - that is, none at all.
bool wxPGProperty::OnEvent( wxPropertyGrid*, wxWindow*, wxEvent& )
{
return false;
}
void wxPGProperty::SetChoiceSelection( int newValue, const wxPGChoiceInfo& choiceInfo )
{
// Changes value of a property with choices, but only
// works if the value type is long or string.
const wxPGValueType* vt = GetValueTypePtr();
wxCHECK_RET( choiceInfo.m_choices, wxT("invalid choiceinfo") );
if ( vt == wxPG_VALUETYPE_PTR(long) )
{
DoSetValue( (long) newValue );
}
else if ( vt == wxPG_VALUETYPE_PTR(wxString) )
{
DoSetValue( choiceInfo.m_choices->GetLabel(newValue) );
}
}
int wxPGProperty::InsertChoice( const wxString& label, int index, int value )
{
wxPropertyGrid* pg = GetGrid();
wxPGChoiceInfo ci;
ci.m_choices = (wxPGChoices*) NULL;
int sel = GetChoiceInfo(&ci);
if ( ci.m_choices )
{
int newSel = sel;
if ( index < 0 )
index = ci.m_choices->GetCount();
if ( index <= sel )
newSel++;
ci.m_choices->Insert(label, index, value);
if ( sel != newSel )
SetChoiceSelection(newSel, ci);
if ( this == wxPGIdToPtr(pg->GetSelection()) )
GetEditorClass()->InsertItem(pg->GetPrimaryEditor(),label,index);
return index;
}
return -1;
}
void wxPGProperty::DeleteChoice( int index )
{
wxPropertyGrid* pg = GetGrid();
wxPGChoiceInfo ci;
ci.m_choices = (wxPGChoices*) NULL;
int sel = GetChoiceInfo(&ci);
if ( ci.m_choices )
{
int newSel = sel;
// Adjust current value
if ( sel == index )
{
SetFlag( wxPG_PROP_UNSPECIFIED );
newSel = 0;
}
else if ( index < sel )
{
newSel--;
}
ci.m_choices->RemoveAt(index);
if ( sel != newSel )
SetChoiceSelection(newSel, ci);
if ( this == wxPGIdToPtr(pg->GetSelection()) )
GetEditorClass()->DeleteItem(pg->GetPrimaryEditor(), index);
}
}
int wxPGProperty::GetChoiceInfo( wxPGChoiceInfo* )
{
return 0;
}
void wxPGProperty::SetAttribute( int, wxVariant& )
{
}
#if wxUSE_VALIDATORS
wxValidator* wxPGProperty::DoGetValidator() const
{
return (wxValidator*) NULL;
}
#endif
bool wxPGProperty::SetChoices( wxPGChoices& choices )
{
wxPGChoiceInfo ci;
ci.m_choices = (wxPGChoices*) NULL;
// Unref existing
GetChoiceInfo(&ci);
if ( ci.m_choices )
{
ci.m_choices->Assign(choices);
// This may be needed to trigger some initialization
// (but don't do it if property is somewhat uninitialized)
if ( m_parent )
DoSetValue(GetValueTypePtr()->GetDefaultValue());
return true;
}
return false;
}
const wxPGEditor* wxPGProperty::GetEditorClass() const
{
const wxPGEditor* editor;
if ( !m_dataExt || !m_dataExt->m_customEditor )
{
#ifdef __WXPYTHON__
wxString editorName = GetEditor();
if ( editorName.length() )
editor = wxPropertyContainerMethods::GetEditorByName(editorName);
else
#endif
editor = DoGetEditorClass();
}
else
{
editor = m_dataExt->m_customEditor;
}
return editor;
}
bool wxPGProperty::IsKindOf( wxPGPropertyClassInfo& info )
{
const wxPGPropertyClassInfo* ownInfo = GetClassInfo();
do
{
if ( ownInfo == &info )
return true;
ownInfo = ownInfo->m_baseInfo;
} while ( ownInfo );
return false;
}
// Privatizes set of choices
void wxPGProperty::SetChoicesExclusive()
{
wxPGChoiceInfo ci;
ci.m_choices = (wxPGChoices*) NULL;
GetChoiceInfo(&ci);
if ( ci.m_choices )
ci.m_choices->SetExclusive();
}
bool wxPGProperty::PrepareValueForDialogEditing( wxPropertyGrid* propGrid )
{
wxWindow* primary = propGrid->GetEditorControl();
if ( primary && propGrid->IsEditorsValueModified() )
{
GetEditorClass()->CopyValueFromControl( this, primary );
return true;
}
else if ( m_flags & wxPG_PROP_UNSPECIFIED )
{
// Set default value in case it was unspecified
DoSetValue(GetValueTypePtr()->GetDefaultValue());
}
return false;
}
bool wxPGProperty::RecreateEditor()
{
wxPropertyGrid* pg = GetGrid();
wxASSERT(pg);
wxPGProperty* selected = pg->GetSelection();
if ( this == selected )
{
pg->DoSelectProperty(this, wxPG_SEL_FORCE);
return true;
}
return false;
}
bool wxPGProperty::EnsureDataExt()
{
if ( !m_dataExt )
{
m_dataExt = new wxPGPropertyDataExt();
return true;
}
return false;
}
void wxPGProperty::SetValueImage( wxBitmap& bmp )
{
EnsureDataExt();
delete m_dataExt->m_valueBitmap;
if ( &bmp && bmp.Ok() )
{
// Resize the image
wxSize maxSz = GetGrid()->GetImageSize();
wxSize imSz(bmp.GetWidth(),bmp.GetHeight());
if ( imSz.x != maxSz.x || imSz.y != maxSz.y )
{
// Create a memory DC
wxBitmap* bmpNew = new wxBitmap(maxSz.x,maxSz.y,bmp.GetDepth());
wxMemoryDC dc;
dc.SelectObject(*bmpNew);
// Scale
// FIXME: This is ugly - use image or wait for scaling patch.
double scaleX = (double)maxSz.x / (double)imSz.x;
double scaleY = (double)maxSz.y / (double)imSz.y;
dc.SetUserScale(scaleX,scaleY);
dc.DrawBitmap( bmp, 0, 0 );
m_dataExt->m_valueBitmap = bmpNew;
}
else
m_dataExt->m_valueBitmap = new wxBitmap(bmp);
m_flags |= wxPG_PROP_CUSTOMIMAGE;
}
else
{
m_dataExt->m_valueBitmap = (wxBitmap*) NULL;
m_flags &= ~(wxPG_PROP_CUSTOMIMAGE);
}
}
wxPGProperty* wxPGProperty::GetMainParent() const
{
const wxPGProperty* curChild = this;
const wxPGPropertyWithChildren* curParent = m_parent;
while ( curParent->m_parentingType < 0 )
{
curChild = curParent;
curParent = curParent->m_parent;
}
return (wxPGProperty*) curChild;
}
const wxPGProperty* wxPGProperty::GetLastVisibleSubItem() const
{
//
// Returns last visible sub-item, recursively.
if ( GetParentingType() == PT_NONE )
return this;
const wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*) this;
unsigned int count = pwc->GetCount();
if ( !pwc->IsExpanded() || !count )
return this;
return pwc->Last()->GetLastVisibleSubItem();
}
bool wxPGProperty::UsesAutoUnspecified() const
{
if ( GetGrid()->GetExtraStyle() & wxPG_EX_AUTO_UNSPECIFIED_VALUES )
return true;
return false;
}
// -----------------------------------------------------------------------
// wxPGPropertyWithChildren
// -----------------------------------------------------------------------
wxPGPropertyClassInfo wxBaseParentPropertyClassInfo = {wxT("wxBaseParentProperty"),
&wxBasePropertyClassInfo,
(wxPGPropertyConstructor) NULL};
wxPGPropertyWithChildren::wxPGPropertyWithChildren()
: wxPGProperty()
{
m_expanded = 1;
m_y = -2;
m_parentingType = -1;
}
wxPGPropertyWithChildren::wxPGPropertyWithChildren( const wxString &label, const wxString& name )
: wxPGProperty(label,name)
{
m_expanded = 1;
m_y = -2;
m_parentingType = -1;
m_parentState = (wxPropertyGridState*) NULL;
}
wxPGPropertyWithChildren::~wxPGPropertyWithChildren()
{
Empty(); // this deletes items
}
// This is used by Insert etc.
void wxPGPropertyWithChildren::AddChild2( wxPGProperty* prop, int index, bool correct_mode )
{
if ( index < 0 || (size_t)index >= m_children.GetCount() )
{
if ( correct_mode ) prop->m_arrIndex = m_children.GetCount();
m_children.Add( (void*)prop );
}
else
{
m_children.Insert( (void*)prop, index );
if ( correct_mode ) FixIndexesOfChildren( index );
}
prop->m_parent = this;
}
// This is used by properties that have fixed sub-properties
void wxPGPropertyWithChildren::AddChild( wxPGProperty* prop )
{
prop->m_arrIndex = m_children.GetCount();
m_children.Add( (void*)prop );
int custImgHeight = prop->GetImageSize().y;
if ( custImgHeight < 0 /*|| custImgHeight > 1*/ )
prop->m_flags |= wxPG_PROP_CUSTOMIMAGE;
prop->m_parent = this;
prop->m_y = -1; // Collapsed
}
void wxPGPropertyWithChildren::FixIndexesOfChildren( size_t starthere )
{
size_t i;
for ( i=starthere;i<GetCount();i++)
Item(i)->m_arrIndex = i;
}
// Returns (direct) child property with given name (or NULL if not found)
wxPGProperty* wxPGPropertyWithChildren::GetPropertyByName( const wxString& name ) const
{
size_t i;
for ( i=0; i<GetCount(); i++ )
{
wxPGProperty* p = Item(i);
if ( p->m_name == name )
return p;
}
// Does it have point, then?
int pos = name.Find(wxT('.'));
if ( pos <= 0 )
return (wxPGProperty*) NULL;
wxPGPropertyWithChildren* pwc =
(wxPGPropertyWithChildren*) GetPropertyByName(name.substr(0,pos));
if ( !pwc || !pwc->GetParentingType() )
return (wxPGProperty*) NULL;
return pwc->GetPropertyByName(name.substr(pos+1,name.length()-pos-1));
}
wxPGProperty* wxPGPropertyWithChildren::GetItemAtY( unsigned int y, unsigned int lh )
{
// Linear search.
unsigned int i = 0;
unsigned int iMax = GetCount();
unsigned long py = 0xFFFFFFFF;
wxPGProperty* p = (wxPGProperty*) NULL;
while ( i < iMax )
{
p = Item(i);
if ( p->m_y >= 0 )
{
py = (unsigned long)p->m_y;
if ( (py+lh) > y )
break;
}
i++;
}
if ( py <= y && i < iMax )
{
// perfectly this item
wxASSERT_MSG( p, wxT("invalid property id") );
return p;
}
else
{
// If no visible children, we must retract our steps
// (should not really happen, so right now we check that it
// really doesn't).
if ( py == 0xFFFFFFFF )
{
wxLogDebug(wxT("wxPropertyGrid: \"%s\" (y=%i) did not have visible children (it should)."),m_label.c_str(),(int)m_y);
return (wxPGProperty*) NULL;
}
// We are about to return a child of previous' visible item.
#ifdef __WXDEBUG__
if ( i < 1 )
{
wxLogDebug( wxT("WARNING: \"%s\"->GetItemAtY: (i <= 0)"), m_label.c_str() );
wxLogDebug( wxT(" \\--> y = %i, py = %i"), (int)y, (int)py );
if ( p )
wxLogDebug( wxT(" \\--> p = \"%s\""), p->GetLabel().c_str() );
else
wxLogDebug( wxT(" \\--> p = None") );
return (wxPGProperty*) NULL;
}
#endif
// Get previous *visible* parent.
wxPGPropertyWithChildren* pwc;
do
{
wxASSERT( i > 0 );
i--;
pwc = (wxPGPropertyWithChildren*)Item(i);
} while ( pwc->m_y < 0 );
if ( pwc->GetParentingType() != 0 )
{
#ifdef __WXDEBUG__
if ( !pwc->m_expanded || pwc->m_y < 0 )
{
wxLogDebug(wxT("WARNING: wxPGPropertyWithChildren::GetItemAtY: Item %s should have been visible and expanded."),pwc->m_label.c_str());
wxLogDebug(wxT(" (%s[%i]: %s)"),pwc->m_parent->m_label.c_str(),pwc->m_arrIndex,pwc->m_label.c_str());
//wxLogDebug(wxT(" py=%i"),(int)py);
return (wxPGProperty*) NULL;
}
#endif
return pwc->GetItemAtY(y,lh);
}
}
return (wxPGProperty*) NULL;
}
void wxPGPropertyWithChildren::Empty()
{
size_t i;
if ( m_expanded != wxPG_EXP_OF_COPYARRAY )
{
for ( i=0; i<GetCount(); i++ )
{
wxPGProperty* p = (wxPGProperty*) Item(i);
delete p;
}
}
m_children.Empty();
}
void wxPGPropertyWithChildren::ChildChanged( wxPGProperty* WXUNUSED(p) )
{
}
wxString wxPGPropertyWithChildren::GetValueAsString( int argFlags ) const
{
wxCHECK_MSG( GetCount() > 0,
wxString(),
wxT("If user property does not have any children, it must override GetValueAsString.") );
wxString text;
int i;
int iMax = m_children.GetCount();
if ( iMax > PWC_CHILD_SUMMARY_LIMIT &&
!(argFlags & wxPG_FULL_VALUE) )
iMax = PWC_CHILD_SUMMARY_LIMIT;
int iMaxMinusOne = iMax-1;
wxPGProperty* curChild = (wxPGProperty*) m_children.Item(0);
for ( i = 0; i < iMax; i++ )
{
wxString s;
if ( !(curChild->m_flags & wxPG_PROP_UNSPECIFIED) )
s = curChild->GetValueAsString(argFlags);
if ( curChild->GetParentingType() == 0 )
text += s;
else
text += wxT("[") + s + wxT("]");
if ( i < iMaxMinusOne )
{
if ( text.length() > PWC_CHILD_SUMMARY_CHAR_LIMIT &&
!(argFlags & wxPG_EDITABLE_VALUE) &&
!(argFlags & wxPG_FULL_VALUE) )
break;
curChild = (wxPGProperty*) m_children.Item(i+1);
if ( curChild->GetParentingType() == 0 )
text += wxT("; ");
else
text += wxT(" ");
}
}
if ( (unsigned int)i < m_children.GetCount() )
text += wxT("; ...");
return text;
}
// Convert semicolon delimited tokens into child values.
bool wxPGPropertyWithChildren::SetValueFromString( const wxString& text, int argFlags )
{
if ( !GetCount() )
return false;
unsigned int curChild = 0;
unsigned int iMax = m_children.GetCount();
if ( iMax > PWC_CHILD_SUMMARY_LIMIT &&
!(argFlags & wxPG_FULL_VALUE) )
iMax = PWC_CHILD_SUMMARY_LIMIT;
bool changed = false;
wxString token;
size_t pos = 0;
// Its best only to add non-empty group items
bool addOnlyIfNotEmpty = false;
const wxChar delimeter = wxT(';');
wxChar a;
size_t lastPos = text.length();
size_t tokenStart = 0xFFFFFF;
do
{
a = text[pos];
if ( tokenStart != 0xFFFFFF )
{
// Token is running
if ( a == delimeter || a == 0 )
{
token = text.substr(tokenStart,pos-tokenStart);
token.Trim(true);
size_t len = token.length();
if ( !addOnlyIfNotEmpty || len > 0 )
{
wxPGProperty* child = Item(curChild);
if ( len > 0 )
{
bool wasUnspecified = child->IsValueUnspecified();
if ( child->SetValueFromString( token, wxPG_REPORT_ERROR ) )
{
// If modified, set mod flag and store value back to parent
child->SetFlag( wxPG_PROP_MODIFIED );
// Clear unspecified flag only if SetValueFromString didn't
// affect it.
if ( child->IsValueUnspecified() &&
(wasUnspecified || !UsesAutoUnspecified()) )
child->ClearFlag( wxPG_PROP_UNSPECIFIED );
ChildChanged( child );
changed = true;
}
}
else
{
child->SetFlag( wxPG_PROP_UNSPECIFIED );
changed = true;
}
curChild++;
if ( curChild >= iMax )
break;
}
tokenStart = 0xFFFFFF;
}
}
else
{
// Token is not running
if ( a != wxT(' ') )
{
addOnlyIfNotEmpty = false;
// Is this a group of tokens?
if ( a == wxT('[') )
{
int depth = 1;
pos++;
size_t startPos = pos;
// Group item - find end
do
{
a = text[pos];
pos++;
if ( a == wxT(']') )
depth--;
else if ( a == wxT('[') )
depth++;
} while ( depth > 0 && a );
token = text.substr(startPos,pos-startPos-1);
if ( !token.length() )
break;
wxPGProperty* child = Item(curChild);
//wxLogDebug(wxT("child(1) %i: %s"),curChild,token.c_str());
if ( child->SetValueFromString( token, wxPG_REPORT_ERROR ) )
{
// If modified, set mod flag and store value back to parent
child->SetFlag( wxPG_PROP_MODIFIED );
ChildChanged( child );
changed = true;
}
curChild++;
if ( curChild >= iMax )
break;
addOnlyIfNotEmpty = true;
tokenStart = 0xFFFFFF;
}
else
{
tokenStart = pos;
if ( a == delimeter )
{
pos--;
}
}
}
}
pos++;
}
while ( pos <= lastPos );
// This ensures that the last item is set unspecified even
// if the blank had no terminating delimiter.
if ( curChild < iMax )
{
wxPGProperty* child = Item(curChild);
child->SetFlag( wxPG_PROP_UNSPECIFIED );
changed = true;
}
return changed;
}
void wxPGPropertyWithChildren::RefreshChildren ()
{
}
// -----------------------------------------------------------------------
// wxParentProperty
// -----------------------------------------------------------------------
wxPGProperty* wxParentProperty( const wxString& label, const wxString& name )
{
return new wxParentPropertyClass(label,name);
}
WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxParentProperty,none,TextCtrl)
WX_PG_IMPLEMENT_CLASSINFO(wxParentProperty,wxBaseParentPropertyClass)
wxParentPropertyClass::wxParentPropertyClass( const wxString& label, const wxString& name )
: wxPGPropertyWithChildren(label,name)
{
m_parentingType = PT_CUSTOMPROPERTY;
}
wxParentPropertyClass::~wxParentPropertyClass() { }
void wxParentPropertyClass::DoSetValue( wxPGVariant value )
{
const wxString& str = wxPGVariantToString(value);
m_string = str;
SetValueFromString(str,wxPG_REPORT_ERROR);
}
wxPGVariant wxParentPropertyClass::DoGetValue() const
{
return wxPGVariant();
}
void wxParentPropertyClass::ChildChanged( wxPGProperty* WXUNUSED(p) )
{
}
wxString wxParentPropertyClass::GetValueAsString( int argFlags ) const
{
if ( !GetCount() )
return wxEmptyString;
return wxPGPropertyWithChildren::GetValueAsString(argFlags);
}
// -----------------------------------------------------------------------
// wxPGRootPropertyClass
// -----------------------------------------------------------------------
WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxPGRootProperty,none,TextCtrl)
const wxPGPropertyClassInfo* wxPGRootPropertyClass::GetClassInfo() const
{
return (const wxPGPropertyClassInfo*) NULL;
}
wxPGRootPropertyClass::wxPGRootPropertyClass()
: wxPGPropertyWithChildren()
{
m_parentingType = PT_ROOT; // this was PT_CAPTION in <= 1.1.6, but changed
// so the depth calculations can become
// more consistent.
m_depth = 0;
}
wxPGRootPropertyClass::~wxPGRootPropertyClass()
{
}
// -----------------------------------------------------------------------
// wxPropertyCategoryClass
// -----------------------------------------------------------------------
wxPGProperty* wxPropertyCategory( const wxString& label, const wxString& name )
{
return new wxPropertyCategoryClass(label,name);
}
WX_PG_IMPLEMENT_CLASSINFO(wxPropertyCategory,wxBaseParentPropertyClass)
WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxPropertyCategory,none,TextCtrl)
wxPropertyCategoryClass::wxPropertyCategoryClass()
: wxPGPropertyWithChildren()
{
// don't set colour - prepareadditem method should do this
m_parentingType = 1;
m_capFgColIndex = 1;
}
wxPropertyCategoryClass::wxPropertyCategoryClass( const wxString &label, const wxString& name )
: wxPGPropertyWithChildren(label,name)
{
// don't set colour - prepareadditem method should do this
m_parentingType = 1;
m_capFgColIndex = 1;
}
wxPropertyCategoryClass::~wxPropertyCategoryClass()
{
}
wxString wxPropertyCategoryClass::GetValueAsString( int ) const
{
return wxEmptyString;
}
void wxPropertyCategoryClass::CalculateTextExtent( wxWindow* wnd, wxFont& font )
{
int x = 0, y = 0;
wnd->GetTextExtent( m_label, &x, &y, 0, 0, &font );
m_textExtent = x;
}
// -----------------------------------------------------------------------
// wxPGEditor
// -----------------------------------------------------------------------
wxPGEditor::~wxPGEditor()
{
}
void wxPGEditor::DrawValue( wxDC& dc, wxPGProperty* property, const wxRect& rect ) const
{
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
dc.DrawText( property->GetDisplayedString(), rect.x+wxPG_XBEFORETEXT, rect.y );
}
void wxPGEditor::SetControlStringValue( wxWindow*, const wxString& ) const
{
}
void wxPGEditor::SetControlIntValue( wxWindow*, int ) const
{
}
int wxPGEditor::InsertItem( wxWindow*, const wxString&, int ) const
{
return -1;
}
void wxPGEditor::DeleteItem( wxWindow*, int ) const
{
return;
}
void wxPGEditor::OnFocus( wxPGProperty*, wxWindow* ) const
{
}
bool wxPGEditor::CanContainCustomImage() const
{
return false;
}
// -----------------------------------------------------------------------
// wxPGClipperWindow
// -----------------------------------------------------------------------
#if wxPG_ENABLE_CLIPPER_WINDOW
//
// Clipper window is used to "remove" borders from controls
// which otherwise insist on having them despite of supplied
// wxNO_BORDER window style.
//
class wxPGClipperWindow : public wxWindow
{
DECLARE_CLASS(wxPGClipperWindow)
public:
wxPGClipperWindow()
: wxWindow()
{
wxPGClipperWindow::Init();
}
wxPGClipperWindow(wxWindow* parent,
wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize)
{
Init();
Create(parent,id,pos,size);
}
void Create(wxWindow* parent,
wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize);
virtual ~wxPGClipperWindow();
virtual bool ProcessEvent(wxEvent& event);
inline wxWindow* GetControl() const { return m_ctrl; }
// This is called before wxControl is constructed.
void GetControlRect( int xadj, int yadj, wxPoint& pt, wxSize& sz );
// This is caleed after wxControl has been constructed.
void SetControl( wxWindow* ctrl );
virtual void Refresh( bool eraseBackground = true,
const wxRect *rect = (const wxRect *) NULL );
virtual void SetFocus();
virtual bool SetFont(const wxFont& font);
inline int GetXClip() const { return m_xadj; }
inline int GetYClip() const { return m_yadj; }
protected:
wxWindow* m_ctrl;
int m_xadj; // Horizontal border clip.
int m_yadj; // Vertical border clip.
private:
void Init ()
{
m_ctrl = (wxWindow*) NULL;
}
};
IMPLEMENT_CLASS(wxPGClipperWindow,wxWindow)
// This is called before wxControl is constructed.
void wxPGClipperWindow::GetControlRect( int xadj, int yadj, wxPoint& pt, wxSize& sz )
{
m_xadj = xadj;
m_yadj = yadj;
pt.x = -xadj;
pt.y = -yadj;
wxSize own_size = GetSize();
sz.x = own_size.x+(xadj*2);
sz.y = own_size.y+(yadj*2);
}
// This is caleed after wxControl has been constructed.
void wxPGClipperWindow::SetControl( wxWindow* ctrl )
{
m_ctrl = ctrl;
// GTK requires this.
ctrl->SetSizeHints(3,3);
// Correct size of this window to match the child.
wxSize sz = GetSize();
wxSize chsz = ctrl->GetSize();
int hei_adj = chsz.y - (sz.y+(m_yadj*2));
if ( hei_adj )
SetSize(sz.x,chsz.y-(m_yadj*2));
}
void wxPGClipperWindow::Refresh( bool eraseBackground, const wxRect *rect )
{
wxWindow::Refresh(false,rect);
if ( m_ctrl )
// FIXME: Rect to sub-ctrl refresh too
m_ctrl->Refresh(eraseBackground);
}
// Pass focus to control
void wxPGClipperWindow::SetFocus()
{
if ( m_ctrl )
m_ctrl->SetFocus();
else
wxWindow::SetFocus();
}
bool wxPGClipperWindow::SetFont(const wxFont& font)
{
bool res = wxWindow::SetFont(font);
if ( m_ctrl )
return m_ctrl->SetFont(font);
return res;
}
void wxPGClipperWindow::Create(wxWindow* parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size )
{
wxWindow::Create(parent,id,pos,size);
}
wxPGClipperWindow::~wxPGClipperWindow()
{
}
bool wxPGClipperWindow::ProcessEvent(wxEvent& event)
{
if ( event.GetEventType() == wxEVT_SIZE )
{
if ( m_ctrl )
{
// Maintain correct size relationship.
wxSize sz = GetSize();
m_ctrl->SetSize(sz.x+(m_xadj*2),sz.y+(m_yadj*2));
event.Skip();
return false;
}
}
return wxWindow::ProcessEvent(event);
}
#endif // wxPG_ENABLE_CLIPPER_WINDOW
/*wxWindow* wxPropertyGrid::GetActualEditorControl( wxWindow* ctrl )
{
#if wxPG_ENABLE_CLIPPER_WINDOW
// Pass real control instead of clipper window
if ( ctrl->IsKindOf(CLASSINFO(wxPGClipperWindow)) )
{
return ((wxPGClipperWindow*)ctrl)->GetControl();
}
#else
return ctrl;
#endif
}*/
// -----------------------------------------------------------------------
// wxPGTextCtrlEditor
// -----------------------------------------------------------------------
// Clipper window support macro (depending on whether it is used
// for this editor or not)
#if wxPG_NAT_TEXTCTRL_BORDER_X || wxPG_NAT_TEXTCTRL_BORDER_Y
#define wxPG_NAT_TEXTCTRL_BORDER_ANY 1
#define wxPGDeclareRealTextCtrl(WND) \
wxASSERT( WND ); \
wxTextCtrl* tc = (wxTextCtrl*)((wxPGClipperWindow*)WND)->GetControl()
#else
#define wxPG_NAT_TEXTCTRL_BORDER_ANY 0
#define wxPGDeclareRealTextCtrl(WND) \
wxASSERT( WND ); \
wxTextCtrl* tc = (wxTextCtrl*)WND
#endif
WX_PG_IMPLEMENT_EDITOR_CLASS(TextCtrl,wxPGTextCtrlEditor,wxPGEditor)
#ifndef __WXPYTHON__
wxWindow* wxPGTextCtrlEditor::CreateControls( wxPropertyGrid* propGrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& sz,
wxWindow** ) const
#else
wxPGWindowPair wxPGTextCtrlEditor::CreateControls( wxPropertyGrid* propGrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& sz ) const
#endif
{
wxString text;
// If has children and limited editing, then don't create.
if ((property->GetFlags() & wxPG_PROP_NOEDITOR) &&
property->GetParentingType() < 0 &&
!property->IsKindOf(WX_PG_CLASSINFO(wxCustomProperty)))
return (wxWindow*) NULL;
int flags = 0;
if ( (property->GetFlags() & wxPG_PROP_PASSWORD) &&
property->IsKindOf(WX_PG_CLASSINFO(wxStringProperty)) )
flags |= wxTE_PASSWORD;
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
text = property->GetValueAsString(property->HasFlag(wxPG_PROP_READONLY)?0:wxPG_EDITABLE_VALUE);
wxWindow* wnd = propGrid->GenerateEditorTextCtrl(pos,sz,text,(wxWindow*)NULL,flags,
property->GetMaxLength());
return wnd;
}
void wxPGTextCtrlEditor::DrawValue( wxDC& dc, wxPGProperty* property, const wxRect& rect ) const
{
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
{
wxString drawStr = property->GetDisplayedString();
// Code below should no longer be needed, as the obfuscation
// is now done in GetValueAsString.
/*if ( (property->GetFlags() & wxPG_PROP_PASSWORD) &&
property->IsKindOf(WX_PG_CLASSINFO(wxStringProperty)) )
{
size_t a = drawStr.length();
drawStr.Empty();
drawStr.Append(wxT('*'),a);
}*/
dc.DrawText( drawStr, rect.x+wxPG_XBEFORETEXT, rect.y );
}
}
void wxPGTextCtrlEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
{
wxPGDeclareRealTextCtrl(ctrl);
tc->SetValue(property->GetDisplayedString());
}
// Provided so that, for example, ComboBox editor can use the same code
// (multiple inheritance would get way too messy).
bool wxPGTextCtrlEditor::OnTextCtrlEvent( wxPropertyGrid* propGrid,
wxPGProperty* property,
wxWindow* ctrl,
wxEvent& event )
{
if ( !ctrl )
return false;
if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER )
{
if ( propGrid->IsEditorsValueModified() )
{
return true;
}
}
else if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED )
{
wxPGDeclareRealTextCtrl(ctrl);
// If value is unspecified and character count is zero,
// then do not set as modified.
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) ||
!tc ||
(tc->IsKindOf(CLASSINFO(wxTextCtrl)) &&
(tc->GetLastPosition() > 0)) )
{
// We must check this since an 'empty' text event
// may be triggered when creating the property.
if ( !(propGrid->GetInternalFlags() & wxPG_FL_IN_SELECT_PROPERTY) )
{
//
// Pass this event outside wxPropertyGrid so that,
// if necessary, program can tell when user is editing
// a textctrl.
// FIXME: Is it safe to change event id in the middle of event
// processing (seems to work, but...)?
event.Skip();
event.SetId(propGrid->GetId());
}
propGrid->EditorsValueWasModified();
}
}
return false;
}
bool wxPGTextCtrlEditor::OnEvent( wxPropertyGrid* propGrid,
wxPGProperty* property,
wxWindow* ctrl,
wxEvent& event ) const
{
return wxPGTextCtrlEditor::OnTextCtrlEvent(propGrid,property,ctrl,event);
}
bool wxPGTextCtrlEditor::CopyTextCtrlValueFromControl( wxPGProperty* property, wxWindow* ctrl )
{
#if wxPG_ENABLE_CLIPPER_WINDOW
// Pass real control instead of clipper window
if ( ctrl->IsKindOf(CLASSINFO(wxPGClipperWindow)) )
{
ctrl = ((wxPGClipperWindow*)ctrl)->GetControl();
}
#endif
wxTextCtrl* tc = (wxTextCtrl*)ctrl;
bool res = property->SetValueFromString(tc->GetValue(),0);
// Changing unspecified always causes event (returning
// true here should be enough to trigger it).
if ( !res && property->IsFlagSet(wxPG_PROP_UNSPECIFIED) )
res = true;
return res;
}
bool wxPGTextCtrlEditor::CopyValueFromControl( wxPGProperty* property, wxWindow* ctrl ) const
{
return wxPGTextCtrlEditor::CopyTextCtrlValueFromControl(property,ctrl);
}
void wxPGTextCtrlEditor::SetValueToUnspecified( wxWindow* ctrl ) const
{
wxPGDeclareRealTextCtrl(ctrl);
tc->Remove(0,tc->GetValue().length());
}
void wxPGTextCtrlEditor::SetControlStringValue( wxWindow* ctrl, const wxString& txt ) const
{
wxPGDeclareRealTextCtrl(ctrl);
tc->SetValue(txt);
}
void wxPGTextCtrlEditor::OnFocus( wxPGProperty*, wxWindow* wnd ) const
{
wxPGDeclareRealTextCtrl(wnd);
tc->SetSelection(-1,-1);
}
wxPGTextCtrlEditor::~wxPGTextCtrlEditor() { }
// -----------------------------------------------------------------------
// wxPGChoiceEditor
// -----------------------------------------------------------------------
extern const wxChar* wxPG_ClassName_wxBoolProperty; // in props.cpp
WX_PG_IMPLEMENT_EDITOR_CLASS(Choice,wxPGChoiceEditor,wxPGEditor)
// This is a special enhanced double-click processor class.
// In essence, it allows for double-clicks for which the
// first click "created" the control.
class wxPGDoubleClickProcessor : public wxEvtHandler
{
public:
wxPGDoubleClickProcessor( wxPGOwnerDrawnComboBox* combo )
: wxEvtHandler()
{
m_timeLastMouseUp = 0;
m_combo = combo;
m_downReceived = false;
}
protected:
void OnMouseEvent( wxMouseEvent& event )
{
wxLongLong t = ::wxGetLocalTimeMillis();
int evtType = event.GetEventType();
if ( m_combo->HasFlag(wxPGCC_DCLICK_CYCLES) &&
!m_combo->IsPopupShown() )
{
// Just check that it is in the text area
wxPoint pt = event.GetPosition();
if ( m_combo->GetTextRect().wxPGRectContains(pt) )
{
if ( evtType == wxEVT_LEFT_DOWN )
{
// Set value to avoid up-events without corresponding downs
m_downReceived = true;
}
else if ( evtType == wxEVT_LEFT_DCLICK )
{
// We'll make our own double-clicks
event.SetEventType(0);
return;
}
else if ( evtType == wxEVT_LEFT_UP )
{
if ( m_downReceived || m_timeLastMouseUp == 1 )
{
wxLongLong timeFromLastUp = (t-m_timeLastMouseUp);
if ( timeFromLastUp < DOUBLE_CLICK_CONVERSION_TRESHOLD )
{
event.SetEventType(wxEVT_LEFT_DCLICK);
m_timeLastMouseUp = 1;
}
else
{
m_timeLastMouseUp = t;
}
}
}
}
}
event.Skip();
}
void OnSetFocus( wxFocusEvent& event )
{
m_timeLastMouseUp = ::wxGetLocalTimeMillis();
event.Skip();
}
private:
wxLongLong m_timeLastMouseUp;
wxPGOwnerDrawnComboBox* m_combo;
bool m_downReceived;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(wxPGDoubleClickProcessor, wxEvtHandler)
EVT_MOUSE_EVENTS(wxPGDoubleClickProcessor::OnMouseEvent)
EVT_SET_FOCUS(wxPGDoubleClickProcessor::OnSetFocus)
END_EVENT_TABLE()
class wxPGComboBox : public wxPGOwnerDrawnComboBox
{
public:
wxPGComboBox()
: wxPGOwnerDrawnComboBox()
{
m_dclickProcessor = (wxPGDoubleClickProcessor*) NULL;
}
~wxPGComboBox()
{
if ( m_dclickProcessor )
{
RemoveEventHandler(m_dclickProcessor);
delete m_dclickProcessor;
}
}
bool Create(wxWindow *parent,
wxWindowID id,
const wxString& value = wxEmptyString,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
int n = 0,
const wxString choices[] = (const wxString *) NULL,
long style = 0,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxComboBoxNameStr)
{
if ( !wxPGOwnerDrawnComboBox::Create( parent,
id,
value,
pos,
size,
n,
choices,
style,
validator,
name ) )
return false;
m_dclickProcessor = new wxPGDoubleClickProcessor(this);
PushEventHandler(m_dclickProcessor);
return true;
}
virtual bool OnDrawListItem( wxDC& dc, const wxRect& rect, int item, int flags )
{
wxPropertyGrid* pg = wxDynamicCast(GetParent(),wxPropertyGrid);
wxASSERT(pg);
pg->OnComboItemPaint((wxPGCustomComboControl*)this,item,dc,(wxRect&)rect,flags);
return true;
}
virtual wxCoord OnMeasureListItem( int item )
{
wxPropertyGrid* pg = wxDynamicCast(GetParent(),wxPropertyGrid);
wxASSERT(pg);
wxRect rect;
rect.x = -1;
rect.width = 0;
pg->OnComboItemPaint((wxPGCustomComboControl*)this,item,*((wxDC*)NULL),rect,0);
return rect.height;
}
virtual wxCoord OnMeasureListItemWidth( int item )
{
wxPropertyGrid* pg = wxDynamicCast(GetParent(),wxPropertyGrid);
wxASSERT(pg);
wxRect rect;
rect.x = -1;
rect.width = -1;
pg->OnComboItemPaint((wxPGCustomComboControl*)this,item,*((wxDC*)NULL),rect,0);
return rect.width;
}
private:
wxPGDoubleClickProcessor* m_dclickProcessor;
};
void wxPropertyGrid::OnComboItemPaint( wxPGCustomComboControl* pCc,
int item,
wxDC& dc,
wxRect& rect,
int flags )
{
wxPGOwnerDrawnComboBox* pCb = (wxPGOwnerDrawnComboBox*)pCc;
// Sanity check
wxASSERT( IsKindOf(CLASSINFO(wxPropertyGrid)) );
wxPGProperty* p = m_selected;
//
// Decide what custom image size to use
wxSize cis = GetImageSize(p);
if ( rect.x < 0 &&
!(m_iFlags & wxPG_FL_SELECTED_IS_PAINT_FLEXIBLE) )
{
// Default measure behaviour (no flexible, custom paint image only)
if ( rect.width < 0 )
{
wxCoord x, y;
GetTextExtent(pCb->GetString(item), &x, &y, 0, 0, &m_font);
rect.width = cis.x + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2 + 9 + x;
}
rect.height = cis.y + 2;
return;
}
wxPGPaintData paintdata;
paintdata.m_parent = NULL;
paintdata.m_choiceItem = item;
// This is by the current (1.0.0b) spec - if painting control, item is -1
if ( (flags & wxPGCC_PAINTING_CONTROL) )
paintdata.m_choiceItem = -1;
if ( &dc )
dc.SetBrush(*wxWHITE_BRUSH);
if ( rect.x >= 0 )
{
//
// DrawItem call
wxPoint pt(rect.x + wxPG_CONTROL_MARGIN - wxPG_CHOICEXADJUST - 1,
rect.y + 1);
if ( cis.x > 0 &&
( !p->m_dataExt || !p->m_dataExt->m_valueBitmap || item == pCb->GetSelection() ) &&
( item >= 0 || (flags & wxPGCC_PAINTING_CONTROL) )
)
{
pt.x += wxCC_CUSTOM_IMAGE_MARGIN1;
wxRect r(pt.x,pt.y,cis.x,cis.y);
if ( flags & wxPGCC_PAINTING_CONTROL )
{
//r.width = cis.x;
r.height = wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight);
}
if ( m_iFlags & wxPG_FL_SELECTED_IS_FULL_PAINT )
r.width = rect.width;
paintdata.m_drawnWidth = r.width;
dc.SetPen(m_colPropFore);
if ( item >= 0 )
p->OnCustomPaint( dc, r, paintdata );
else
dc.DrawRectangle( r );
if ( (m_iFlags & wxPG_FL_SELECTED_IS_FULL_PAINT) )
{
if ( paintdata.m_drawnWidth > 0 )
return;
// Revert pt.x
pt.x -= (wxCC_CUSTOM_IMAGE_MARGIN1+1);
}
else
pt.x += paintdata.m_drawnWidth + wxCC_CUSTOM_IMAGE_MARGIN2 - 1;
}
else
// TODO: This aligns text so that it seems to be horizontally
// on the same line as property values. Not really
// sure if its needed, but seems to not cause any harm.
pt.x -= 1;
//
// Draw text
//
pt.y += (rect.height-m_fontHeight)/2 - 1;
wxString text;
if ( !(flags & wxPGCC_PAINTING_CONTROL) )
{
text = pCb->GetString(item);
}
else
{
if ( !p->IsValueUnspecified() )
text = p->GetValueAsString(0);
}
dc.DrawText( text, pt.x + wxPG_XBEFORETEXT, pt.y );
}
else
{
//
// MeasureItem call
p->OnCustomPaint( dc, rect, paintdata );
rect.height = paintdata.m_drawnHeight + 2;
rect.width = cis.x + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2 + 9;
}
}
// CreateControls calls this with CB_READONLY in extraStyle
wxWindow* wxPGChoiceEditor::CreateControlsBase( wxPropertyGrid* propGrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& sz,
long extraStyle ) const
{
wxString defString;
wxPGChoiceInfo choiceInfo;
// Get choices.
choiceInfo.m_arrWxString = (wxString*) NULL;
choiceInfo.m_arrWxChars = (const wxChar**) NULL;
choiceInfo.m_itemCount = 0;
int index = property->GetChoiceInfo( &choiceInfo );
if ( property->GetFlags() & wxPG_PROP_UNSPECIFIED )
{
index = -1;
}
else
{
defString = property->GetDisplayedString();
}
// SLAlloc allows fast conversion using potentially pre-allocated wxStrings
// (and appending is out of question due to performance problems on some platforms).
// If itemcount is < 0, fill wxArrayString using GetEntry
if ( choiceInfo.m_itemCount < 0 )
{
wxBaseEnumPropertyClass* ep = (wxBaseEnumPropertyClass*) property;
size_t i = 0;
const wxString* entryLabel;
int entryValue;
wxArrayString& sl = propGrid->SLGet();
entryLabel = ep->GetEntry(i,&entryValue);
while ( entryLabel )
{
if ( sl.GetCount() > i )
sl[i] = *entryLabel;
else
sl.Add(*entryLabel);
i++;
entryLabel = ep->GetEntry(i,&entryValue);
}
choiceInfo.m_itemCount = ((int)i) - 1;
}
else if ( !choiceInfo.m_arrWxString )
{
wxASSERT( choiceInfo.m_arrWxChars || !choiceInfo.m_itemCount );
propGrid->SLAlloc( choiceInfo.m_itemCount, choiceInfo.m_arrWxChars );
if ( choiceInfo.m_itemCount )
choiceInfo.m_arrWxString = &propGrid->SLGet().Item(0);
}
//wxPGOwnerDrawnComboBox* cb;
wxPGComboBox* cb;
wxPoint po(pos);
wxSize si(sz);
po.y += wxPG_CHOICEYADJUST;
si.y -= (wxPG_CHOICEYADJUST*2);
/*#if wxPG_NAT_CHOICE_BORDER_ANY
po.x += (wxPG_CHOICEXADJUST+wxPG_NAT_CHOICE_BORDER_X);
si.x -= (wxPG_CHOICEXADJUST+wxPG_NAT_CHOICE_BORDER_X);
wxPGClipperWindow* wnd = new wxPGClipperWindow(propGrid,wxPG_SUBID1,po,si);
wxWindow* ctrlParent = wnd;
wnd->GetControlRect(wxPG_NAT_CHOICE_BORDER_X,wxPG_NAT_CHOICE_BORDER_Y,po,si);
#else*/
po.x += wxPG_CHOICEXADJUST;
si.x -= wxPG_CHOICEXADJUST;
wxWindow* ctrlParent = propGrid;
//#endif
// NB: Using wxWidgets wxOwnerDrawnComboBox needs adding wxTE_PROCESS_ENTER
// into the flags.
int odcbFlags = extraStyle | wxNO_BORDER | wxPGCC_PROCESS_ENTER | wxPGCC_ALT_KEYS;
if ( !(property->GetFlags() & wxPG_PROP_CUSTOMIMAGE) )
odcbFlags |= wxODCB_STD_CONTROL_PAINT;
if ( (property->GetFlags() & wxPG_PROP_USE_DCC) &&
(property->GetClassName()==wxPG_ClassName_wxBoolProperty) )
odcbFlags |= wxPGCC_DCLICK_CYCLES;
cb = new wxPGComboBox();
#ifdef __WXMSW__
cb->Hide();
#endif
cb->Create(ctrlParent,
wxPG_SUBID1,
wxString(),
po,
si,
choiceInfo.m_itemCount,choiceInfo.m_arrWxString,
//(wxComboPaintCallback) &wxPropertyGrid::OnComboItemPaint,
odcbFlags);
int extRight = propGrid->GetClientSize().x - (po.x+si.x);
cb->SetButtonPosition(si.y,0,wxRIGHT);
cb->SetPopupExtents( 1, extRight );
cb->SetTextIndent(wxPG_XBEFORETEXT-2);
if ( (property->GetFlags() & wxPG_PROP_CUSTOMIMAGE) &&
!(propGrid->GetInternalFlags() & wxPG_FL_SELECTED_IS_FULL_PAINT) )
{
wxSize imageSize = propGrid->GetImageSize(property);
cb->SetCustomPaintWidth( imageSize.x+6 );
}
if ( index >= 0 && index < (int)cb->GetCount() )
{
cb->SetSelection( index );
if ( defString.length() )
cb->SetValue( defString );
}
else if ( !(extraStyle & wxCB_READONLY) && defString.length() )
cb->SetValue( defString );
else
cb->SetSelection( -1 );
if ( property->HasFlag(wxPG_PROP_READONLY) )
cb->Disable();
#ifdef __WXMSW__
cb->Show();
#endif
return (wxWindow*) cb;
}
void wxPGChoiceEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
{
wxASSERT( ctrl );
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
wxASSERT( cb->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)));
int ind = property->GetChoiceInfo( (wxPGChoiceInfo*)NULL );
cb->SetSelection(ind);
}
#ifndef __WXPYTHON__
wxWindow* wxPGChoiceEditor::CreateControls( wxPropertyGrid* propGrid, wxPGProperty* property,
const wxPoint& pos, const wxSize& sz, wxWindow** ) const
#else
wxPGWindowPair wxPGChoiceEditor::CreateControls( wxPropertyGrid* propGrid, wxPGProperty* property,
const wxPoint& pos, const wxSize& sz ) const
#endif
{
return CreateControlsBase(propGrid,property,pos,sz,wxCB_READONLY);
}
int wxPGChoiceEditor::InsertItem( wxWindow* ctrl, const wxString& label, int index ) const
{
wxASSERT( ctrl );
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
wxASSERT( cb->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)));
if (index < 0)
index = cb->GetCount();
return cb->Insert(label,index);
}
void wxPGChoiceEditor::DeleteItem( wxWindow* ctrl, int index ) const
{
wxASSERT( ctrl );
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
wxASSERT( cb->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)));
cb->Delete(index);
}
bool wxPGChoiceEditor::OnEvent( wxPropertyGrid* WXUNUSED(propGrid), wxPGProperty* WXUNUSED(property),
wxWindow* WXUNUSED(ctrl), wxEvent& event ) const
{
if ( event.GetEventType() == wxEVT_COMMAND_COMBOBOX_SELECTED )
{
/*if ( CopyValueFromControl( property, ctrl ) )
{
return true;
}
propGrid->EditorsValueWasNotModified();
//wxPropertyGridState::ClearPropertyAndChildrenFlags(property,wxPG_PROP_UNSPECIFIED);
CLEAR_PROPERTY_UNSPECIFIED_FLAG(property);*/
return true;
}
return false;
}
bool wxPGChoiceEditor::CopyValueFromControl( wxPGProperty* property, wxWindow* ctrl ) const
{
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
int index = cb->GetSelection();
if ( index != property->GetChoiceInfo( (wxPGChoiceInfo*) NULL ) ||
// Changing unspecified always causes event (returning
// true here should be enough to trigger it).
property->IsFlagSet(wxPG_PROP_UNSPECIFIED)
)
{
property->SetValueFromInt(index,0);
return true;
}
return false;
}
void wxPGChoiceEditor::SetControlStringValue( wxWindow* ctrl, const wxString& txt ) const
{
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
wxASSERT( cb );
cb->SetValue(txt);
}
void wxPGChoiceEditor::SetControlIntValue( wxWindow* ctrl, int value ) const
{
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
wxASSERT( cb );
cb->SetSelection(value);
}
void wxPGChoiceEditor::SetValueToUnspecified( wxWindow* ctrl ) const
{
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
cb->SetSelection(-1);
}
bool wxPGChoiceEditor::CanContainCustomImage() const
{
return true;
}
wxPGChoiceEditor::~wxPGChoiceEditor() { }
// -----------------------------------------------------------------------
// wxPGComboBoxEditor
// -----------------------------------------------------------------------
WX_PG_IMPLEMENT_EDITOR_CLASS(ComboBox,wxPGComboBoxEditor,wxPGChoiceEditor)
void wxPGComboBoxEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
{
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
cb->SetValue(property->GetDisplayedString());
// TODO: If string matches any selection, then select that.
}
#ifndef __WXPYTHON__
wxWindow* wxPGComboBoxEditor::CreateControls( wxPropertyGrid* propGrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& sz,
wxWindow** ) const
#else
wxPGWindowPair wxPGComboBoxEditor::CreateControls( wxPropertyGrid* propGrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& sz ) const
#endif
{
return CreateControlsBase(propGrid,property,pos,sz,0);
}
bool wxPGComboBoxEditor::OnEvent( wxPropertyGrid* propGrid,
wxPGProperty* property,
wxWindow* ctrl,
wxEvent& event ) const
{
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*) NULL;
wxWindow* textCtrl = (wxWindow*) NULL;
if ( ctrl )
{
cb = (wxPGOwnerDrawnComboBox*)ctrl;
textCtrl = cb->GetTextCtrl();
}
if ( wxPGTextCtrlEditor::OnTextCtrlEvent(propGrid,property,textCtrl,event) )
return true;
return wxPGChoiceEditor::OnEvent(propGrid,property,ctrl,event);
}
bool wxPGComboBoxEditor::CopyValueFromControl( wxPGProperty* property, wxWindow* ctrl ) const
{
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
bool res = property->SetValueFromString(cb->GetValue(),0);
// Changing unspecified always causes event (returning
// true here should be enough to trigger it).
if ( !res && property->IsFlagSet(wxPG_PROP_UNSPECIFIED) )
res = true;
return res;
}
void wxPGComboBoxEditor::OnFocus( wxPGProperty*, wxWindow* ctrl ) const
{
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
cb->GetTextCtrl()->SetSelection(-1,-1);
}
wxPGComboBoxEditor::~wxPGComboBoxEditor() { }
// -----------------------------------------------------------------------
// wxPGChoiceAndButtonEditor
// -----------------------------------------------------------------------
// This simpler implement_editor macro doesn't define class body.
WX_PG_IMPLEMENT_EDITOR_CLASS(ChoiceAndButton,wxPGChoiceAndButtonEditor,wxPGChoiceEditor)
#ifndef __WXPYTHON__
wxWindow* wxPGChoiceAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& sz,
wxWindow** psecondary ) const
#else
wxPGWindowPair wxPGChoiceAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& sz ) const
#endif
{
// Use one two units smaller to match size of the combo's dropbutton.
// (normally a bigger button is used because it looks better)
int bt_wid = sz.y;
bt_wid -= 2;
wxSize bt_sz(bt_wid,bt_wid);
// Position of button.
wxPoint bt_pos(pos.x+sz.x-bt_sz.x,pos.y);
#ifdef __WXMAC__
bt_pos.y -= 1;
#else
bt_pos.y += 1;
#endif
wxWindow* bt = propGrid->GenerateEditorButton( bt_pos, bt_sz );
// Size of choice.
wxSize ch_sz(sz.x-bt->GetSize().x,sz.y);
#ifdef __WXMAC__
ch_sz.x -= wxPG_TEXTCTRL_AND_BUTTON_SPACING;
#endif
wxWindow* ch = wxPG_EDITOR(Choice)->CreateControls(propGrid,property,
pos,ch_sz
#ifndef __WXPYTHON__
, (wxWindow**)NULL);
#else
).m_primary;
#endif
#ifdef __WXMSW__
bt->Show();
#endif
#ifndef __WXPYTHON__
*psecondary = bt;
return ch;
#else
return wxPGWindowPair(ch, bt);
#endif
}
wxPGChoiceAndButtonEditor::~wxPGChoiceAndButtonEditor() { }
// -----------------------------------------------------------------------
// wxPGTextCtrlAndButtonEditor
// -----------------------------------------------------------------------
// This simpler implement_editor macro doesn't define class body.
WX_PG_IMPLEMENT_EDITOR_CLASS(TextCtrlAndButton,wxPGTextCtrlAndButtonEditor,wxPGTextCtrlEditor)
#ifndef __WXPYTHON__
wxWindow* wxPGTextCtrlAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& sz,
wxWindow** psecondary ) const
{
wxWindow* wnd = propGrid->GenerateEditorTextCtrlAndButton( pos, sz, psecondary,
property->GetFlags() & wxPG_PROP_NOEDITOR, property);
return wnd;
}
#else
wxPGWindowPair wxPGTextCtrlAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& sz ) const
{
wxWindow* wnd2;
wxWindow* wnd = propGrid->GenerateEditorTextCtrlAndButton( pos, sz, &wnd2,
property->GetFlags() & wxPG_PROP_NOEDITOR, property);
return wxPGWindowPair(wnd, wnd2);
}
#endif
wxPGTextCtrlAndButtonEditor::~wxPGTextCtrlAndButtonEditor() { }
// -----------------------------------------------------------------------
// wxPGCheckBoxEditor
// -----------------------------------------------------------------------
#if wxPG_INCLUDE_CHECKBOX
WX_PG_IMPLEMENT_EDITOR_CLASS(CheckBox,wxPGCheckBoxEditor,wxPGEditor)
// state argument: 0x01 = set if checked
// 0x02 = set if rectangle should be bold
static void DrawSimpleCheckBox( wxDC& dc, const wxRect& rect, int box_hei, int state, const wxColour& linecol )
{
// Box rectangle.
wxRect r(rect.x+wxPG_XBEFORETEXT,rect.y+((rect.height-box_hei)/2),box_hei,box_hei);
// Draw check mark first because it is likely to overdraw the
// surrounding rectangle.
if ( state & 1 )
{
wxRect r2(r.x+wxPG_CHECKMARK_XADJ,
r.y+wxPG_CHECKMARK_YADJ,
r.width+wxPG_CHECKMARK_WADJ,
r.height+wxPG_CHECKMARK_HADJ);
#if wxPG_CHECKMARK_DEFLATE
r2.Deflate(wxPG_CHECKMARK_DEFLATE);
#endif
dc.DrawCheckMark(r2);
// This would draw a simple cross check mark.
// dc.DrawLine(r.x,r.y,r.x+r.width-1,r.y+r.height-1);
// dc.DrawLine(r.x,r.y+r.height-1,r.x+r.width-1,r.y);
}
if ( !(state & 2) )
{
// Pen for thin rectangle.
dc.SetPen(linecol);
}
else
{
// Pen for bold rectangle.
wxPen linepen(linecol,2,wxSOLID);
linepen.SetJoin(wxJOIN_MITER); // This prevents round edges.
dc.SetPen(linepen);
r.x++;
r.y++;
r.width--;
r.height--;
}
dc.SetBrush(*wxTRANSPARENT_BRUSH);
dc.DrawRectangle(r);
dc.SetPen(*wxTRANSPARENT_PEN);
}
//
// Real simple custom-drawn checkbox-without-label class.
//
class wxSimpleCheckBox : public wxControl
{
public:
void SetValue( int value );
wxSimpleCheckBox( wxWindow* parent,
wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize )
: wxControl(parent,id,pos,size,wxNO_BORDER|wxWANTS_CHARS)
{
// Due to SetOwnFont stuff necessary for GTK+ 1.2, we need to have this
SetFont( parent->GetFont() );
m_state = 0;
m_boxHeight = ((wxPropertyGrid*)parent)->GetFontHeight();
SetBackgroundStyle( wxBG_STYLE_COLOUR );
}
virtual ~wxSimpleCheckBox();
virtual bool ProcessEvent(wxEvent& event);
int m_state;
int m_boxHeight;
static wxBitmap* ms_doubleBuffer;
};
wxSimpleCheckBox::~wxSimpleCheckBox()
{
delete ms_doubleBuffer;
ms_doubleBuffer = NULL;
}
wxBitmap* wxSimpleCheckBox::ms_doubleBuffer = (wxBitmap*) NULL;
// value = 2 means toggle (sorry, too lazy to do constants)
void wxSimpleCheckBox::SetValue( int value )
{
if ( value > 1 )
{
m_state++;
if ( m_state > 1 ) m_state = 0;
}
else
{
m_state = value;
}
Refresh();
wxCommandEvent evt(wxEVT_COMMAND_CHECKBOX_CLICKED,GetParent()->GetId());
((wxPropertyGrid*)GetParent())->OnCustomEditorEvent(evt);
}
bool wxSimpleCheckBox::ProcessEvent(wxEvent& event)
{
wxPropertyGrid* propGrid = (wxPropertyGrid*) GetParent();
if ( event.GetEventType() == wxEVT_NAVIGATION_KEY )
{
//wxLogDebug(wxT("wxEVT_NAVIGATION_KEY"));
//SetFocusFromKbd();
//event.Skip();
//return wxControl::ProcessEvent(event);
}
else
if ( ( (event.GetEventType() == wxEVT_LEFT_DOWN || event.GetEventType() == wxEVT_LEFT_DCLICK)
&& ((wxMouseEvent&)event).m_x > (wxPG_XBEFORETEXT-2)
&& ((wxMouseEvent&)event).m_x <= (wxPG_XBEFORETEXT-2+m_boxHeight) )
)
{
SetValue(2);
return true;
}
else if ( event.GetEventType() == wxEVT_PAINT )
{
wxSize clientSize = GetClientSize();
wxPaintDC dc(this);
/*
// Buffered paint DC doesn't seem to do much good
if ( !ms_doubleBuffer ||
clientSize.x > ms_doubleBuffer->GetWidth() ||
clientSize.y > ms_doubleBuffer->GetHeight() )
{
delete ms_doubleBuffer;
ms_doubleBuffer = new wxBitmap(clientSize.x+25,clientSize.y+25);
}
wxBufferedPaintDC dc(this,*ms_doubleBuffer);
*/
wxRect rect(0,0,clientSize.x,clientSize.y);
rect.x -= 1;
rect.width += 1;
m_boxHeight = propGrid->GetFontHeight();
wxColour bgcol = GetBackgroundColour();
dc.SetBrush( bgcol );
dc.SetPen( bgcol );
dc.DrawRectangle( rect );
wxColour txcol = GetForegroundColour();
int state = m_state;
if ( m_font.GetWeight() == wxBOLD )
state |= 2;
DrawSimpleCheckBox(dc,rect,m_boxHeight,state,txcol);
// If focused, indicate it somehow.
/*
if ( wxWindow::FindFocus() == this )
{
rect.x += 1;
rect.width -= 1;
wxPGDrawFocusRect(dc,rect);
}
*/
return true;
}
else if ( event.GetEventType() == wxEVT_SIZE ||
event.GetEventType() == wxEVT_SET_FOCUS ||
event.GetEventType() == wxEVT_KILL_FOCUS
)
{
Refresh();
}
else if ( event.GetEventType() == wxEVT_KEY_DOWN )
{
wxKeyEvent& keyEv = (wxKeyEvent&) event;
if ( keyEv.GetKeyCode() == WXK_TAB )
{
propGrid->SendNavigationKeyEvent( keyEv.ShiftDown()?0:1 );
return true;
}
else
if ( keyEv.GetKeyCode() == WXK_SPACE )
{
SetValue(2);
return true;
}
}
return wxControl::ProcessEvent(event);
}
#ifndef __WXPYTHON__
wxWindow* wxPGCheckBoxEditor::CreateControls( wxPropertyGrid* propGrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& size,
wxWindow** ) const
#else
wxPGWindowPair wxPGCheckBoxEditor::CreateControls( wxPropertyGrid* propGrid,
wxPGProperty* property,
const wxPoint& pos,
const wxSize& size ) const
#endif
{
wxPoint pt = pos;
pt.x -= wxPG_XBEFOREWIDGET;
wxSize sz = size;
sz.x += wxPG_XBEFOREWIDGET;
wxSimpleCheckBox* cb = new wxSimpleCheckBox(propGrid,wxPG_SUBID1,pt,sz);
cb->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
cb->Connect( wxPG_SUBID1, wxEVT_LEFT_DOWN,
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
&wxPropertyGrid::OnCustomEditorEvent, NULL, propGrid );
cb->Connect( wxPG_SUBID1, wxEVT_LEFT_DCLICK,
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
&wxPropertyGrid::OnCustomEditorEvent, NULL, propGrid );
if ( property->GetChoiceInfo((wxPGChoiceInfo*)NULL) &&
!(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
cb->m_state = 1;
// If mouse cursor was on the item, toggle the value now.
if ( propGrid->GetInternalFlags() & wxPG_FL_ACTIVATION_BY_CLICK )
{
wxPoint pt = propGrid->ScreenToClient(::wxGetMousePosition());
if ( pt.x <= (cb->GetPosition().x+wxPG_XBEFORETEXT-2+cb->m_boxHeight) )
{
cb->m_state++;
if ( cb->m_state > 1 )
cb->m_state = 0;
property->ClearFlag(wxPG_PROP_UNSPECIFIED);
property->SetValueFromInt(cb->m_state,0);
propGrid->PropertyWasModified(property);
}
}
return cb;
}
void wxPGCheckBoxEditor::DrawValue( wxDC& dc, wxPGProperty* property, const wxRect& rect ) const
{
int state = 0;
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
{
state = property->GetChoiceInfo((wxPGChoiceInfo*)NULL);
if ( dc.GetFont().GetWeight() == wxBOLD ) state |= 2;
}
DrawSimpleCheckBox(dc,rect,dc.GetCharHeight(),state,dc.GetTextForeground());
}
void wxPGCheckBoxEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
{
wxASSERT( ctrl );
((wxSimpleCheckBox*)ctrl)->m_state = property->GetChoiceInfo((wxPGChoiceInfo*)NULL);
ctrl->Refresh();
}
bool wxPGCheckBoxEditor::OnEvent( wxPropertyGrid* WXUNUSED(propGrid), wxPGProperty* WXUNUSED(property),
wxWindow* WXUNUSED(ctrl), wxEvent& event ) const
{
if ( event.GetEventType() == wxEVT_COMMAND_CHECKBOX_CLICKED )
{
/*if ( CopyValueFromControl( property, ctrl ) )
return true;
propGrid->EditorsValueWasNotModified();
CLEAR_PROPERTY_UNSPECIFIED_FLAG(property);*/
return true;
}
return false;
}
bool wxPGCheckBoxEditor::CopyValueFromControl( wxPGProperty* property, wxWindow* ctrl ) const
{
wxSimpleCheckBox* cb = (wxSimpleCheckBox*)ctrl;
int index = cb->m_state;
if ( index != property->GetChoiceInfo( (wxPGChoiceInfo*) NULL ) ||
// Changing unspecified always causes event (returning
// true here should be enough to trigger it).
property->IsFlagSet(wxPG_PROP_UNSPECIFIED)
)
{
property->SetValueFromInt(index,0);
return true;
}
return false;
}
void wxPGCheckBoxEditor::SetControlIntValue( wxWindow* ctrl, int value ) const
{
if ( value != 0 ) value = 1;
((wxSimpleCheckBox*)ctrl)->m_state = value;
ctrl->Refresh();
}
void wxPGCheckBoxEditor::SetValueToUnspecified( wxWindow* ctrl ) const
{
((wxSimpleCheckBox*)ctrl)->m_state = 0;
ctrl->Refresh();
}
wxPGCheckBoxEditor::~wxPGCheckBoxEditor() { }
#endif // wxPG_INCLUDE_CHECKBOX
// -----------------------------------------------------------------------
// wxPGBrush
// -----------------------------------------------------------------------
//
// This class is a wxBrush derivative used in the background colour
// brush cache. It adds wxPG-type colour-in-long to the class.
// JMS: Yes I know wxBrush doesn't actually hold the value (refcounted
// object does), but this is simpler implementation and equally
// effective.
//
class wxPGBrush : public wxBrush
{
public:
wxPGBrush( const wxColour& colour );
wxPGBrush();
virtual ~wxPGBrush() { }
void SetColour2( const wxColour& colour );
inline long GetColourAsLong() const { return m_colAsLong; }
private:
long m_colAsLong;
};
void wxPGBrush::SetColour2( const wxColour& colour )
{
wxBrush::SetColour(colour);
m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
}
wxPGBrush::wxPGBrush() : wxBrush()
{
m_colAsLong = 0;
}
wxPGBrush::wxPGBrush( const wxColour& colour ) : wxBrush(colour)
{
m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
}
// -----------------------------------------------------------------------
// wxPGColour
// -----------------------------------------------------------------------
//
// Same as wxPGBrush, but for wxColour instead.
//
class wxPGColour : public wxColour
{
public:
wxPGColour( const wxColour& colour );
wxPGColour();
virtual ~wxPGColour() { }
void SetColour2( const wxColour& colour );
inline long GetColourAsLong() const { return m_colAsLong; }
private:
long m_colAsLong;
};
void wxPGColour::SetColour2( const wxColour& colour )
{
*this = colour;
m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
}
wxPGColour::wxPGColour() : wxColour()
{
m_colAsLong = 0;
}
wxPGColour::wxPGColour( const wxColour& colour ) : wxColour(colour)
{
m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
}
// -----------------------------------------------------------------------
// wxPGTLWHandler
// Intercepts Close-events sent to wxPropertyGrid's top-level parent,
// and tries to commit property value.
// -----------------------------------------------------------------------
class wxPGTLWHandler : public wxEvtHandler
{
public:
wxPGTLWHandler( wxPropertyGrid* pg )
: wxEvtHandler()
{
m_pg = pg;
}
protected:
void OnClose( wxCloseEvent& event )
{
// ClearSelection forces value validation/commit.
if ( event.CanVeto() && !m_pg->ClearSelection() )
{
event.Veto();
return;
}
event.Skip();
}
private:
wxPropertyGrid* m_pg;
DECLARE_EVENT_TABLE()
};
BEGIN_EVENT_TABLE(wxPGTLWHandler, wxEvtHandler)
EVT_CLOSE(wxPGTLWHandler::OnClose)
END_EVENT_TABLE()
// -----------------------------------------------------------------------
// wxPropertyGrid
// -----------------------------------------------------------------------
IMPLEMENT_CLASS(wxPropertyGrid, wxScrolledWindow)
BEGIN_EVENT_TABLE(wxPropertyGrid, wxScrolledWindow)
EVT_MOTION(wxPropertyGrid::OnMouseMove)
EVT_IDLE(wxPropertyGrid::OnIdle)
EVT_LEFT_DOWN(wxPropertyGrid::OnMouseClick)
EVT_LEFT_UP(wxPropertyGrid::OnMouseUp)
EVT_RIGHT_UP(wxPropertyGrid::OnMouseRightClick)
EVT_LEFT_DCLICK(wxPropertyGrid::OnMouseDoubleClick)
EVT_PAINT(wxPropertyGrid::OnPaint)
EVT_SIZE(wxPropertyGrid::OnResize)
EVT_KEY_DOWN(wxPropertyGrid::OnKey)
EVT_KEY_UP(wxPropertyGrid::OnKeyUp)
EVT_CHAR(wxPropertyGrid::OnKey)
EVT_ENTER_WINDOW(wxPropertyGrid::OnMouseEntry)
EVT_LEAVE_WINDOW(wxPropertyGrid::OnMouseEntry)
EVT_MOUSE_CAPTURE_CHANGED(wxPropertyGrid::OnCaptureChange)
EVT_SCROLLWIN(wxPropertyGrid::OnScrollEvent)
EVT_NAVIGATION_KEY(wxPropertyGrid::OnNavigationKey)
EVT_TEXT(wxPG_SUBID1,wxPropertyGrid::OnCustomEditorEvent)
EVT_COMBOBOX(wxPG_SUBID1,wxPropertyGrid::OnCustomEditorEvent)
EVT_BUTTON(wxPG_SUBID2,wxPropertyGrid::OnCustomEditorEvent)
EVT_CHILD_FOCUS(wxPropertyGrid::OnChildFocusEvent)
EVT_SET_FOCUS(wxPropertyGrid::OnFocusEvent)
EVT_KILL_FOCUS(wxPropertyGrid::OnFocusEvent)
EVT_TEXT_ENTER(wxPG_SUBID1,wxPropertyGrid::OnCustomEditorEvent)
EVT_SYS_COLOUR_CHANGED(wxPropertyGrid::OnSysColourChanged)
END_EVENT_TABLE()
// -----------------------------------------------------------------------
wxPropertyGrid::wxPropertyGrid()
: wxScrolledWindow()
{
Init1();
}
// -----------------------------------------------------------------------
wxPropertyGrid::wxPropertyGrid( wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxChar* name )
: wxScrolledWindow()
{
Init1();
Create(parent,id,pos,size,style,name);
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::Create( wxWindow *parent,
wxWindowID id,
const wxPoint& pos,
const wxSize& size,
long style,
const wxChar* name )
{
if ( !(style&wxBORDER_MASK) )
style |= wxSIMPLE_BORDER;
style |= wxVSCROLL;
#ifdef __WXMSW__
// This prevents crash under Win2K, but still
// enables keyboard navigation
if ( style & wxTAB_TRAVERSAL )
{
style &= ~(wxTAB_TRAVERSAL);
style |= wxWANTS_CHARS;
}
#else
if ( style & wxTAB_TRAVERSAL )
style |= wxWANTS_CHARS;
#endif
wxScrolledWindow::Create(parent,id,pos,size,style,name);
Init2();
return true;
}
// -----------------------------------------------------------------------
static void wxPGRegisterStandardPropertyClasses();
//
// Initialize values to defaults
//
void wxPropertyGrid::Init1()
{
WX_PG_GLOBALS_LOCKER()
#if !wxPG_USE_WXMODULE
if ( !wxPGGlobalVars )
wxPGGlobalVars = new wxPGGlobalVarsClass();
#endif
// Register type classes, if necessary.
if ( wxPGGlobalVars->m_dictValueType.empty() )
RegisterDefaultValues();
// Register editor classes, if necessary.
if ( wxPGGlobalVars->m_mapEditorClasses.empty() )
RegisterDefaultEditors();
// Register property classes, if necessary
if ( wxPGGlobalVars->m_dictPropertyClassInfo.empty() )
wxPGRegisterStandardPropertyClasses();
m_iFlags = 0;
m_pState = (wxPropertyGridState*) NULL;
m_wndPrimary = m_wndSecondary = (wxWindow*) NULL;
m_selected = (wxPGProperty*) NULL;
m_propHover = (wxPGProperty*) NULL;
m_eventObject = this;
m_curFocused = (wxWindow*) NULL;
m_tlwHandler = NULL;
m_processingEvent = 0;
m_dragStatus = 0;
m_mouseSide = 16;
m_editorFocused = 0;
m_coloursCustomized = 0;
m_frozen = 0;
#if wxPG_DOUBLE_BUFFER
m_doubleBuffer = (wxBitmap*) NULL;
#endif
m_windowsToDelete = NULL;
#ifndef wxPG_ICON_WIDTH
m_expandbmp = NULL;
m_collbmp = NULL;
m_iconWidth = 11;
m_iconHeight = 11;
#else
m_iconWidth = wxPG_ICON_WIDTH;
#endif
m_prevVY = -1;
m_calcVisHeight = 0;
m_gutterWidth = wxPG_GUTTER_MIN;
m_subgroup_extramargin = 10;
m_lineHeight = 0;
m_width = m_height = m_fWidth = 0;
m_bottomy = 0;
m_splitterx = wxPG_DEFAULT_SPLITTERX;
m_fSplitterX = (float) wxPG_DEFAULT_SPLITTERX;
#if !wxPG_HEAVY_GFX
m_splitterpen.SetColour( *wxBLACK );
m_splitterpen.SetWidth( 4 );
m_splitterprevdrawnx = -1;
#endif
SetButtonShortcut(0);
m_keyComboConsumed = 0;
m_ignoredEvents = 0;
}
// -----------------------------------------------------------------------
//
// Initialize after parent etc. set
//
void wxPropertyGrid::Init2()
{
wxASSERT( !(m_iFlags & wxPG_FL_INITIALIZED ) );
#ifdef __WXMAC__
// Smaller controls on Mac
SetWindowVariant(wxWINDOW_VARIANT_SMALL);
#endif
// Now create state, if one didn't exist already
// (wxPropertyGridManager might have created it for us).
if ( !m_pState )
{
m_pState = CreateState();
m_pState->m_pPropGrid = this;
m_iFlags |= wxPG_FL_CREATEDSTATE;
}
if ( !(m_windowStyle & wxPG_SPLITTER_AUTO_CENTER) )
m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
if ( m_windowStyle & wxPG_HIDE_CATEGORIES )
{
m_pState->InitNonCatMode();
FROM_STATE(m_properties) = FROM_STATE(m_abcArray);
}
GetClientSize(&m_width,&m_height);
#if !wxPG_HEAVY_GFX
m_splitterpen.SetColour( *wxBLACK );
m_splitterpen.SetWidth( 4 );
#endif
#ifndef wxPG_ICON_WIDTH
// create two bitmap nodes for drawing
m_expandbmp = new wxBitmap(expand_xpm);
m_collbmp = new wxBitmap(collapse_xpm);
// calculate average font height for bitmap centering
m_iconWidth = m_expandbmp->GetWidth();
m_iconHeight = m_expandbmp->GetHeight();
#endif
m_curcursor = wxCURSOR_ARROW;
m_cursorSizeWE = new wxCursor( wxCURSOR_SIZEWE );
// adjust bitmap icon y position so they are centered
m_vspacing = wxPG_DEFAULT_VSPACING;
if ( !m_font.Ok() )
{
wxFont useFont = wxScrolledWindow::GetFont();
wxScrolledWindow::SetOwnFont( useFont );
}
else
// This should be otherwise called by SetOwnFont
CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING );
// Add base brush item
m_arrBgBrushes.Add((void*)new wxPGBrush());
// Add base colour items
m_arrFgCols.Add((void*)new wxPGColour());
m_arrFgCols.Add((void*)new wxPGColour());
RegainColours();
// This helps with flicker
SetBackgroundStyle( wxBG_STYLE_CUSTOM );
// Hook the TLW
wxPGTLWHandler* handler = new wxPGTLWHandler(this);
m_tlp = ::wxGetTopLevelParent(this);
m_tlwHandler = handler;
m_tlp->PushEventHandler(handler);
// set virtual size to this window size
wxSize wndsize = GetSize();
SetVirtualSize(wndsize.GetWidth(), wndsize.GetWidth());
m_timeCreated = ::wxGetLocalTimeMillis();
m_iFlags |= wxPG_FL_INITIALIZED;
// Need to call OnResize handler or size given in constructor/Create
// will never work.
wxSizeEvent sizeEvent(wndsize,0);
OnResize(sizeEvent);
}
// -----------------------------------------------------------------------
wxPropertyGrid::~wxPropertyGrid()
{
size_t i;
DoSelectProperty(NULL);
// This should do prevent things from going too badly wrong
m_iFlags &= ~(wxPG_FL_INITIALIZED);
END_MOUSE_CAPTURE
wxPGTLWHandler* handler = (wxPGTLWHandler*) m_tlwHandler;
m_tlp->RemoveEventHandler(handler);
delete handler;
#ifdef __WXDEBUG__
if ( IsEditorsValueModified() )
::wxMessageBox(wxT("Most recent change in property editor was lost!!!\n\n(if you don't want this to happen, close your frames and dialogs using Close(false).)"),
wxT("wxPropertyGrid Debug Warning") );
#endif
#if wxPG_DOUBLE_BUFFER
if ( m_doubleBuffer )
delete m_doubleBuffer;
#endif
delete m_windowsToDelete;
m_selected = (wxPGProperty*) NULL;
if ( m_iFlags & wxPG_FL_CREATEDSTATE )
delete m_pState;
delete m_cursorSizeWE;
#ifndef wxPG_ICON_WIDTH
delete m_expandbmp;
delete m_collbmp;
#endif
// Delete cached text colours.
for ( i=0; i<m_arrFgCols.GetCount(); i++ )
{
delete (wxPGColour*)m_arrFgCols.Item(i);
}
// Delete cached brushes.
for ( i=0; i<m_arrBgBrushes.GetCount(); i++ )
{
delete (wxPGBrush*)m_arrBgBrushes.Item(i);
}
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::Destroy()
{
END_MOUSE_CAPTURE
return wxScrolledWindow::Destroy();
}
// -----------------------------------------------------------------------
wxPropertyGridState* wxPropertyGrid::CreateState() const
{
return new wxPropertyGridState();
}
// -----------------------------------------------------------------------
// wxPropertyGrid overridden wxWindow methods
// -----------------------------------------------------------------------
void wxPropertyGrid::SetWindowStyleFlag( long style )
{
long old_style = m_windowStyle;
if ( m_iFlags & wxPG_FL_INITIALIZED )
{
wxASSERT( m_pState );
if ( !(style & wxPG_HIDE_CATEGORIES) && (old_style & wxPG_HIDE_CATEGORIES) )
{
// Enable categories
EnableCategories( true );
}
else if ( (style & wxPG_HIDE_CATEGORIES) && !(old_style & wxPG_HIDE_CATEGORIES) )
{
// Disable categories
EnableCategories( false );
}
if ( !(old_style & wxPG_AUTO_SORT) && (style & wxPG_AUTO_SORT) )
{
//
// Autosort enabled
//
if ( !m_frozen )
PrepareAfterItemsAdded();
else
FROM_STATE(m_itemsAdded) = 1;
}
#if wxPG_SUPPORT_TOOLTIPS
if ( !(old_style & wxPG_TOOLTIPS) && (style & wxPG_TOOLTIPS) )
{
//
// Tooltips enabled
//
/*wxToolTip* tooltip = new wxToolTip ( wxEmptyString );
SetToolTip ( tooltip );
tooltip->SetDelay ( wxPG_TOOLTIP_DELAY );*/
}
else if ( (old_style & wxPG_TOOLTIPS) && !(style & wxPG_TOOLTIPS) )
{
//
// Tooltips disabled
//
wxScrolledWindow::SetToolTip ( (wxToolTip*) NULL );
}
#endif
}
wxScrolledWindow::SetWindowStyleFlag ( style );
if ( m_iFlags & wxPG_FL_INITIALIZED )
{
if ( (old_style & wxPG_HIDE_MARGIN) != (style & wxPG_HIDE_MARGIN) )
{
CalculateFontAndBitmapStuff( m_vspacing );
RedrawAllVisible();
}
}
}
// -----------------------------------------------------------------------
void wxPropertyGrid::Freeze()
{
m_frozen++;
wxScrolledWindow::Freeze();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::Thaw()
{
m_frozen--;
wxScrolledWindow::Thaw();
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
Refresh();
#endif
// Force property re-selection
if ( m_selected )
DoSelectProperty(m_selected, wxPG_SEL_FORCE);
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetExtraStyle( long exStyle )
{
if ( exStyle & wxPG_EX_NATIVE_DOUBLE_BUFFERING )
{
#if defined(__WXMSW__)
/*
// Don't use WS_EX_COMPOSITED just now.
HWND hWnd;
if ( m_iFlags & wxPG_FL_IN_MANAGER )
hWnd = (HWND)GetParent()->GetHWND();
else
hWnd = (HWND)GetHWND();
::SetWindowLong( hWnd, GWL_EXSTYLE,
::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED );
*/
//#elif defined(__WXGTK20__)
#endif
// Only apply wxPG_EX_NATIVE_DOUBLE_BUFFERING if the window
// truly was double-buffered.
if ( !wxPGIsWindowBuffered(this) )
{
exStyle &= ~(wxPG_EX_NATIVE_DOUBLE_BUFFERING);
}
else
{
#if wxPG_DOUBLE_BUFFER
delete m_doubleBuffer;
m_doubleBuffer = NULL;
#endif
}
}
wxScrolledWindow::SetExtraStyle( exStyle );
if ( exStyle & wxPG_EX_INIT_NOCAT )
m_pState->InitNonCatMode ();
if ( exStyle & wxPG_EX_HELP_AS_TOOLTIPS )
m_windowStyle |= wxPG_TOOLTIPS;
if ( exStyle & wxPG_EX_AUTO_UNSPECIFIED_VALUES )
{
wxPGGlobalVars->m_numBoolChoices = 3;
}
else
{
wxPGGlobalVars->m_numBoolChoices = 2;
}
}
// -----------------------------------------------------------------------
// returns the best acceptable minimal size
wxSize wxPropertyGrid::DoGetBestSize() const
{
int hei = 15;
if ( m_lineHeight > hei )
hei = m_lineHeight;
wxSize sz = wxSize( 60, hei+40 );
CacheBestSize(sz);
return sz;
}
// -----------------------------------------------------------------------
// wxPropertyGrid Font and Colour Methods
// -----------------------------------------------------------------------
void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing )
{
int x = 0, y = 0;
m_captionFont = GetFont();
GetTextExtent(wxT("jG"), &x, &y, 0, 0, &m_captionFont);
m_subgroup_extramargin = x + (x/2);
m_fontHeight = y;
#if wxPG_USE_RENDERER_NATIVE
m_iconWidth = wxPG_ICON_WIDTH;
#elif wxPG_ICON_WIDTH
// scale icon
m_iconWidth = (m_fontHeight * wxPG_ICON_WIDTH) / 13;
if ( m_iconWidth < 5 ) m_iconWidth = 5;
else if ( !(m_iconWidth & 0x01) ) m_iconWidth++; // must be odd
#endif
m_gutterWidth = m_iconWidth / wxPG_GUTTER_DIV;
if ( m_gutterWidth < wxPG_GUTTER_MIN )
m_gutterWidth = wxPG_GUTTER_MIN;
int vdiv = 6;
if ( vspacing <= 1 ) vdiv = 12;
else if ( vspacing >= 3 ) vdiv = 3;
m_spacingy = m_fontHeight / vdiv;
if ( m_spacingy < wxPG_YSPACING_MIN )
m_spacingy = wxPG_YSPACING_MIN;
m_marginWidth = 0;
if ( !(m_windowStyle & wxPG_HIDE_MARGIN) )
m_marginWidth = m_gutterWidth*2 + m_iconWidth;
m_captionFont.SetWeight(wxBOLD);
GetTextExtent(wxT("jG"), &x, &y, 0, 0, &m_captionFont);
m_lineHeight = m_fontHeight+(2*m_spacingy)+1;
// button spacing
m_buttonSpacingY = (m_lineHeight - m_iconHeight) / 2;
if ( m_buttonSpacingY < 0 ) m_buttonSpacingY = 0;
InvalidateBestSize();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnSysColourChanged( wxSysColourChangedEvent &WXUNUSED(event) )
{
RegainColours();
Refresh();
}
// -----------------------------------------------------------------------
static wxColour wxPGAdjustColour(const wxColour& src, int ra,
int ga = 1000, int ba = 1000,
bool forceDifferent = false)
{
if ( ga >= 1000 )
ga = ra;
if ( ba >= 1000 )
ba = ra;
// Recursion guard (allow 2 max)
static int isinside = 0;
isinside++;
wxCHECK_MSG( isinside < 3,
*wxBLACK,
wxT("wxPGAdjustColour should not be recursively called more than once") );
wxColour dst;
int r = src.Red();
int g = src.Green();
int b = src.Blue();
int r2 = r + ra;
if ( r2>255 ) r2 = 255;
else if ( r2<0) r2 = 0;
int g2 = g + ga;
if ( g2>255 ) g2 = 255;
else if ( g2<0) g2 = 0;
int b2 = b + ba;
if ( b2>255 ) b2 = 255;
else if ( b2<0) b2 = 0;
// Make sure they are somewhat different
if ( forceDifferent && (abs((r+g+b)-(r2+g2+b2)) < abs(ra/2)) )
dst = wxPGAdjustColour(src,-(ra*2));
else
dst = wxColour(r2,g2,b2);
// Recursion guard (allow 2 max)
isinside--;
return dst;
}
static int wxPGGetColAvg( const wxColour& col )
{
return (col.Red() + col.Green() + col.Blue()) / 3;
}
void wxPropertyGrid::RegainColours()
{
wxColour def_bgcol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
if ( !(m_coloursCustomized & 0x0002) )
{
wxColour col = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
// Make sure colour is dark enough
#ifdef __WXGTK__
int colDec = wxPGGetColAvg(col) - 230;
#else
int colDec = wxPGGetColAvg(col) - 200;
#endif
if ( colDec > 0 )
m_colCapBack = wxPGAdjustColour(col,-colDec);
else
m_colCapBack = col;
}
if ( !(m_coloursCustomized & 0x0001) )
m_colMargin = m_colCapBack;
if ( !(m_coloursCustomized & 0x0004) )
{
#ifdef __WXGTK__
int colDec = -90;
#else
int colDec = -72;
#endif
wxColour capForeCol = wxPGAdjustColour(m_colCapBack,colDec,5000,5000,true);
m_colCapFore = capForeCol;
// Set the cached colour as well.
((wxPGColour*)m_arrFgCols.Item(1))->SetColour2(capForeCol);
}
if ( !(m_coloursCustomized & 0x0008) )
{
wxColour bgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
m_colPropBack = bgCol;
// Set the cached brush as well.
((wxPGBrush*)m_arrBgBrushes.Item(0))->SetColour2(bgCol);
}
if ( !(m_coloursCustomized & 0x0010) )
{
wxColour fgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
m_colPropFore = fgCol;
// Set the cached colour as well.
((wxPGColour*)m_arrFgCols.Item(0))->SetColour2(fgCol);
}
if ( !(m_coloursCustomized & 0x0020) )
m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
if ( !(m_coloursCustomized & 0x0040) )
m_colSelFore = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT );
if ( !(m_coloursCustomized & 0x0080) )
m_colLine = m_colCapBack;
if ( !(m_coloursCustomized & 0x0100) )
m_colDisPropFore = m_colCapFore;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::ResetColours()
{
m_coloursCustomized = 0;
RegainColours();
Refresh();
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::SetFont( const wxFont& font )
{
#if __INTENSE_DEBUGGING__
wxLogDebug( wxT("wxPropertyGrid::SetFont") );
#endif
// Must disable active editor.
if ( m_selected )
{
bool selRes = ClearSelection();
wxPG_CHECK_MSG_DBG( selRes,
false,
wxT("failed to deselect a property (editor probably had invalid value)") );
}
// TODO: Following code is disabled with wxMac because
// it is reported to fail. I (JMS) cannot debug it
// personally right now.
#if !defined(__WXMAC__)
bool res = wxScrolledWindow::SetFont( font );
if ( res )
{
CalculateFontAndBitmapStuff( m_vspacing );
if ( m_pState )
{
// Recalculate caption text extents.
// TODO: This should also be done to other pages of manager
// (so add wxPropertyGridManager::SetFont), but since font
// is usually set before categories are added, this is
// quite low priority.
size_t i;
for ( i=0;i<FROM_STATE(m_regularArray).GetCount();i++ )
{
wxPGProperty* p = FROM_STATE(m_regularArray).Item(i);
if ( p->GetParentingType() > 0 )
((wxPropertyCategoryClass*)p)->CalculateTextExtent(this,m_captionFont);
}
CalculateYs(NULL,-1);
}
Refresh();
}
return res;
#else
// ** wxMAC Only **
// TODO: Remove after SetFont crash fixed.
if ( m_iFlags & wxPG_FL_INITIALIZED )
{
wxLogDebug(wxT("WARNING: propGrid.cpp: wxPropertyGrid::SetFont has been disabled on wxMac since there has been crash reported in it. If you are willing to debug the cause, replace line '#if !defined(__WXMAC__)' with line '#if 1' in wxPropertyGrid::SetFont."));
}
return false;
#endif
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetLineColour( const wxColour& col )
{
m_colLine = col;
m_coloursCustomized |= 0x80;
Refresh();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetMarginColour( const wxColour& col )
{
m_colMargin = col;
m_coloursCustomized |= 0x01;
Refresh();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetCellBackgroundColour( const wxColour& col )
{
m_colPropBack = col;
m_coloursCustomized |= 0x08;
// Set the cached brush as well.
((wxPGBrush*)m_arrBgBrushes.Item(0))->SetColour2(col);
Refresh();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetCellTextColour( const wxColour& col )
{
m_colPropFore = col;
m_coloursCustomized |= 0x10;
// Set the cached colour as well.
((wxPGColour*)m_arrFgCols.Item(0))->SetColour2(col);
Refresh();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetCellDisabledTextColour( const wxColour& col )
{
m_colDisPropFore = col;
m_coloursCustomized |= 0x100;
Refresh();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetSelectionBackground( const wxColour& col )
{
m_colSelBack = col;
m_coloursCustomized |= 0x20;
Refresh();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetSelectionForeground( const wxColour& col )
{
m_colSelFore = col;
m_coloursCustomized |= 0x40;
Refresh();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetCaptionBackgroundColour( const wxColour& col )
{
m_colCapBack = col;
m_coloursCustomized |= 0x02;
Refresh();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetCaptionForegroundColour( const wxColour& col )
{
m_colCapFore = col;
m_coloursCustomized |= 0x04;
// Set the cached colour as well.
((wxPGColour*)m_arrFgCols.Item(1))->SetColour2(col);
Refresh();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetBackgroundColourIndex( wxPGProperty* p, int index, int flags )
{
unsigned char ind = index;
if ( (p->m_bgColIndex == 0) || (flags & (wxPG_RECURSE_STARTS|wxPG_FORCE)) )
p->m_bgColIndex = ind;
if ( p->GetParentingType() != 0 && (flags & wxPG_RECURSE) )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
unsigned int i;
for ( i=0; i<pwc->GetCount(); i++ )
SetBackgroundColourIndex(pwc->Item(i), index, flags & ~(wxPG_RECURSE_STARTS));
}
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetPropertyBackgroundColour( wxPGId id, const wxColour& colour )
{
wxPG_PROP_ID_CALL_PROLOG()
size_t i;
int colInd = -1;
long colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
// As it is most likely that the previous colour is used, start comparison
// from the end.
for ( i=(m_arrBgBrushes.GetCount()-1); i>0; i-- )
{
if ( ((wxPGBrush*)m_arrBgBrushes.Item(i))->GetColourAsLong() == colAsLong )
{
colInd = i;
break;
}
}
if ( colInd < 0 )
{
colInd = m_arrBgBrushes.GetCount();
wxCHECK_RET( colInd < 256, wxT("wxPropertyGrid: Warning - Only 255 different property background colours allowed.") );
m_arrBgBrushes.Add( (void*)new wxPGBrush(colour) );
}
// Set indexes
SetBackgroundColourIndex(p, colInd, wxPG_RECURSE|wxPG_RECURSE_STARTS);
// If this was on a visible grid, then draw it.
DrawItemAndChildren(p);
}
// -----------------------------------------------------------------------
wxColour wxPropertyGrid::GetPropertyBackgroundColour( wxPGId id ) const
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxColour())
return ((wxPGBrush*)m_arrBgBrushes.Item(p->m_bgColIndex))->GetColour();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetTextColourIndex( wxPGProperty* p, int index, int flags )
{
unsigned char ind = index;
if ( (p->m_fgColIndex == 0) || (flags & (wxPG_RECURSE_STARTS|wxPG_FORCE)) )
p->m_fgColIndex = ind;
if ( p->GetParentingType() != 0 && (flags & wxPG_RECURSE) )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
unsigned int i;
for ( i=0; i<pwc->GetCount(); i++ )
SetTextColourIndex( pwc->Item(i), index, flags&~(wxPG_RECURSE_STARTS) );
}
}
// -----------------------------------------------------------------------
int wxPropertyGrid::CacheColour( const wxColour& colour )
{
unsigned int i;
int colInd = -1;
long colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
// As it is most likely that the previous colour is used, start comparison
// from the end.
for ( i=(m_arrFgCols.GetCount()-1); i>0; i-- )
{
if ( ((wxPGColour*)m_arrFgCols.Item(i))->GetColourAsLong() == colAsLong )
{
colInd = i;
break;
}
}
if ( colInd < 0 )
{
colInd = m_arrFgCols.GetCount();
wxCHECK_MSG( colInd < 256, 0, wxT("wxPropertyGrid: Warning - Only 255 different property foreground colours allowed.") );
m_arrFgCols.Add( (void*)new wxPGColour(colour) );
}
return colInd;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetPropertyTextColour( wxPGId id, const wxColour& colour )
{
wxPG_PROP_ID_CALL_PROLOG()
// Set indexes
SetTextColourIndex(p, CacheColour(colour), wxPG_RECURSE|wxPG_RECURSE_STARTS );
// If this was on a visible grid, then draw it.
DrawItemAndChildren(p);
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetCaptionTextColour( wxPGId id, const wxColour& colour )
{
wxPG_PROP_ID_CALL_PROLOG()
wxCHECK_RET( p->GetParentingType() == PT_CAPTION,
wxT("Only call SetCaptionTextColour for caption properties") );
// Set indexes
wxPropertyCategoryClass* cat = (wxPropertyCategoryClass*) p;
cat->SetTextColIndex(CacheColour(colour));
// If this was on a visible grid, then draw it.
DrawItemAndChildren(p);
}
// -----------------------------------------------------------------------
wxColour wxPropertyGrid::GetPropertyTextColour( wxPGId id ) const
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxColour())
return wxColour(*((wxPGColour*)m_arrFgCols.Item(p->m_fgColIndex)));
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetPropertyColourToDefault( wxPGId id )
{
wxPG_PROP_ID_CALL_PROLOG()
SetBackgroundColourIndex( p, 0, wxPG_RECURSE|wxPG_FORCE );
SetTextColourIndex( p, 0, wxPG_RECURSE|wxPG_FORCE );
if ( p->GetParentingType() == PT_CAPTION )
{
wxPropertyCategoryClass* cat = (wxPropertyCategoryClass*) p;
cat->SetTextColIndex(1);
}
}
// -----------------------------------------------------------------------
// wxPropertyGrid property adding and removal
// -----------------------------------------------------------------------
wxPGId wxPropertyGrid::Append( wxPGProperty* property )
{
return FROM_STATE(Append(property));
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGrid::_Insert( wxPGProperty* priorthis, wxPGProperty* property )
{
wxASSERT( priorthis );
return FROM_STATE(DoInsert(priorthis->GetParent(), priorthis->GetArrIndex(), property));
}
// -----------------------------------------------------------------------
void wxPropertyContainerMethods::Delete( wxPGId id )
{
wxPG_PROP_ID_CALL_PROLOG()
wxPropertyGridState* state = p->GetParentState();
wxPropertyGrid* grid = state->GetGrid();
if ( grid->GetState() == state )
{
bool selRes = grid->DoSelectProperty(wxPGIdGen(NULL), wxPG_SEL_DELETING);
wxPG_CHECK_RET_DBG( selRes,
wxT("failed to deselect a property (editor probably had invalid value)") );
}
state->DoDelete( p );
if ( grid->GetState() == state && !grid->IsFrozen() )
{
// This should be enough to resolve even the worst
// graphics glitch imaginable.
grid->Update();
grid->Refresh();
}
}
// -----------------------------------------------------------------------
wxPGId wxPropertyContainerMethods::ReplaceProperty( wxPGId id, wxPGProperty* property )
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
wxPGProperty* replaced = wxPGIdToPtr(id);
wxCHECK_MSG( replaced && property,
wxNullProperty,
wxT("NULL property") );
wxCHECK_MSG( replaced->GetParentingType() == 0 || replaced->GetParentingType() == -1,
wxNullProperty,
wxT("cannot replace this type of property") );
wxCHECK_MSG( !m_pState->IsInNonCatMode(),
wxNullProperty,
wxT("cannot replace properties in alphabetic mode") );
// Get address to the slot
wxPGPropertyWithChildren* parent = replaced->GetParent();
int ind = replaced->GetIndexInParent();
wxPropertyGridState* state = replaced->GetParentState();
Delete(replaced); // Must use generic Delete
state->DoInsert(parent,ind,property);
return wxPGIdGen(property);
}
// -----------------------------------------------------------------------
void wxPropertyGrid::PrepareAfterItemsAdded()
{
if ( !FROM_STATE(m_itemsAdded) ) return;
#if __INTENSE_DEBUGGING__
wxLogDebug(wxT("PrepareAfterItemsAdded( in thread 0x%lX )"),
(unsigned long)wxThread::GetCurrentId());
#endif
FROM_STATE(m_itemsAdded) = 0;
if ( m_windowStyle & wxPG_AUTO_SORT )
{
Sort ();
}
else
{
if ( m_bottomy < 1 )
CalculateYs( NULL, -1 );
else
{
RecalculateVirtualSize();
// Update visibles array (maybe not necessary here, but just in case)
CalculateVisibles ( -1, true );
}
}
}
// -----------------------------------------------------------------------
// wxPropertyGrid property value setting and getting
// -----------------------------------------------------------------------
void wxPGGetFailed( const wxPGProperty* p, const wxChar* typestr )
{
wxPGTypeOperationFailed(p,typestr,wxT("Get"));
}
// -----------------------------------------------------------------------
void wxPGTypeOperationFailed( const wxPGProperty* p, const wxChar* typestr,
const wxChar* op )
{
wxASSERT( p != NULL );
wxLogError( _("Type operation \"%s\" failed: Property labeled \"%s\" is of type \"%s\", NOT \"%s\"."),
op,p->GetLabel().c_str(),wxPG_TO_WXCHAR_PTR(p->GetValueTypePtr()->GetCustomTypeName()),typestr );
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetPropertyValue( wxPGId id, const wxPGValueType* typeclass, const wxPGVariant& value )
{
wxPG_PROP_ID_CALL_PROLOG()
if ( p && m_pState->SetPropertyValue(p,typeclass,value) )
DrawItemAndValueRelated( p );
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetPropertyValue( wxPGId id, const wxChar* typestring, const wxPGVariant& value )
{
wxPG_PROP_ID_CALL_PROLOG()
if ( p && m_pState->SetPropertyValue(p,typestring,value) )
DrawItemAndValueRelated( p );
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetPropertyValueString( wxPGId id, const wxString& value )
{
wxPG_PROP_ID_CALL_PROLOG()
if ( m_pState->SetPropertyValueString(p,value) )
DrawItemAndValueRelated( p );
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetPropertyValueWxObjectPtr( wxPGId id, wxObject* value )
{
wxPG_PROP_ID_CALL_PROLOG()
if ( m_pState->SetPropertyValueWxObjectPtr(p,value) )
DrawItemAndValueRelated( p );
}
// -----------------------------------------------------------------------
#ifndef __WXPYTHON__
void wxPropertyGrid::SetPropertyValue( wxPGId id, wxVariant& value )
{
wxPG_PROP_ID_CALL_PROLOG()
if ( m_pState->SetPropertyValue(p,value) )
DrawItemAndValueRelated( p );
}
#endif
// -----------------------------------------------------------------------
bool wxPropertyGrid::ClearPropertyValue( wxPGId id )
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
if ( m_pState->ClearPropertyValue(p) )
{
DrawItemAndChildren( p );
return true;
}
return false;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetPropertyUnspecified( wxPGId id )
{
wxPG_PROP_ID_CALL_PROLOG()
m_pState->SetPropertyUnspecified(p);
DrawItemAndChildren(p);
wxPGPropertyWithChildren* parent = p->GetParent();
while ( parent->GetParentingType() <= PT_FIXEDCHILDREN )
{
DrawItem(parent);
parent = parent->GetParent();
}
}
// -----------------------------------------------------------------------
// wxPropertyGrid miscellaneous GetPropertyXXX methods
// -----------------------------------------------------------------------
wxPropertyCategoryClass* wxPropertyGrid::_GetPropertyCategory( wxPGProperty* p )
{
wxPGProperty* parent = (wxPGPropertyWithChildren*)p;
wxPGProperty* grandparent = (wxPGProperty*)parent->GetParent();
do
{
parent = grandparent;
grandparent = (wxPGProperty*)parent->GetParent();
if ( parent->GetParentingType() >= PT_CAPTION && grandparent )
return (wxPropertyCategoryClass*)parent;
} while ( grandparent );
return (wxPropertyCategoryClass*) NULL;
}
// -----------------------------------------------------------------------
// wxPropertyGrid property operations
// -----------------------------------------------------------------------
bool wxPropertyGrid::EnableProperty( wxPGId id, bool enable )
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
if ( enable )
{
if ( !(p->m_flags & wxPG_PROP_DISABLED) )
return false;
// If active, Set active Editor.
if ( p == m_selected )
DoSelectProperty( p, wxPG_SEL_FORCE );
}
else
{
if ( p->m_flags & wxPG_PROP_DISABLED )
return false;
// If active, Disable as active Editor.
if ( p == m_selected )
DoSelectProperty( p, wxPG_SEL_FORCE );
}
m_pState->EnableProperty(p,enable);
DrawItemAndChildren( p );
return true;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::LimitPropertyEditing( wxPGId id, bool limit )
{
wxPG_PROP_ID_CALL_PROLOG()
m_pState->LimitPropertyEditing(p,limit);
if ( p == m_selected )
DoSelectProperty( p, wxPG_SEL_FORCE );
}
// -----------------------------------------------------------------------
void wxPropertyGrid::_SetPropertyLabel( wxPGProperty* p, const wxString& newproplabel )
{
wxCHECK_RET( p, wxT("invalid property id") );
p->m_label = newproplabel;
if ( m_windowStyle & wxPG_AUTO_SORT )
{
Sort(p->GetParent());
Refresh();
}
else
DrawItem ( p );
}
// -----------------------------------------------------------------------
void wxPropertyGrid::DoSetPropertyName( wxPGProperty* p, const wxString& newname )
{
wxCHECK_RET( p, wxT("invalid property id") );
#if __INTENSE_DEBUGGING__
wxLogDebug( wxT("wxPropertyGrid::SetPropertyName( %s -> %s )"),
p->GetName().c_str(), newname.c_str() );
#endif
if ( p->GetName().Len() ) FROM_STATE(m_dictName).erase ( wxPGNameConv(p->GetName()) );
if ( newname.Len() ) FROM_STATE(m_dictName)[newname] = (void*) p;
p->DoSetName(newname);
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::EnsureVisible( wxPGId id )
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
Update();
bool changed = false;
// Is it inside collapsed section?
if ( p->m_y < 0 )
{
// expand parents
wxPGProperty* parent = p->GetParent();
wxPGProperty* grandparent = parent->GetParent();
if ( grandparent && grandparent != FROM_STATE(m_properties) )
Expand ( grandparent );
Expand ( parent );
changed = true;
}
// Need to scroll?
int vx, vy;
GetViewStart(&vx,&vy);
vy*=wxPG_PIXELS_PER_UNIT;
if ( p->m_y < vy )
{
Scroll (vx, p->m_y/wxPG_PIXELS_PER_UNIT );
m_iFlags |= wxPG_FL_SCROLLED;
changed = true;
}
else if ( (p->m_y+m_lineHeight) > (vy+m_height) )
{
Scroll (vx, (p->m_y-m_height+(m_lineHeight*2))/wxPG_PIXELS_PER_UNIT );
m_iFlags |= wxPG_FL_SCROLLED;
changed = true;
}
if ( changed )
DrawItems ( p, p );
return changed;
}
// -----------------------------------------------------------------------
// wxPropertyGrid helper methods called by properties
// -----------------------------------------------------------------------
// Fixes position of wxTextCtrl-like control (wxSpinCtrl usually
// fits into that category as well).
void wxPropertyGrid::FixPosForTextCtrl( wxWindow* ctrl )
{
// Center the control vertically
wxRect finalPos = ctrl->GetRect();
int y_adj = (m_lineHeight - finalPos.height)/2 + wxPG_TEXTCTRLYADJUST;
// Prevent over-sized control
int sz_dec = (y_adj + finalPos.height) - m_lineHeight;
if ( sz_dec < 0 ) sz_dec = 0;
finalPos.y += y_adj;
finalPos.height -= (y_adj+sz_dec);
// STUPID HACK: wxTextCtrl has different indentation with different
// fonts, so this is to solve most common case (ie. using MS Shell Dlg 2
// or Tahoma - which are usually the same).
/*#ifdef __WXMSW__
wxString faceName = m_font.GetFaceName();
int textCtrlXAdjust = wxPG_TEXTCTRLXADJUST;
if ( (faceName == wxT("MS Shell Dlg 2") ||
faceName == wxT("Tahoma")) &&
m_font.GetWeight() != wxFONTWEIGHT_BOLD )
textCtrlXAdjust = 0;
#else*/
const int textCtrlXAdjust = wxPG_TEXTCTRLXADJUST;
//#endif
finalPos.x += textCtrlXAdjust;
finalPos.width -= textCtrlXAdjust;
ctrl->SetSize(finalPos);
}
// -----------------------------------------------------------------------
// Control font changer helper.
void wxPropertyGrid::SetCurControlBoldFont()
{
wxASSERT( m_wndPrimary );
m_wndPrimary->SetFont( m_captionFont );
}
// -----------------------------------------------------------------------
wxWindow* wxPropertyGrid::GenerateEditorTextCtrl( const wxPoint& pos,
const wxSize& sz,
const wxString& value,
wxWindow* secondary,
int extraStyle,
int maxLen )
{
int tcFlags = wxTE_PROCESS_ENTER | extraStyle;
if ( m_selected->HasFlag(wxPG_PROP_READONLY) )
tcFlags |= wxTE_READONLY;
wxPoint p(pos.x,pos.y);
wxSize s(sz.x,sz.y);
// Need to reduce width of text control on Mac
#if defined(__WXMAC__)
s.x -= 8;
#endif
// Take button into acccount
if ( secondary )
{
s.x -= (secondary->GetSize().x + wxPG_TEXTCTRL_AND_BUTTON_SPACING);
m_iFlags &= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE);
}
// If the height is significantly higher, then use border, and fill the rect exactly.
bool hasSpecialSize = false;
if ( (sz.y - m_lineHeight) > 5 )
hasSpecialSize = true;
#if wxPG_NAT_TEXTCTRL_BORDER_ANY
// Create clipper window
wxPGClipperWindow* wnd = new wxPGClipperWindow();
#if defined(__WXMSW__)
wnd->Hide();
#endif
wnd->Create(this,wxPG_SUBID1,p,s);
// This generates rect of the control inside the clipper window
if ( !hasSpecialSize )
wnd->GetControlRect(wxPG_NAT_TEXTCTRL_BORDER_X, wxPG_NAT_TEXTCTRL_BORDER_Y, p, s);
else
wnd->GetControlRect(0, 0, p, s);
wxWindow* ctrlParent = wnd;
#else
wxWindow* ctrlParent = this;
if ( !hasSpecialSize )
tcFlags |= wxNO_BORDER;
#endif
wxTextCtrl* tc = new wxTextCtrl();
#if defined(__WXMSW__) && !wxPG_NAT_TEXTCTRL_BORDER_ANY
tc->Hide();
#endif
tc->Create(ctrlParent,wxPG_SUBID1,value, p, s,tcFlags);
#if wxPG_NAT_TEXTCTRL_BORDER_ANY
wxWindow* ed = wnd;
wnd->SetControl(tc);
#else
wxWindow* ed = tc;
#endif
// Center the control vertically
if ( !hasSpecialSize )
FixPosForTextCtrl(ed);
#ifdef __WXMSW__
ed->Show();
if ( secondary )
secondary->Show();
#endif
// Set maximum length
if ( maxLen > 0 )
tc->SetMaxLength( maxLen );
return (wxWindow*) ed;
}
// -----------------------------------------------------------------------
wxWindow* wxPropertyGrid::GenerateEditorButton( const wxPoint& pos, const wxSize& sz )
{
#ifdef __WXMAC__
// Decorations are chunky on Mac, and we can't make the button square, so
// do things a bit differently on this platform.
wxPoint p(pos.x+sz.x,
pos.y+wxPG_BUTTON_SIZEDEC-wxPG_NAT_BUTTON_BORDER_Y);
wxSize s(25, -1);
wxButton* but = new wxButton();
but->Create(this,wxPG_SUBID2,wxT("..."),p,s,wxWANTS_CHARS);
// Now that we know the size, move to the correct position
p.x = pos.x + sz.x - but->GetSize().x - 2;
but->Move(p);
#else
wxSize s(sz.y-(wxPG_BUTTON_SIZEDEC*2)+(wxPG_NAT_BUTTON_BORDER_Y*2),
sz.y-(wxPG_BUTTON_SIZEDEC*2)+(wxPG_NAT_BUTTON_BORDER_Y*2));
// Reduce button width to lineheight
if ( s.x > m_lineHeight )
s.x = m_lineHeight;
wxPoint p(pos.x+sz.x-s.x,
pos.y+wxPG_BUTTON_SIZEDEC-wxPG_NAT_BUTTON_BORDER_Y);
wxButton* but = new wxButton();
#ifdef __WXMSW__
but->Hide();
#endif
but->Create(this,wxPG_SUBID2,wxT("..."),p,s,wxWANTS_CHARS);
but->SetFont( m_captionFont );
#endif
if ( m_selected->HasFlag(wxPG_PROP_READONLY) )
but->Disable();
return but;
}
// -----------------------------------------------------------------------
wxWindow* wxPropertyGrid::GenerateEditorTextCtrlAndButton( const wxPoint& pos,
const wxSize& sz,
wxWindow** psecondary,
int limitedEditing,
wxPGProperty* property )
{
wxButton* but = (wxButton*)GenerateEditorButton(pos,sz);
*psecondary = (wxWindow*)but;
if ( limitedEditing )
{
#ifdef __WXMSW__
// There is button Show in GenerateEditorTextCtrl as well
but->Show();
#endif
return (wxWindow*) NULL;
}
wxString text;
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
text = property->GetValueAsString(property->HasFlag(wxPG_PROP_READONLY)?0:wxPG_EDITABLE_VALUE);
return GenerateEditorTextCtrl(pos,sz,text,but,property->m_maxLen);
}
// -----------------------------------------------------------------------
wxPoint wxPropertyGrid::GetGoodEditorDialogPosition( wxPGProperty* p,
const wxSize& sz )
{
#if wxPG_SMALL_SCREEN
// On small-screen devices, always show dialogs with default position and size.
return wxDefaultPosition;
#else
int x = m_splitterx;
int y = p->m_y;
wxCHECK_MSG( y >= 0, wxPoint(-1,-1), wxT("invalid y?") );
wxCHECK_MSG( y < (int)m_bottomy, wxPoint(-1,-1), wxT("invalid y?") );
ImprovedClientToScreen( &x, &y );
int sw = wxSystemSettings::GetMetric( ::wxSYS_SCREEN_X );
int sh = wxSystemSettings::GetMetric( ::wxSYS_SCREEN_Y );
int new_x;
int new_y;
if ( x > (sw/2) )
// left
new_x = x + (m_width-m_splitterx) - sz.x;
else
// right
new_x = x;
if ( y > (sh/2) )
// above
new_y = y - sz.y;
else
// below
new_y = y + m_lineHeight;
return wxPoint(new_x,new_y);
#endif
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SLAlloc( unsigned int itemcount, const wxChar** items )
{
wxArrayString& sl = m_sl;
unsigned int i;
unsigned int sl_oldcount = sl.GetCount();
if ( sl_oldcount > itemcount ) sl_oldcount = itemcount;
#if wxUSE_INTL
if ( !wxPGGlobalVars->m_autoGetTranslation )
{
#endif
for ( i=0; i<sl_oldcount; i++ )
sl.Item(i) = items[i];
for ( i=sl_oldcount; i<itemcount; i++ )
sl.Add ( items[i] );
#if wxUSE_INTL
}
else
{
for ( i=0; i<sl_oldcount; i++ )
sl.Item(i) = ::wxGetTranslation ( items[i] );
for ( i=sl_oldcount; i<itemcount; i++ )
sl.Add ( ::wxGetTranslation ( items[i] ) );
}
#endif
}
wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& src_str )
{
if ( src_str.length() == 0 )
{
dst_str = src_str;
return src_str;
}
bool prev_is_slash = false;
wxString::const_iterator i = src_str.begin();
dst_str.clear();
for ( ; i != src_str.end(); i++ )
{
wxUniChar a = wxPGGetIterChar(src_str, i);
if ( a != wxT('\\') )
{
if ( !prev_is_slash )
{
dst_str << a;
}
else
{
if ( a == wxT('n') )
{
#ifdef __WXMSW__
dst_str << wxT('\n');
//dst_str << wxT('\10');
#else
dst_str << wxT('\n');
//dst_str << 10;
#endif
}
else if ( a == wxT('t') )
dst_str << wxT('\t');
else
dst_str << a;
}
prev_is_slash = false;
}
else
{
if ( prev_is_slash )
{
dst_str << wxT('\\');
prev_is_slash = false;
}
else
{
prev_is_slash = true;
}
}
}
return dst_str;
}
// -----------------------------------------------------------------------
wxString& wxPropertyGrid::CreateEscapeSequences( wxString& dst_str, wxString& src_str )
{
if ( src_str.length() == 0 )
{
dst_str = src_str;
return src_str;
}
wxString::const_iterator i = src_str.begin();
wxChar prev_a = wxT('\0');
dst_str.clear();
for ( ; i != src_str.end(); i++ )
{
wxChar a = wxPGGetIterChar(src_str, i);
if ( a >= wxT(' ')
#if !wxUSE_UNICODE
|| a < 0
#endif
)
{
// This surely is not something that requires an escape sequence.
dst_str << a;
}
else
{
// This might need...
if ( a == wxT('\r') )
{
// DOS style line end.
// Already taken care below
//dst_str = wxT("\\n");
//src++;
}
else if ( a == wxT('\n') )
// UNIX style line end.
dst_str << wxT("\\n");
else if ( a == wxT('\t') )
// Tab.
dst_str << wxT('\t');
else
{
//wxLogDebug(wxT("WARNING: Could not create escape sequence for character #%i"),(int)a);
dst_str << a;
}
}
prev_a = a;
}
return dst_str;
}
// -----------------------------------------------------------------------
// Item iteration macros
// -----------------------------------------------------------------------
#define II_INVALID_I 0x00FFFFFF
#define ITEM_ITERATION_VARIABLES \
wxPGPropertyWithChildren* parent; \
unsigned int i; \
unsigned int iMax;
#define ITEM_ITERATION_DCAE_VARIABLES \
wxPGPropertyWithChildren* parent; \
unsigned int i; \
unsigned int iMax;
#define ITEM_ITERATION_INIT_FROM_THE_TOP \
parent = FROM_STATE(m_properties); \
i = 0;
#define ITEM_ITERATION_INIT(startparent,startindex) \
parent = startparent; \
i = (unsigned int)startindex; \
if ( parent == (wxPGPropertyWithChildren*) NULL ) \
{ \
parent = FROM_STATE(m_properties); \
i = 0; \
}
#define ITEM_ITERATION_LOOP_BEGIN \
unsigned char parent_expanded; \
do \
{ \
parent_expanded = (unsigned char)parent->m_expanded; \
if ( parent->m_parent && !parent->m_parent->m_expanded ) \
parent_expanded = 0; \
iMax = parent->GetCount(); \
while ( i < iMax ) \
{ \
wxPGProperty* p = parent->Item(i); \
int parenting = p->GetParentingType();
#define ITEM_ITERATION_LOOP_END \
if ( parenting ) \
{ \
i = 0; \
parent = (wxPGPropertyWithChildren*)p; \
if ( parent_expanded ) \
parent_expanded = (unsigned char)parent->m_expanded; \
else \
parent_expanded = 0; \
iMax = parent->GetCount(); \
} \
else \
i++; \
} \
i = parent->m_arrIndex + 1; \
parent = parent->m_parent; \
} \
while ( parent != NULL );
// DCAE = Don't care about parent_expanded (this is the least space hungry method).
#define ITEM_ITERATION_DCAE_LOOP_BEGIN \
do \
{ \
iMax = parent->GetCount(); \
while ( i < iMax ) \
{ \
wxPGProperty* p = parent->Item(i); \
int parenting = p->GetParentingType();
#define ITEM_ITERATION_DCAE_LOOP_END \
if ( parenting ) \
{ \
i = 0; \
parent = (wxPGPropertyWithChildren*)p; \
iMax = parent->GetCount(); \
} \
else \
i++; \
} \
i = parent->m_arrIndex + 1; \
parent = parent->m_parent; \
} \
while ( parent != NULL );
// DCAE_ISP = Don't care about parent_expanded, Ignore sub-properties.
// Note that this treats fixed sub-properties same as sub-properties
// of wxParentProperty. Mode conversion requires this behaviour.
#define ITEM_ITERATION_DCAE_ISP_LOOP_BEGIN \
do \
{ \
iMax = parent->GetCount(); \
while ( i < iMax ) \
{ \
wxPGProperty* p = parent->Item(i); \
int parenting = p->GetParentingType();
#define ITEM_ITERATION_DCAE_ISP_LOOP_END \
if ( parenting > 0 ) \
{ \
i = 0; \
parent = (wxPGPropertyWithChildren*)p; \
iMax = parent->GetCount(); \
} \
else \
i++; \
} \
i = parent->m_arrIndex + 1; \
parent = parent->m_parent; \
} \
while ( parent != (wxPGPropertyWithChildren*) NULL );
// VO = Visible only (including those outside the scrolled section).
#define ITEM_ITERATION_VO_LOOP_BEGIN \
if ( (parent == FROM_STATE(m_properties) || parent->m_y >= 0) && parent->m_expanded ) \
{ \
do \
{ \
iMax = parent->GetCount(); \
while ( i < iMax ) \
{ \
wxPGProperty* p = parent->Item(i); \
if ( p->m_y >= 0 ) \
{ \
int parenting = p->GetParentingType();
#define ITEM_ITERATION_VO_LOOP_END \
if ( parenting && ((wxPGPropertyWithChildren*)p)->m_expanded ) \
{ \
parent = (wxPGPropertyWithChildren*)p; \
i = 0; \
break; \
} \
} \
i++; \
} \
if ( i >= iMax ) \
{ \
i = parent->m_arrIndex + 1; \
parent = parent->m_parent; \
} \
} \
while ( parent != (wxPGPropertyWithChildren*) NULL ); \
}
// -----------------------------------------------------------------------
// wxPropertyGrid visibility related methods
// -----------------------------------------------------------------------
void wxPropertyGrid::CalculateYs( wxPGPropertyWithChildren* startparent,
int startindex )
{
// Selection must be temporarily cleared during y-recalc
wxPGProperty* prevSelected = m_selected;
if ( prevSelected )
{
bool selRes = ClearSelection();
wxPG_CHECK_RET_DBG( selRes,
wxT("failed to deselect a property (editor probably had invalid value)") );
}
ITEM_ITERATION_VARIABLES
#if __INTENSE_DEBUGGING__
wxLogDebug(wxT("CalculateYs(startsfrom: %s[%i] ) "),
startparent?startparent->m_label.c_str():wxT("NULL"),
startindex);
#endif
ITEM_ITERATION_INIT(startparent,startindex)
wxASSERT( !m_frozen );
int cury = 0;
int lh = m_lineHeight;
if ( startparent != NULL )
cury = parent->Item(i)->m_y;
wxASSERT_MSG( cury >= 0, wxT("CalculateYs first item was not visible!!!") );
long hide_state = m_iFlags & wxPG_FL_HIDE_STATE;
bool inside_hidden_part = false;
//parent_expanded = (unsigned char)parent->m_expanded;
wxPGPropertyWithChildren* nearest_expanded = (wxPGPropertyWithChildren*) NULL;
// Find first visible and expanded parent.
while ( !parent->IsExpanded() ||
( (parent->m_flags & wxPG_PROP_HIDEABLE) && hide_state )
)
{
parent = parent->GetParent();
i = 0;
}
wxASSERT( parent );
//parent = nearest_expanded;
do
{
iMax = parent->GetCount();
if ( !inside_hidden_part )
{
while ( i < iMax )
{
wxPGProperty* p = parent->Item(i);
int parenting = p->GetParentingType();
if ( !(p->m_flags & wxPG_PROP_HIDEABLE) || (!hide_state) )
{
// item is visible (all parents are expanded, non-hideable or not in hide state)
p->m_y = (int)cury;
cury += lh;
}
else
{
p->m_y = -1;
}
if ( parenting )
{
wxPGPropertyWithChildren* p2 = (wxPGPropertyWithChildren*)p;
if ( !p2->m_expanded ||
( (p2->m_flags & wxPG_PROP_HIDEABLE) && hide_state )
)
{
inside_hidden_part = true;
nearest_expanded = parent;
}
parent = p2;
i = 0;
break;
}
i++;
}
}
else
{
while ( i < iMax )
{
wxPGProperty* p = parent->Item(i);
int parenting = p->GetParentingType();
p->m_y = -1;
if ( parenting )
{
parent = (wxPGPropertyWithChildren*)p;
i = 0;
break;
}
i++;
}
}
if ( i >= iMax )
{
i = parent->m_arrIndex + 1;
parent = parent->m_parent;
if ( inside_hidden_part && parent == nearest_expanded )
{
inside_hidden_part = false;
}
}
}
while ( parent != (wxPGPropertyWithChildren*) NULL );
m_bottomy = cury;
#if __INTENSE_DEBUGGING__
wxLogDebug(wxT(" \\-> m_bottomy = %i"),(int)m_bottomy);
#endif
// Forces a new DoGetBestSize() call.
wxScrolledWindow::InvalidateBestSize();
// Visibles need to be recalculated *always* after y recalculation
// (but make sure it stays here, above RecalculateVirtualSize).
CalculateVisibles( -1, true );
RecalculateVirtualSize();
// Reselect
if ( prevSelected )
DoSelectProperty( prevSelected, wxPG_SEL_NONVISIBLE );
}
// -----------------------------------------------------------------------
// Call when scroll position changes. Do not pre-fill m_prevVY.
void wxPropertyGrid::CalculateVisibles( int vy, bool full_recalc )
{
if ( vy < 0 )
{
int vx;
GetViewStart(&vx,&vy);
vy *= wxPG_PIXELS_PER_UNIT;
if ( full_recalc )
m_prevVY = -1;
}
// Control not yet properly built.
if ( vy >= (int)m_bottomy )
return;
if ( m_height < 0 )
return;
// Hide popup
// FIXME: Delete after transient popup support fully added
if ( m_wndPrimary && m_wndPrimary->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
((wxPGOwnerDrawnComboBox*)m_wndPrimary)->HidePopup();
int vy2 = vy + m_height;
if ( vy2 > (int)m_bottomy )
vy2 = m_bottomy;
unsigned int arr_index = 0;
unsigned int vis_height = vy2-vy;
unsigned int new_item_count = vis_height/m_lineHeight;
if ( vis_height % m_lineHeight )
new_item_count++;
wxPGArrayProperty& arr = m_arrVisible;
arr.SetCount ( new_item_count );
#if __INTENSE_DEBUGGING__
wxLogDebug( wxT("wxPropertyGrid::CalculateVisibles ( vy=%i, vy2=%i, m_height=%i, newitemcount=%i, lineheight=%i )"),
(int)vy, (int)vy2, (int)m_height, (int)new_item_count, (int)m_lineHeight );
#endif
//wxASSERT( vy != m_prevVY );
wxASSERT( vy >= 0 );
if ( !new_item_count )
{
arr.Empty();
return;
}
ITEM_ITERATION_VARIABLES
wxPGProperty* base = NULL;
// Will simpler operation be enough?
if ( m_prevVY >= 0 )
{
if ( m_calcVisHeight == m_height )
{
if ( m_iFlags & wxPG_FL_SCROLLED )
{
int diff = vy - m_prevVY;
if ( diff == m_lineHeight )
{
// Scrolled one down
base = DoGetItemAtY_Full( vy2 - 1 );
wxASSERT( base );
arr_index = new_item_count - 1;
for ( i=0; i<arr_index; i++ )
arr.Item(i) = arr.Item(i+1);
arr.Item(arr_index) = base;
base = (wxPGProperty*) NULL;
}
else if ( diff == -m_lineHeight )
{
// Scrolled one up
base = DoGetItemAtY_Full( vy );
wxASSERT( base );
vy2 = vy + m_lineHeight; // update visibility
for ( i=(new_item_count-1); i>arr_index; i-- )
arr.Item(i) = arr.Item(i-1);
arr.Item(arr_index) = base;
base = (wxPGProperty*) NULL;
}
else
base = DoGetItemAtY_Full( vy );
}
else
base = DoGetItemAtY_Full( vy );
}
else
if ( m_prevVY == vy && !(m_iFlags & wxPG_FL_SCROLLED) )
{
if ( m_height > m_calcVisHeight )
{
// Increased height - add missing items
arr_index = (m_calcVisHeight-1)/m_lineHeight;
if ( arr_index >= new_item_count )
{
// Now, were probably below last item here
//if ( (vy+m_calcVisHeight) >= (int)m_bottomy )
base = NULL;
/*else
arr_index = arr.GetCount()-1;*/
}
else
{
base = (wxPGProperty*) arr.Item( arr_index );
}
}
else
{
// Decreased height - do nothing
//base = NULL;
}
}
else
base = DoGetItemAtY_Full( vy );
}
else
{
base = DoGetItemAtY_Full( vy );
}
if ( base )
{
ITEM_ITERATION_INIT(base->m_parent,base->m_arrIndex)
#if __INTENSE_DEBUGGING__
wxLogDebug( wxT(" Starting at index %i"), (int)arr_index );
#endif
ITEM_ITERATION_VO_LOOP_BEGIN
//wxASSERT( p->m_y >= 0 );
// update visibility limit reached?
if ( p->m_y >= vy2 ) { parent = NULL; break; }
#ifdef __WXDEBUG__
if ( arr_index >= arr.GetCount() )
{
wxLogDebug(wxT(" wxPropertyGrid::CalculateVisibles Loop overflow (index=%i,vy+vis_height=%i,p->m_y=%i)"),
(int)arr_index,(int)(vy+vis_height),(int)p->m_y);
}
#endif
arr.Item(arr_index) = (void*)p;
arr_index++;
ITEM_ITERATION_VO_LOOP_END
}
// Adjust controls
/*if ( m_selected )
{
int adjust = prevVY - vy;
if ( adjust )
{
wxPoint cp(0,adjust);
if ( m_wndPrimary )
m_wndPrimary->Move ( m_wndPrimary->GetPosition() + cp );
if ( m_wndSecondary )
m_wndSecondary->Move ( m_wndSecondary->GetPosition() + cp );
}
}*/
m_iFlags &= ~(wxPG_FL_SCROLLED);
m_prevVY = vy;
m_calcVisHeight = m_height;
}
// -----------------------------------------------------------------------
// This version uses the visible item cache.
wxPGProperty* wxPropertyGrid::DoGetItemAtY( int y )
{
//wxASSERT( m_prevVY >= 0 );
// Outside(check 1)?
if ( y >= (int)m_bottomy || y < 0 )
{
/*
#if __PAINT_DEBUGGING__
wxLogDebug(wxT("WARNING: DoGetItemAtY(a): y = %i"),y);
#endif
*/
return (wxPGProperty*) NULL;
}
int vx, vy;
GetViewStart(&vx,&vy);
vy*=wxPG_PIXELS_PER_UNIT;
// Need to recalculate visibility cache
// Note: need to check for y < m_prevVY is a hack.
if ( m_prevVY != vy ||y < m_prevVY ) //m_iFlags & wxPG_FL_SCROLLED ||
CalculateVisibles( vy, true );
// Outside(check 2)?
if ( y >= (vy+m_height) )
{
/*
#if __PAINT_DEBUGGING__
wxLogDebug(wxT("WARNING: DoGetItemAtY(b): y = %i"),y);
#endif
*/
return (wxPGProperty*) NULL;
}
unsigned int index = (unsigned int)((y - vy) / m_lineHeight);
// Consistency checks
if ( !m_arrVisible.GetCount() )
return (wxPGProperty*) NULL;
if ( index >= m_arrVisible.GetCount() )
{
#ifdef __WXDEBUG__
wxLogDebug(wxT(" index = %i"),(int)index);
wxLogDebug(wxT(" (height/lineheight+1) = %i"),(int)((m_height/m_lineHeight)+1));
wxLogDebug(wxT(" m_arrVisible.GetCount() = %i"),(int)m_arrVisible.GetCount());
// This was wxCHECK_MSG, but I don't want it to show, since it can happen from
// time to time, and I probably won't fix in the current version of wxPropertyGrid.
wxLogDebug( wxT("Not enough entries in m_arrVisible (y was < m_bottomy).") );
#endif
return (wxPGProperty*) NULL;
}
if ( index >= m_arrVisible.GetCount() )
{
index = m_arrVisible.GetCount()-1;
}
return (wxPGProperty*)m_arrVisible.Item(index);
}
// -----------------------------------------------------------------------
// wxPropertyGrid graphics related methods
// -----------------------------------------------------------------------
void wxPropertyGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
{
wxPG_PAINT_DC_INIT()
// Don't paint after destruction has begun
if ( !(m_iFlags & wxPG_FL_INITIALIZED) )
return;
#if __PAINT_DEBUGGING__
wxLogDebug( wxT("wxPropertyGrid::OnPaint()") );
#endif
// Find out where the window is scrolled to
int vx,vy; // Top left corner of client
GetViewStart(&vx,&vy);
vy *= wxPG_PIXELS_PER_UNIT;
// Update everything inside the box
wxRect r = GetUpdateRegion().GetBox();
r.y += vy;
// Repaint this rectangle
//if ( r.height > 0 )
DrawItems ( dc, r.y, r.y + r.height,
#if wxPG_ALLOW_CLIPPING
NULL //&r
#else
NULL
#endif
);
// We assume that the size set when grid is shown
// is what is desired.
m_iFlags |= wxPG_FL_GOOD_SIZE_SET;
}
// -----------------------------------------------------------------------
//
// This is the one called by OnPaint event handler and others.
// topy and bottomy are already unscrolled
// Clears any area in coordinates that doesn't have items.
//
void wxPropertyGrid::DrawItems( wxDC& dc,
unsigned int topy,
unsigned int bottomy,
const wxRect* clipRect )
{
if ( m_frozen || m_height < 1 || bottomy < topy || !m_pState ) return;
#if __PAINT_DEBUGGING__
wxLogDebug(wxT("wxPropertyGrid::DrawItems ( %i -> %i, clipRect = 0x%X )"),topy,bottomy,
(unsigned int)clipRect);
#endif
// items added check
if ( FROM_STATE(m_itemsAdded) ) PrepareAfterItemsAdded();
unsigned int vx, vy; // Top left corner of client
GetViewStart((int*)&vx,(int*)&vy);
vy *= wxPG_PIXELS_PER_UNIT;
unsigned int client_bottom = (unsigned int)m_height + vy;
// Clip topy and bottomy
if ( bottomy > client_bottom )
bottomy = client_bottom;
if ( topy < vy )
topy = vy;
#if __PAINT_DEBUGGING__
wxLogDebug(wxT(" \\--> ( final area %i -> %i )"),topy,bottomy);
#endif
//
// Determine first and last item to draw
// (don't draw if already over the top)
//
if ( topy < client_bottom && topy < m_bottomy && FROM_STATE(m_properties)->GetCount() > 0 )
{
wxPGProperty* firstItem = DoGetItemAtY(topy);
if ( firstItem == (wxPGProperty*) NULL )
{
#ifdef __WXDEBUG__
wxString msg;
msg.Printf(wxT("WARNING: wxPropertyGrid::DrawItems(): firstItem == NULL!"));
wxMessageBox(msg);
wxLogDebug(msg);
wxLogDebug(wxT(" More info: y: %i -> %i visible_window: %i -> %i"),
(int)topy,(int)bottomy,(int)vy,(int)client_bottom);
// This is here for debugging purposes.
DoGetItemAtY(topy);
#endif
return;
}
wxPGProperty* lastItem = (wxPGProperty*) NULL;
// lastItem may be NULL on call to DoDrawItems
// in this case lastItem will truly become the last item
if ( bottomy > topy && bottomy < m_bottomy )
{
lastItem = DoGetItemAtY(bottomy-1);
#if __PAINT_DEBUGGING__
wxLogDebug( wxT(" \\--> WARNING: lastItem acquisition failed (should not)!"));
#endif
}
DoDrawItems( dc, firstItem, lastItem, clipRect );
}
// Clear area beyond m_bottomy?
if ( bottomy > m_bottomy )
{
wxColour& bgc = wxPG_SLACK_BACKROUND;
//wxColour& bgc = wxColour(255,0,255);
dc.SetPen ( wxPen(bgc) );
dc.SetBrush ( wxBrush(bgc) );
unsigned int clear_top = m_bottomy;
if ( topy > clear_top ) clear_top = topy;
dc.DrawRectangle ( 0, clear_top, m_width, m_height-(clear_top-vy) );
}
}
// -----------------------------------------------------------------------
#define DECLARE_ITEM_ITERATION_UVC_VARIABLES \
unsigned int ind; \
wxPGProperty* p;
// UVC = Use Visibility Cache
// VISTART = index to first item from visibility cache to use.
// BOTTOMY = Logical y coordinate of last item to draw.
#define ITEM_ITERATION_UVC_LOOP_BEGIN(VISTART,BOTTOMY) \
ind = VISTART; \
do \
{ \
p = (wxPGProperty*)m_arrVisible.Item(ind); \
ind++; \
int parenting = p->GetParentingType();
#define ITEM_ITERATION_UVC_LOOP_END(BOTTOMY) \
} while ( p->m_y < BOTTOMY ); \
void wxPropertyGrid::DoDrawItems( wxDC& dcMain,
const wxPGProperty* firstItem,
const wxPGProperty* lastItem,
const wxRect* clipRect )
{
if ( m_frozen || m_height < 1 )
return;
//wxCHECK_RET( !FROM_STATE(m_itemsAdded), wxT("m_itemsAdded must be zero at this point") );
// items added check
if ( FROM_STATE(m_itemsAdded) ) PrepareAfterItemsAdded();
wxCHECK_RET( firstItem != NULL, wxT("invalid first item") );
wxASSERT( FROM_STATE(m_properties->GetCount()) );
// Make sure visibility cache is up-to-date
int vy;
int vx;
GetViewStart(&vx,&vy);
vy*=wxPG_PIXELS_PER_UNIT;
if ( vy != m_prevVY )
CalculateVisibles(vy,true);
if ( vy != m_prevVY )
return;
// Determine last item, if not given (but requires clipRect).
if ( lastItem == NULL )
{
if ( clipRect != NULL )
{
unsigned int bottomy = clipRect->y + clipRect->height;
if ( bottomy <= (unsigned int)firstItem->m_y )
lastItem = firstItem;
}
if ( lastItem == NULL )
{
lastItem = DoGetItemAtY(vy+m_height-1);
if ( lastItem == NULL )
lastItem = GetLastItem(true);
}
}
DoDrawItems2(dcMain, firstItem, lastItem, clipRect);
}
//
// Uses three pass approach, so it is optimized for drawing
// multiple items at once.
//
// IMPORTANT NOTES:
// - Clipping rectangle must be of physical coordinates.
//
//
void wxPropertyGrid::DoDrawItems2( wxDC& dcMain,
const wxPGProperty* firstItem,
const wxPGProperty* lastItem,
const wxRect* clipRect ) const
{
int lh = m_lineHeight;
int vy;
int vx;
GetViewStart(&vx,&vy);
vy*=wxPG_PIXELS_PER_UNIT;
int firstItemTopY = firstItem->m_y;
int lastItemBottomY = lastItem->m_y+lh-1;
int yRelMod = 0;
// Entire range outside scrolled, visible area?
if ( firstItemTopY >= (vy+m_height) || lastItemBottomY <= vy )
return;
wxCHECK_RET( firstItemTopY < lastItemBottomY, wxT("invalid y values") );
wxDC* dcPtr;
#if wxPG_DOUBLE_BUFFER
wxMemoryDC* bufferDC = NULL;
const wxRect* blitClipRect = NULL;
int renderHeight = lastItem->m_y - firstItemTopY + m_lineHeight;
if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
{
if ( !m_doubleBuffer )
return;
// Must fit to double-buffer
#ifdef __WXDEBUG__
if ( (lastItemBottomY - firstItemTopY) > m_doubleBuffer->GetHeight() )
{
wxString msg;
msg.Printf( wxT("wxPropertyGrid: DOUBLE-BUFFER TOO SMALL ( drawn %i vs db height %i vs client_height %i)!"),
(int)(lastItemBottomY - firstItemTopY),
(int)(m_doubleBuffer->GetHeight()),
(int)m_height );
wxLogError(msg);
wxLogDebug(msg);
}
#endif
bufferDC = new wxMemoryDC();
bufferDC->SelectObject( *m_doubleBuffer );
dcPtr = bufferDC;
blitClipRect = clipRect;
//if ( m_iFlags & wxPG_FL_CHANGED ||
// !(m_iFlags & wxPG_FL_HANDLING_PAINT_EVENT) )
//{
}
else
#endif
{
dcPtr = &dcMain;
}
wxDC& dc = *dcPtr;
#if __PAINT_DEBUGGING__
wxLogDebug(wxT(" -> DoDrawItems ( \"%s\" -> \"%s\", height=%i (ch=%i), clipRect = 0x%lX )"),
firstItem->GetLabel().c_str(),
lastItem->GetLabel().c_str(),
(int)(lastItemBottomY - firstItemTopY),
(int)m_height,
(unsigned long)clipRect );
#endif
wxPGPaintData paintdata;
wxRect r;
DECLARE_ITEM_ITERATION_UVC_VARIABLES
// Get first and last indexes to visibility cache
unsigned int viStart = (firstItemTopY - vy) / lh;
int vi_end_y = lastItem->m_y;
if ( viStart >= m_arrVisible.GetCount() )
{
wxLogDebug(wxT("WARNING: viStart >= m_arrVisible.GetCount() ( %i >= %i )"),
(int)viStart, (int)m_arrVisible.GetCount() );
return;
}
#ifdef __WXDEBUG__
unsigned int viEnd = (lastItem->m_y - vy) / lh;
if ( viEnd >= m_arrVisible.GetCount() )
{
wxLogDebug(wxT("WARNING: viEnd >= m_arrVisible.GetCount() ( %i >= %i )"),
(int)viEnd, (int)m_arrVisible.GetCount() );
return;
}
#endif
int x = m_marginWidth;
int y;
long window_style = m_windowStyle;
int extraStyle = GetExtraStyle();
//
// With wxPG_DOUBLE_BUFFER, do double buffering
// - buffer's y = 0, so align cliprect and coordinates to that
//
#if wxPG_DOUBLE_BUFFER
if ( bufferDC )
{
wxRect cr2;
//yRelMod = firstItemTopY;
yRelMod = vy;
//
// clipRect conversion
if ( clipRect )
{
cr2 = *clipRect;
cr2.y -= yRelMod;
clipRect = &cr2;
}
//int renderHeight = lastItem->m_y - firstItemTopY + m_lineHeight;
//lastItemBottomY -= firstItemTopY;
//firstItemTopY = 0;
firstItemTopY -= vy;
lastItemBottomY -= vy;
}
#endif
const wxFont& normalfont = m_font;
bool reallyFocused = (m_iFlags & wxPG_FL_FOCUSED) ? true : false;
bool isEnabled = IsEnabled();
//
// Prepare some pens and brushes that are often changed to.
//
wxBrush marginBrush(m_colMargin);
wxPen marginPen(m_colMargin);
wxBrush capbgbrush(m_colCapBack,wxSOLID);
wxPen linepen(m_colLine,1,wxSOLID);
// pen that has same colour as text
wxPen outlinepen(m_colPropFore,1,wxSOLID);
if ( clipRect )
dc.SetClippingRegion( *clipRect );
//
// Clear margin with background colour
//
dc.SetBrush( marginBrush );
if ( !(window_style & wxPG_HIDE_MARGIN) )
{
dc.SetPen( *wxTRANSPARENT_PEN );
dc.DrawRectangle(-1,firstItemTopY-1,m_marginWidth+2,lastItemBottomY-firstItemTopY+3);
}
/*
// This colorizer helps to debug painting.
bool small_draw = false;
if ( renderHeight < (m_height-(lh*3)) )
{
if ( firstItem == lastItem )
{
bgbrush = wxBrush(wxColour(255,128,128));
linepen = wxPen(wxColour(128,0,255));
//boxbrush = wxBrush(wxColour(192,192,192));
}
else
{
bgbrush = wxBrush(wxColour(128,255,128));
linepen = wxPen(wxColour(0,0,255));
//boxbrush = wxBrush(wxColour(230,230,230));
}
small_draw = true;
}
*/
//dc.SetPen ( *wxTRANSPARENT_PEN );
//dc.SetFont(normalfont);
wxPGProperty* selected = m_selected;
/*#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
bool selected_painted = false;
#endif*/
// NOTE: Clipping and pen/brush switching are main reasons for multi-pass approach.
//
// zero'th pass: Wireframes.
// (this could be embedded in another loop)
dc.SetBrush( marginBrush );
unsigned long cur_first_ind = viStart;
unsigned long next_cur_first_ind = 0xFFFFFFFF;
wxPGPropertyWithChildren* cur_category = (wxPGPropertyWithChildren*) NULL;
int vcache_last_item_y = vy + m_height;
if ( vcache_last_item_y > (int)m_bottomy ) vcache_last_item_y = m_bottomy;
vcache_last_item_y -= lh;
ITEM_ITERATION_UVC_LOOP_BEGIN(viStart,vi_end_y)
wxPGPropertyWithChildren* parent = p->GetParent();
// Should not happen (but this is better than a crash)...
wxCHECK_RET( parent, wxT("NULL parent") );
// Does this wireframe end?
// Conditions:
// A) This is last item within its parent, and parent is category.
// B) Next is category.
// C) This is collapsed category.
// D) This is the last item drawn.
if ( p->m_y >= vi_end_y )
{
// This is the last item.
//wxLogDebug(wxT("--> last item"));
next_cur_first_ind = ind;
}
else if ( p->m_arrIndex >= (parent->GetCount()-1) && parent->GetParentingType() >= PT_CAPTION &&
( parenting <= 0 /*|| ((wxPGPropertyWithChildren*)p)->GetCount() < 1*/ )
)
{
// This is last item within its parent, and parent is category, but this isn't
// an non-empty category.
//wxLogDebug(wxT("--> category ends"));
cur_category = (wxPropertyCategoryClass*)parent;
next_cur_first_ind = ind;
}
else if ( ((wxPGProperty*)m_arrVisible.Item(ind))->GetParentingType() >= PT_CAPTION )
{
// Next item is a category.
//wxLogDebug(wxT("--> next item is category"));
next_cur_first_ind = ind;
}
else if ( parenting > 0 &&
(!((wxPGPropertyWithChildren*)p)->IsExpanded() ||
!((wxPGPropertyWithChildren*)p)->GetCount()) )
{
// This is collapsed category.
//wxLogDebug(wxT("--> collapsed category"));
cur_category = (wxPropertyCategoryClass*)p;
next_cur_first_ind = ind;
}
// When new category begins or old ends, draw wireframe for items in-between
if ( next_cur_first_ind < 0xFFFFFF )
{
wxPGProperty* cur_first = (wxPGProperty*)m_arrVisible.Item(cur_first_ind);
wxPGPropertyWithChildren* cur_last_item = (wxPGPropertyWithChildren*)p;
if ( !cur_category )
{
if ( cur_first->GetParentingType() >= PT_CAPTION )
{
cur_category = (wxPropertyCategoryClass*)cur_first;
}
else if ( !(m_windowStyle & wxPG_HIDE_CATEGORIES) )
{
cur_category = _GetPropertyCategory(cur_first);
/*if ( !cur_category )
cur_category = (wxPropertyCategoryClass*)FROM_STATE(m_properties);*/
}
}
int draw_top = cur_first->m_y - yRelMod;
int draw_bottom = cur_last_item->m_y + lh - yRelMod;
int frame_top = draw_top;
int frame_bottom = draw_bottom;
int margin_top = draw_top;
int margin_bottom = draw_bottom;
int ly = frame_top + lh - 1;
if ( cur_first->GetParentingType() >= PT_CAPTION )
{
wxPropertyCategoryClass* pc = ((wxPropertyCategoryClass*)cur_first);
frame_top += lh;
if ( !pc->IsExpanded() )
{
// Category collapsed.
frame_top = frame_bottom + 1;
}
}
int grey_x = x;
if ( cur_category /*!(window_style & wxPG_HIDE_CATEGORIES)*/ )
grey_x += ((unsigned int)((cur_category->GetDepth()-1)*m_subgroup_extramargin));
//wxLogDebug( wxT("wireframe: %s -> %s (grey_x:%i)"), cur_first->GetLabel().c_str(),
// cur_last_item->GetLabel().c_str(),((int)grey_x-x));
dc.SetPen( *wxTRANSPARENT_PEN );
// Clear extra margin area.
dc.DrawRectangle( x-1, margin_top, grey_x - x + 1, margin_bottom-margin_top );
dc.SetPen( linepen );
if ( frame_bottom > frame_top )
{
//if ( cat_top < firstItemTopY )
// cat_top = firstItemTopY;
// Margin Edge
dc.DrawLine ( grey_x, frame_top, grey_x, frame_bottom );
// Splitter
dc.DrawLine ( m_splitterx, frame_top, m_splitterx, frame_bottom );
// Horizontal Lines
while ( ly < (frame_bottom-1) )
{
dc.DrawLine ( grey_x, ly, m_width, ly );
ly += lh;
}
}
int use_depth = grey_x; // Default is to simply tidy up this wireframe.
// Properly draw top line of next wireframe, if adjacent.
// Get next item.
wxPGProperty* next_item;
//if ( ind < m_arrVisible.GetCount() )
if ( cur_last_item->m_y < vcache_last_item_y )
{
next_item = (wxPGProperty*)m_arrVisible.Item(ind);
}
else
{
// Was not in visibility cache, so use clumsier method.
next_item = GetNeighbourItem(cur_last_item,true,1);
if (!next_item)
next_item = cur_last_item; // This will serve our purpose.
}
//wxLogDebug(wxT("next_item: %s"),next_item->GetLabel().c_str());
// Just take the depth and is-it-category out of it.
int next_parenting = next_item->GetParentingType();
int last_parenting = cur_last_item->GetParentingType();
// A) If both are categories, draw line with background colour.
// B) If only next is category, use its category's depth.
// C) If only last is category, use grey_x as depth.
// D) If neither is a category, use smaller.
if ( next_parenting > 0 )
{
// Next is category.
if ( last_parenting > 0 )
{
// Last is a category too - draw complete line with background colour.
dc.SetPen ( marginPen );
use_depth = x;
}
}
else
{
// Next is not a category.
wxPropertyCategoryClass* next_cat = _GetPropertyCategory(next_item);
int depth_next = x;
if ( next_cat && /*cur_category*/ !(window_style & wxPG_HIDE_CATEGORIES) )
{
//wxLogDebug(wxT("next_item_cat: %s"),next_cat->GetLabel().c_str());
depth_next += ((unsigned int)((next_cat->GetDepth()-1)*m_subgroup_extramargin));
}
if ( last_parenting <= 0 )
{
// Last is not a category - use lesser depth
if ( depth_next < grey_x )
use_depth = depth_next;
//wxLogDebug(wxT("- neither is category"));
}
else
{
// Last is a category
use_depth = depth_next;
//wxLogDebug(wxT("last only is category"));
}
}
//wxLogDebug(wxT("last_line_use_depth: %i"),(int)use_depth);
dc.DrawLine( use_depth, ly, m_width, ly );
cur_first_ind = next_cur_first_ind;
next_cur_first_ind = 0xFFFFFFFF;
//cur_first = (wxPGPropertyWithChildren*)p;
cur_category = (wxPGPropertyWithChildren*) NULL;
}
//cur_last_item = p;
ITEM_ITERATION_UVC_LOOP_END(vi_end_y)
//
// First pass: Category background and text, Images, Label+value background.
//
//wxLogDebug(wxT(" \\--> first pass..."));
y = firstItemTopY;
dc.SetFont( m_captionFont );
dc.SetPen( *wxTRANSPARENT_PEN );
ITEM_ITERATION_UVC_LOOP_BEGIN(viStart,vi_end_y)
y += m_spacingy;
int text_x = x + ((unsigned int)((p->GetDepth()-1)*m_subgroup_extramargin));
if ( parenting > 0 )
{
dc.SetBrush( capbgbrush ); // Category label background colour.
// Category - draw background, text and possibly selection rectangle.
wxPropertyCategoryClass* pc = (wxPropertyCategoryClass*)p;
// Note how next separator line is overdrawn if next item is category .
int useLh = lh;
if ( ind < (m_arrVisible.GetCount()) &&
( ((wxPGProperty*)m_arrVisible[ind])->GetParentingType() <= 0 ) )
useLh -= 1;
if ( isEnabled && p->IsEnabled() )
dc.SetTextForeground( *(wxPGColour*)m_arrFgCols[pc->GetTextColIndex()] );
else
dc.SetTextForeground( m_colDisPropFore );
dc.DrawRectangle( text_x, y-m_spacingy, m_width-text_x, useLh );
dc.DrawText( pc->GetLabel(), text_x+wxPG_XBEFORETEXT, y );
// active caption gets nice dotted rectangle
if ( p == selected )
{
wxRect focusRect(text_x+wxPG_XBEFORETEXT-wxPG_CAPRECTXMARGIN,
y-wxPG_CAPRECTYMARGIN,
pc->GetTextExtent()+(wxPG_CAPRECTXMARGIN*2),
m_fontHeight+(wxPG_CAPRECTYMARGIN*2));
wxPGDrawFocusRect(dc,focusRect);
dc.SetPen( *wxTRANSPARENT_PEN );
}
}
else
{
// Basic background colour.
dc.SetBrush( *(wxPGBrush*)m_arrBgBrushes[p->m_bgColIndex] );
//wxLogDebug(wxT("%s: %i"),p->m_label.c_str(),(int)p->m_depthBgCol);
int greyDepth = 0;
if ( !(window_style & wxPG_HIDE_CATEGORIES) )
greyDepth = (((int)p->m_depthBgCol)-1) * m_subgroup_extramargin;
// In two parts to retain splitter
if ( p == m_selected )
{
// Selected get different label background.
if ( reallyFocused )
dc.SetBrush( m_colSelBack );
else
dc.SetBrush( m_colLine );
dc.DrawRectangle( x+greyDepth+1, y-m_spacingy, m_splitterx-greyDepth-x-1, lh-1 );
}
else
{
dc.DrawRectangle( x+greyDepth+1, y-m_spacingy, m_splitterx-greyDepth-x-1, lh-1 );
}
dc.DrawRectangle( m_splitterx+1, y-m_spacingy, m_width-m_splitterx, lh-1 );
}
y += m_fontHeight+m_spacingy+1;
ITEM_ITERATION_UVC_LOOP_END(vi_end_y)
dc.SetFont( normalfont );
//
// Second pass: Expander Buttons, Labels.
//
// Second pass happens entirely on the left side, so sometimes
// we can just skip it.
if ( clipRect == NULL || clipRect->x < m_splitterx )
{
//wxLogDebug(wxT(" \\--> second pass..."));
y = firstItemTopY;
r = wxRect(0,y,m_splitterx,lastItemBottomY);
dc.SetClippingRegion ( r );
dc.SetFont(normalfont);
ITEM_ITERATION_UVC_LOOP_BEGIN(viStart,vi_end_y)
if ( isEnabled && (p->IsEnabled() || !(extraStyle & wxPG_EX_GREY_LABEL_WHEN_DISABLED)) )
dc.SetTextForeground( *(wxPGColour*)m_arrFgCols[p->m_fgColIndex] );
else
dc.SetTextForeground( m_colDisPropFore );
//
// Expand/collapse button image.
if ( parenting != 0 &&
!(window_style & wxPG_HIDE_MARGIN) &&
((wxPGPropertyWithChildren*)p)->GetChildCount() )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
int depth = p->m_depth - 1;
#ifdef wxPG_ICON_WIDTH
int imageX = m_gutterWidth + ( depth * m_subgroup_extramargin );
#endif
y += m_buttonSpacingY;
#if (wxPG_USE_RENDERER_NATIVE)
// Prepare rectangle to be used
r.x = imageX; r.y = y;
r.width = m_iconWidth; r.height = m_iconHeight;
#elif wxPG_ICON_WIDTH
// Drawing expand/collapse button manually
dc.SetPen(m_colPropFore);
if ( parenting > 0 )
{
dc.SetBrush(*wxTRANSPARENT_BRUSH);
}
else
{
dc.SetBrush(m_colPropBack);
}
dc.DrawRectangle( imageX, y, m_iconWidth, m_iconWidth );
int _y = y+(m_iconWidth/2);
dc.DrawLine(imageX+2,_y,imageX+m_iconWidth-2,_y);
#else
wxBitmap* bmp;
#endif
if ( pwc->m_expanded )
{
// wxRenderer functions are non-mutating in nature, so it
// should be safe to cast "const wxPropertyGrid*" to "wxWindow*".
// Hopefully this does not cause problems.
#if (wxPG_USE_RENDERER_NATIVE)
wxRendererNative::Get().DrawTreeItemButton(
(wxWindow*)this,
dc,
r,
wxCONTROL_EXPANDED
);
#elif wxPG_ICON_WIDTH
//
#else
bmp = m_collbmp;
#endif
}
else
{
#if (wxPG_USE_RENDERER_NATIVE)
wxRendererNative::Get().DrawTreeItemButton(
(wxWindow*)this,
dc,
r,
0
);
#elif wxPG_ICON_WIDTH
int _x = imageX+(m_iconWidth/2);
dc.DrawLine(_x,y+2,_x,y+m_iconWidth-2);
#else
bmp = m_expandbmp;
#endif
}
#if (wxPG_USE_RENDERER_NATIVE)
//
#elif wxPG_ICON_WIDTH
//
#else
dc.DrawBitmap( *bmp, m_gutterWidth, y, true );
#endif
y -= m_buttonSpacingY;
}
y += m_spacingy;
if ( parenting <= 0 )
{
// Non-categories.
int text_x = x;
// Use basic depth if in non-categoric mode and parent is base array.
if ( !(window_style & wxPG_HIDE_CATEGORIES) || p->GetParent() != FROM_STATE(m_properties) )
{
text_x += ((unsigned int)((p->m_depth-1)*m_subgroup_extramargin));
}
/*
else
{
wxLogDebug( wxT("%s"), p->GetLabel().c_str() );
text_x = x;
}
*/
if ( p != selected )
{
dc.DrawText( p->m_label, text_x+wxPG_XBEFORETEXT, y );
}
else
{
// Selected gets different colour.
if ( reallyFocused )
dc.SetTextForeground( m_colSelFore );
dc.DrawText( p->m_label, text_x+wxPG_XBEFORETEXT, y );
}
}
else
{
/*// switch background colour
bgbrush.SetColour ( ((wxPropertyCategoryClass*)p)->m_colCellBg );
dc.SetBrush ( bgbrush );*/
}
y += m_fontHeight+m_spacingy+1;
ITEM_ITERATION_UVC_LOOP_END(vi_end_y)
}
//
// Third pass: Values
//
dc.DestroyClippingRegion();
if ( clipRect )
{
// third pass happens entirely on the right side, so sometimes
// we can just skip it
if ( (clipRect->x + clipRect->width) < m_splitterx )
x = -1;
dc.SetClippingRegion ( *clipRect );
}
// This used with value drawer method.
wxRect valueRect(0,0,
m_width-(m_splitterx+wxPG_CONTROL_MARGIN),
m_fontHeight);
wxSize imageSize;
if ( x != -1 )
{
r.x = m_splitterx+1+wxPG_CONTROL_MARGIN;
r.width = m_width-m_splitterx-wxPG_CONTROL_MARGIN;
//r.x = m_splitterx+wxPG_DIST_SPLITTER_TO_IMAGE;
//r.width = m_width-m_splitterx-wxPG_DIST_SPLITTER_TO_IMAGE-1;
r.height = lh-1;
/*#if wxCC_CORRECT_CONTROL_POSITION
const int vy2 = vy;
#endif*/
//wxLogDebug(wxT(" \\--> third pass..."));
// Altough this line may seem unnecessary, it isn't
dc.SetFont(normalfont);
dc.SetPen( *wxTRANSPARENT_PEN );
// Prepare paintdata.
paintdata.m_parent = this;
paintdata.m_choiceItem = -1; // Not drawing list item at this time.
y = firstItemTopY;
ITEM_ITERATION_UVC_LOOP_BEGIN(viStart,vi_end_y)
if ( parenting <= 0 )
{
r.y = y;
y += m_spacingy;
// background
dc.SetBrush( *(wxPGBrush*)m_arrBgBrushes[p->m_bgColIndex] );
if ( isEnabled && p->IsEnabled() )
dc.SetTextForeground( *(wxPGColour*)m_arrFgCols[p->m_fgColIndex] );
else
dc.SetTextForeground( m_colDisPropFore );
// draw value string only if editor widget not open
// (exception: no primary editor widget or it is hidden)
if ( p != selected || !m_wndPrimary
// "if not primary shown" is required because
// primary is not usually shown during splitter
// movement.
|| m_dragStatus > 0
)
{
valueRect.x = m_splitterx+wxPG_CONTROL_MARGIN;
valueRect.y = y;
// Draw background
if ( p != selected )
{
dc.DrawRectangle( r );
}
else
{
if ( m_wndPrimary )
dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
else
dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
dc.DrawRectangle( r );
}
// Set bold font?
if ( p->m_flags & wxPG_PROP_MODIFIED && (window_style & wxPG_BOLD_MODIFIED) )
dc.SetFont( m_captionFont );
const wxPGEditor* editor = p->GetEditorClass();
bool fullPaint = false;
if ( p->m_flags & wxPG_PROP_CUSTOMIMAGE )
{
imageSize = p->GetImageSize();
wxRect imageRect(r.x + wxPG_CONTROL_MARGIN + wxCC_CUSTOM_IMAGE_MARGIN1,
r.y+wxPG_CUSTOM_IMAGE_SPACINGY,
wxPG_CUSTOM_IMAGE_WIDTH,
r.height-(wxPG_CUSTOM_IMAGE_SPACINGY*2));
if ( imageSize.x == wxPG_FULL_CUSTOM_PAINT_WIDTH )
{
fullPaint = true;
imageRect.width = m_width - imageRect.x;
}
dc.SetPen( outlinepen );
paintdata.m_drawnWidth = imageRect.width;
if ( !(p->m_flags & wxPG_PROP_UNSPECIFIED) )
{
p->OnCustomPaint( dc, imageRect, paintdata );
}
else
{
dc.SetBrush(*wxWHITE_BRUSH);
dc.DrawRectangle(imageRect);
}
dc.SetPen( *wxTRANSPARENT_PEN );
}
else
paintdata.m_drawnWidth = 0;
if ( paintdata.m_drawnWidth > 0 )
valueRect.x += paintdata.m_drawnWidth + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2;
else
fullPaint = false;
if ( !fullPaint )
editor->DrawValue(dc,p,valueRect);
// Return original font?
if ( p->m_flags & wxPG_PROP_MODIFIED && (window_style & wxPG_BOLD_MODIFIED) )
dc.SetFont(normalfont);
}
else
{
if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) ||
m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE )
{
//wxLogDebug(wxT("Primary doesn't fill entire"));
dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
dc.DrawRectangle( r );
}
if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE )
{
wxRect imagerect(r.x + wxPG_CONTROL_MARGIN + wxCC_CUSTOM_IMAGE_MARGIN1,
r.y+wxPG_CUSTOM_IMAGE_SPACINGY,wxPG_CUSTOM_IMAGE_WIDTH,
r.height-(wxPG_CUSTOM_IMAGE_SPACINGY*2));
dc.SetPen ( outlinepen );
if ( !(p->m_flags & wxPG_PROP_UNSPECIFIED) )
{
p->OnCustomPaint( dc, imagerect, paintdata );
}
else
{
dc.SetBrush(*wxWHITE_BRUSH);
dc.DrawRectangle(imagerect);
}
}
dc.SetPen( *wxTRANSPARENT_PEN );
}
y += m_fontHeight+m_spacingy + 1;
}
else
{
// caption item
y += lh;
}
//if ( y > lastItemBottomY ) { parent = NULL; break; }
ITEM_ITERATION_UVC_LOOP_END(vi_end_y)
}
dc.DestroyClippingRegion(); // Is this really necessary?
#if wxPG_DOUBLE_BUFFER
//}
//else wxLogDebug(wxT("Used Cache"));
if ( bufferDC )
{
if ( blitClipRect )
dcMain.SetClippingRegion( *blitClipRect );
//wxLogDebug(wxT(" \\--> (0,%i)"),(int)final_y);
dcMain.Blit ( 0, firstItem->m_y, m_width, renderHeight,
&dc, 0, firstItem->m_y-vy, wxCOPY );
//dcMain.Blit ( 0, 0, m_width, m_height,
// &dc, 0, 0, wxCOPY );
dcMain.DestroyClippingRegion(); // Is this really necessary?
delete bufferDC;
}
#endif
#if __PAINT_DEBUGGING__
wxLogDebug(wxT(" \\--> ends..."));
#endif
}
// -----------------------------------------------------------------------
wxRect wxPropertyGrid::GetPropertyRect( const wxPGProperty* p1, const wxPGProperty* p2 ) const
{
wxRect r;
if ( m_width < 10 || m_height < 10 ||
!FROM_STATE(m_properties)->GetCount() ||
p1 == (wxPGProperty*) NULL )
return wxRect(0,0,0,0);
int vx,vy;
GetViewStart(&vx,&vy);
vy*=wxPG_PIXELS_PER_UNIT;
//
// Return rect which encloses the given property range
int visTop = p1->m_y;
int visBottom = m_bottomy;
if ( p2 )
visBottom = p2->m_y + m_lineHeight;
// If seleced property is inside the range, we'll extend the range to include
// control's size.
wxPGProperty* selected = m_selected;
if ( selected && selected->m_y >= visTop && selected->m_y < visBottom )
{
wxWindow* editor = GetEditorControl();
if ( editor )
{
int visBottom2 = selected->m_y + editor->GetSize().y;
if ( visBottom2 > visBottom )
visBottom = visBottom2;
}
}
return wxRect(0,visTop-vy,m_width,visBottom-visTop);
}
// -----------------------------------------------------------------------
void wxPropertyGrid::DrawItems( const wxPGProperty* p1, const wxPGProperty* p2 )
{
if ( m_frozen )
return;
if ( FROM_STATE(m_itemsAdded) )
PrepareAfterItemsAdded();
wxRect r = GetPropertyRect(p1, p2);
if ( r.width > 0 )
RefreshRect(r);
}
// -----------------------------------------------------------------------
// In addition to calling DoDrawItems directly, this is the
// only alternative for using wxClientDC - others just call
// RefreshRect.
void wxPropertyGrid::DrawItem( wxDC& dc, wxPGProperty* p )
{
wxCHECK_RET( p, wxT("invalid property id") );
// do not draw a single item if multiple pending
if ( FROM_STATE(m_itemsAdded) )
return;
if ( p->m_y < 0 )
return;
#if __PAINT_DEBUGGING__
wxLogDebug(wxT("wxPropertyGrid::DrawItem( %s )"), p->GetLabel().c_str() );
#endif
DoDrawItems( dc, p, p, NULL );
}
// -----------------------------------------------------------------------
void wxPropertyGrid::RefreshProperty( wxPGProperty* p )
{
if ( p == m_selected )
DoSelectProperty(p, wxPG_SEL_FORCE);
DrawItemAndChildren(p);
}
// -----------------------------------------------------------------------
void wxPropertyGrid::DrawItemAndValueRelated( wxPGProperty* p )
{
if ( m_frozen )
return;
// Draw item, children, and parent too, if it is not category
wxPGProperty* parent = p->GetParent();
while ( parent &&
parent->GetParentingType() < PT_NONE )
{
DrawItem(parent);
parent = parent->GetParent();
}
DrawItemAndChildren(p);
}
void wxPropertyGrid::DrawItemAndChildren( wxPGProperty* p )
{
wxCHECK_RET( p, wxT("invalid property id") );
// Do not draw if in non-visible page
if ( p->GetParentState() != m_pState )
return;
// do not draw a single item if multiple pending
if ( FROM_STATE(m_itemsAdded) || p->m_y < 0 || m_frozen )
return;
#if __PAINT_DEBUGGING__
wxLogDebug(wxT("wxPropertyGrid::DrawItemAndChildren( %s )"), p->GetLabel().c_str() );
#endif
// Update child control.
if ( m_selected && m_selected->GetParent() == p )
m_selected->UpdateControl(m_wndPrimary);
const wxPGProperty* lastDrawn = p->GetLastVisibleSubItem();
DrawItems(p, lastDrawn);
}
// -----------------------------------------------------------------------
void wxPropertyGrid::Refresh( bool WXUNUSED(eraseBackground),
const wxRect *rect )
{
// Refresh implies forced redraw
//m_iFlags |= wxPG_FL_CHANGED;
wxWindow::Refresh(false,rect);
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
// I think this really helps only GTK+1.2
if ( m_wndPrimary ) m_wndPrimary->Refresh();
if ( m_wndSecondary ) m_wndSecondary->Refresh();
#endif
}
// -----------------------------------------------------------------------
/*
void wxPropertyGrid::RedrawAllVisible ()
{
// TODO: Is this safe?
//Update();
if ( m_frozen || !IsShown() )
return;
wxPG_CLIENT_DC_INIT()
#if __PAINT_DEBUGGING__
wxLogDebug( wxT("wxPropertyGrid::RedrawAllVisible()") );
#endif
int vx,vy; // Top left corner of client
GetViewStart(&vx,&vy);
vy *= wxPG_PIXELS_PER_UNIT;
int y1 = vy;
int y2 = y1 + m_height;
// Repaint this rectangle
DrawItems ( dc, y1, y2, (wxRect*) NULL );
}
*/
// -----------------------------------------------------------------------
#if wxPG_HEAVY_GFX
void wxPropertyGrid::DrawSplitterDragColumn( wxDC&, int ) { }
#else
void wxPropertyGrid::DrawSplitterDragColumn( wxDC& dc, int x )
{
int vx, vy;
GetViewStart(&vx,&vy);
vy *= wxPG_PIXELS_PER_UNIT;
dc.SetLogicalFunction(wxINVERT);
dc.DestroyClippingRegion();
dc.SetPen( m_splitterpen );
dc.DrawLine(x,vy,x,vy+m_height);
}
#endif
// -----------------------------------------------------------------------
// wxPropertyGrid global operations
// -----------------------------------------------------------------------
void wxPropertyGrid::Clear()
{
if ( m_selected )
{
bool selRes = DoSelectProperty(wxPGIdGen(NULL), wxPG_SEL_DELETING); // This must be before state clear
wxPG_CHECK_RET_DBG( selRes,
wxT("failed to deselect a property (editor probably had invalid value)") );
}
FROM_STATE(Clear());
m_propHover = NULL;
m_bottomy = 0;
m_prevVY = 0;
m_arrVisible.Empty();
RecalculateVirtualSize();
// Need to clear some area at the end
if ( !m_frozen )
RefreshRect(wxRect(0, 0, m_width, m_height));
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::EnableCategories( bool enable )
{
if ( !ClearSelection() )
return false;
if ( enable )
{
//
// Enable categories
//
m_windowStyle &= ~(wxPG_HIDE_CATEGORIES);
}
else
{
//
// Disable categories
//
m_windowStyle |= wxPG_HIDE_CATEGORIES;
}
if ( !m_pState->EnableCategories(enable) )
return false;
if ( !m_frozen )
{
if ( m_windowStyle & wxPG_AUTO_SORT )
{
FROM_STATE(m_itemsAdded) = 1; // force
PrepareAfterItemsAdded();
}
else
{
CalculateYs(NULL,-1);
//CalculateVisibles( -1 );
}
}
else
FROM_STATE(m_itemsAdded) = 1;
Refresh();
return true;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SwitchState( wxPropertyGridState* pNewState )
{
wxASSERT( pNewState );
wxPGProperty* oldSelection = m_selected;
// Deselect
if ( m_selected )
{
bool selRes = ClearSelection();
wxPG_CHECK_RET_DBG( selRes,
wxT("failed to deselect a property (editor probably had invalid value)") );
}
m_pState->m_selected = oldSelection;
bool orig_mode = m_pState->IsInNonCatMode();
bool new_state_mode = pNewState->IsInNonCatMode();
m_pState = pNewState;
m_bottomy = 0; // This is necessary or y's won't get updated.
m_propHover = (wxPGProperty*) NULL;
// If necessary, convert state to correct mode.
if ( orig_mode != new_state_mode )
{
// This should refresh as well.
EnableCategories ( orig_mode?false:true );
}
else if ( !m_frozen )
{
// Refresh, if not frozen.
if ( FROM_STATE(m_itemsAdded) )
PrepareAfterItemsAdded();
else
CalculateYs(NULL,-1);
// Reselect
if ( FROM_STATE(m_selected) )
DoSelectProperty( FROM_STATE(m_selected) );
//RedrawAllVisible();
Refresh();
}
else
m_pState->m_itemsAdded = 1;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::Sort( wxPGId id )
{
wxPG_PROP_ID_CALL_PROLOG()
m_pState->Sort( p );
// Because order changed, Y's need to be changed as well
if ( p->GetParentState() == m_pState )
CalculateYs ( p->m_parent, p->m_arrIndex );
}
// -----------------------------------------------------------------------
void wxPropertyGrid::Sort()
{
bool selRes = ClearSelection(); // This must be before state clear
wxPG_CHECK_RET_DBG( selRes,
wxT("failed to deselect a property (editor probably had invalid value)") );
m_pState->Sort();
CalculateYs( NULL, -1 );
}
// -----------------------------------------------------------------------
// Call to SetSplitterPosition will always disable splitter auto-centering
// if parent window is shown.
void wxPropertyGrid::DoSetSplitterPosition( int newxpos, bool refresh )
{
if ( ( newxpos < wxPG_DRAG_MARGIN ) )
return;
// ( m_width > wxPG_DRAG_MARGIN && newxpos > (m_width-wxPG_DRAG_MARGIN) )
#if __INTENSE_DEBUGGING__
wxLogDebug( wxT("wxPropertyGrid::DoSetSplitterPosition ( %i )"), newxpos );
#endif
#if wxPG_HEAVY_GFX
m_splitterx = newxpos;
m_fSplitterX = (float) newxpos;
if ( refresh )
{
if ( m_selected )
CorrectEditorWidgetSizeX( m_splitterx, m_width );
Refresh();
//RedrawAllVisible(); // no flicker
}
#else
if ( !m_dragStatus )
{
// Only do this if this was not a call from HandleMouseUp
m_startingSplitterX = m_splitterx;
m_splitterx = newxpos;
m_fSplitterX = (float) newxpos;
}
// Clear old
if ( m_splitterprevdrawnx != -1 )
{
wxPG_CLIENT_DC_INIT()
DrawSplitterDragColumn( dc, m_splitterprevdrawnx );
m_splitterprevdrawnx = -1;
}
// Redraw only if drag really moved
if ( m_splitterx != m_startingSplitterX && refresh )
{
if ( m_selected)
CorrectEditorWidgetSizeX( m_splitterx, m_width );
Update(); // This fixes a graphics-mess in wxMSW
Refresh();
//RedrawAllVisible(); // no flicker
}
#endif
// Don't allow initial splitter auto-positioning after this.
m_iFlags |= wxPG_FL_SPLITTER_PRE_SET;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::CenterSplitter( bool enable_auto_centering )
{
SetSplitterPosition ( m_width/2, true );
if ( enable_auto_centering && ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER ) )
m_iFlags &= ~(wxPG_FL_DONT_CENTER_SPLITTER);
}
// -----------------------------------------------------------------------
// Moves splitter so that all labels are visible, but just.
void wxPropertyGrid::SetSplitterLeft( bool subProps )
{
wxClientDC dc(this);
dc.SetFont(m_font);
int maxW = m_pState->GetLeftSplitterPos(dc, m_pState->m_properties, subProps );
if ( maxW > 0 )
SetSplitterPosition( maxW );
m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
}
// -----------------------------------------------------------------------
// wxPropertyGrid item iteration (GetNextProperty etc.) methods
// -----------------------------------------------------------------------
// Returns nearest paint visible property (such that will be painted unless
// window is scrolled or resized). If given property is paint visible, then
// it itself will be returned
wxPGProperty* wxPropertyGrid::GetNearestPaintVisible( wxPGProperty* p )
{
int vx,vy1;// Top left corner of client
GetViewStart(&vx,&vy1);
vy1 *= wxPG_PIXELS_PER_UNIT;
int vy2 = vy1 + m_height;
if ( (p->m_y + m_lineHeight) < vy1 )
{
// Too high
return DoGetItemAtY( vy1 );
}
else if ( p->m_y > vy2 )
{
// Too low
return DoGetItemAtY( vy2 );
}
// Itself paint visible
return p;
}
// -----------------------------------------------------------------------
wxPGProperty* wxPropertyGrid::GetNeighbourItem( wxPGProperty* item,
bool need_visible,
int dir ) const
{
wxPGPropertyWithChildren* parent = item->m_parent;
unsigned int indinparent = item->GetIndexInParent();
if ( dir > 0 )
{
if ( item->GetChildCount() == 0 ||
(!((wxPGPropertyWithChildren*)item)->m_expanded && need_visible) )
{
// current item did not have any expanded children
if ( indinparent < (parent->GetCount()-1) )
{
// take next in parent's array
item = parent->Item(indinparent+1);
}
else
{
// no more in parent's array; move up until found;
wxPGPropertyWithChildren* p2 = parent;
parent = parent->m_parent;
item = (wxPGProperty*) NULL;
while ( parent )
{
if ( p2->m_arrIndex < (parent->GetCount()-1) )
{
item = parent->Item(p2->m_arrIndex+1);
break;
}
p2 = parent;
parent = parent->m_parent;
}
}
}
else
{
// take first of current item's children
wxPGPropertyWithChildren* p2 = (wxPGPropertyWithChildren*)item;
item = p2->Item(0);
//indinparent = 0;
}
}
else
{
// items in array left?
if ( indinparent > 0 )
{
// take prev in parent's array
item = parent->Item(indinparent-1);
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)item;
// Recurse to it's last child
while ( item->GetParentingType() != 0 && pwc->GetCount() &&
( pwc->m_expanded || !need_visible )
)
{
item = pwc->Last();
pwc = (wxPGPropertyWithChildren*)item;
}
}
else
{
// If we were at first, go to parent
item = parent;
}
}
if ( item == FROM_STATE(m_properties) )
return (wxPGProperty*) NULL;
// If item was hidden and need_visible, get next.
if ( (m_iFlags & wxPG_FL_HIDE_STATE) && need_visible && item )
{
if ( item->m_flags & wxPG_PROP_HIDEABLE )
{
// Speed-up: If parent is hidden as well, then skip to last child or to itself
if ( parent->m_flags & wxPG_PROP_HIDEABLE )
{
item = parent; // if dir up
if ( dir > 0 )
item = parent->Last(); // if dir down
}
return GetNeighbourItem ( item, need_visible, dir );
}
}
return item;
}
// -----------------------------------------------------------------------
// This is used in DoDrawItems.
wxPGProperty* wxPropertyGrid::GetLastItem ( bool need_visible, bool allowSubprops )
{
if ( FROM_STATE(m_properties)->GetCount() < 1 )
return (wxPGProperty*) NULL;
wxPGProperty* p = FROM_STATE(m_properties)->Last();
int parenting = p->GetParentingType();
while ( parenting != 0 && ( allowSubprops || parenting >= PT_CAPTION ) )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
parenting = 0;
if ( pwc->GetCount() )
{
if ( (!need_visible || pwc->m_expanded) )
{
p = pwc->Last();
parenting = p->GetParentingType();
}
else
parenting = 0;
}
}
// If item was hidden and need_visible, get previous.
if ( (m_iFlags & wxPG_FL_HIDE_STATE) &&
need_visible &&
p && ( p->m_flags & wxPG_PROP_HIDEABLE )
)
return GetNeighbourItem( p, need_visible, -1 );
return p;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetButtonShortcut( int keycode, bool ctrlDown, bool altDown )
{
if ( keycode )
{
m_pushButKeyCode = keycode;
m_pushButKeyCodeNeedsCtrl = ctrlDown ? 1 : 0;
m_pushButKeyCodeNeedsAlt = altDown ? 1 : 0;
}
else
{
m_pushButKeyCode = WXK_DOWN;
m_pushButKeyCodeNeedsCtrl = 0;
m_pushButKeyCodeNeedsAlt = 1;
}
}
// -----------------------------------------------------------------------
// Methods related to change in value, value modification and sending events
// -----------------------------------------------------------------------
// commits any changes in editor of selected property
// return true if validation did not fail
// flags are same as with DoSelectProperty
bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags )
{
if ( m_wndPrimary && IsEditorsValueModified() &&
(m_iFlags & wxPG_FL_INITIALIZED) )
{
wxCHECK_MSG( m_selected, false, wxT("no selection") );
bool wasUnspecified = m_selected->IsValueUnspecified();
// JACS - necessary to avoid new focus being found spuriously within OnIdle
// due to another window getting focus
wxWindow* oldFocus = m_curFocused;
if ( !(flags & (wxPG_SEL_NOVALIDATE|wxPG_SEL_FORCE)) &&
!DoEditorValidate() )
{
if (oldFocus)
{
oldFocus->SetFocus();
m_curFocused = oldFocus;
}
return false;
}
// Save value (only if truly modified).
if ( !m_selected->GetEditorClass()->CopyValueFromControl( m_selected, m_wndPrimary ) )
EditorsValueWasNotModified();
if ( m_selected->IsValueUnspecified() && !wasUnspecified && UsesAutoUnspecified() )
flags |= wxPG_SEL_SETUNSPEC;
DoPropertyChanged( m_selected, flags );
return true;
}
return true;
}
// -----------------------------------------------------------------------
// flags are same as with DoSelectProperty
void wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags )
{
if ( m_processingEvent )
return;
#if __INTENSE_DEBUGGING__
wxLogDebug(wxT("wxPropertyGrid::DoPropertyChanged( %s )"),p->GetLabel().c_str());
#endif
m_pState->m_anyModified = 1;
m_processingEvent = 1;
// No longer unspecified (but not if the value was set to unspecified by
// user modification)
if ( !(selFlags & wxPG_SEL_SETUNSPEC) )
CLEAR_PROPERTY_UNSPECIFIED_FLAG(p);
if ( m_iFlags & wxPG_FL_VALUE_MODIFIED )
{
m_iFlags &= ~(wxPG_FL_VALUE_MODIFIED);
// Set as Modified (not if dragging just began)
if ( !(p->m_flags & wxPG_PROP_MODIFIED) )
{
p->m_flags |= wxPG_PROP_MODIFIED;
if ( p == m_selected && (m_windowStyle & wxPG_BOLD_MODIFIED) )
{
if ( m_wndPrimary )
SetCurControlBoldFont();
}
}
wxPGProperty* curChild = p;
wxPGPropertyWithChildren* curParent = p->m_parent;
// Also update parent(s), if any
// (but not if its wxCustomProperty)
while ( curParent &&
curParent->GetParentingType() < 0 /*&&
wxStrcmp(curParent->GetClassName(),wxT("wxCustomProperty")) != 0*/ )
{
// Set as Modified
if ( !(curParent->m_flags & wxPG_PROP_MODIFIED) )
{
curParent->m_flags |= wxPG_PROP_MODIFIED;
if ( curParent == m_selected && (m_windowStyle & wxPG_BOLD_MODIFIED) )
{
if ( m_wndPrimary )
SetCurControlBoldFont();
}
}
curParent->ChildChanged( curChild );
DrawItem( curParent );
curChild = curParent;
curParent = curParent->GetParent();
}
// Draw the actual property
if ( ( p != m_selected ) || !m_wndPrimary ||
( p->GetParentingType() < 0 ) ||
( p->m_flags & wxPG_PROP_CUSTOMIMAGE ) )
{
DrawItemAndChildren( p );
}
if ( curChild != p && !(selFlags & wxPG_SEL_SETUNSPEC) )
//m_pState->ClearPropertyAndChildrenFlags(curChild,wxPG_PROP_UNSPECIFIED);
CLEAR_PROPERTY_UNSPECIFIED_FLAG(curChild);
wxPGProperty* changedProperty;
// Call wx event handler for property (or its topmost parent, but only
// when dealing with legitemate sub-properties - see above).
if ( curChild->GetParentingType() != PT_CUSTOMPROPERTY )
changedProperty = curChild;
else
changedProperty = p;
// Maybe need to update control
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
if ( m_wndPrimary ) m_wndPrimary->Refresh();
if ( m_wndSecondary ) m_wndSecondary->Refresh();
#endif
SendEvent( wxEVT_PG_CHANGED, changedProperty, selFlags );
}
m_processingEvent = 0;
}
// -----------------------------------------------------------------------
// Runs wxValidator for the selected property
bool wxPropertyGrid::DoEditorValidate()
{
#if wxUSE_VALIDATORS
if ( m_iFlags & wxPG_FL_VALIDATION_FAILED )
{
return false;
}
wxWindow* wnd = GetEditorControl();
wxValidator* validator = m_selected->GetValidator();
if ( validator && wnd )
{
// Use TextCtrl of ODComboBox instead
if ( wnd->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
{
wnd = ((wxPGOwnerDrawnComboBox*)wnd)->GetTextCtrl();
if ( !wnd )
return true;
}
validator->SetWindow(wnd);
// Instead setting the flag after the failure, we set
// it before checking and then clear afterwards if things
// went fine. This trick is necessary since focus events
// may be triggered while in Validate.
m_iFlags |= wxPG_FL_VALIDATION_FAILED;
if ( !validator->Validate(this) )
{
// If you dpm't want to display message multiple times per change,
// comment the following line.
m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
return false;
}
m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
}
#endif
return true;
}
// -----------------------------------------------------------------------
#if wxUSE_VALIDATORS
bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* propGrid,
wxValidator* validator,
const wxString& value )
{
if ( !validator )
return true;
wxTextCtrl* tc = m_textCtrl;
if ( !tc )
{
{
tc = new wxTextCtrl( propGrid, wxPG_SUBID_TEMP1, wxEmptyString,
wxPoint(30000,30000));
tc->Hide();
}
m_textCtrl = tc;
}
//wxString oldValue = tc->GetValue();
tc->SetValue(value);
validator->SetWindow(tc);
bool res = validator->Validate(propGrid);
//tc->SetValue(oldValue);
return res;
}
#else
bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* WXUNUSED(propGrid),
wxValidator* WXUNUSED(validator),
const wxString& WXUNUSED(value) )
{
return true;
}
#endif
// -----------------------------------------------------------------------
// NB: It may really not be wxCommandEvent - must check if necessary
// (usually not).
void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event )
{
wxPGProperty* selected = m_selected;
//
// Somehow, event is handled after property has been deselected.
// Possibly, but very rare.
if ( !selected )
return;
bool wasUnspecified = selected->IsValueUnspecified();
bool usesAutoUnspecified = UsesAutoUnspecified();
wxWindow* wnd = m_wndPrimary;
bool res1, res2;
m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
// First call editor class' event handler.
const wxPGEditor* editor = selected->GetEditorClass();
res1 = editor->OnEvent( this, selected, wnd, event );
if ( res1 )
{
// If changes, validate them
if ( DoEditorValidate() )
{
if ( editor->CopyValueFromControl( selected, wnd ) )
{
}
else
{
// False alarm
res1 = false;
EditorsValueWasNotModified();
// However, even moot editing will clear the unspecified status
if ( wasUnspecified || !usesAutoUnspecified )
CLEAR_PROPERTY_UNSPECIFIED_FLAG(selected);
}
}
else
{
res1 = false;
EditorsValueWasNotModified();
if ( wasUnspecified || !usesAutoUnspecified )
CLEAR_PROPERTY_UNSPECIFIED_FLAG(selected);
return;
}
}
// Then the property's custom handler (must be always called).
res2 = selected->OnEvent( this, wnd, event );
if ( res1 || res2 )
{
// Setting this is not required if res was true, so we do it now.
m_iFlags |= wxPG_FL_VALUE_MODIFIED;
int selFlags = ( !wasUnspecified && selected->IsValueUnspecified() && usesAutoUnspecified ) ? wxPG_SEL_SETUNSPEC : 0;
DoPropertyChanged(selected, selFlags);
}
else
// Let unhandled button click events go to the parent
if ( event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
{
wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,GetId());
GetEventHandler()->AddPendingEvent(evt);
}
}
// -----------------------------------------------------------------------
// When a property's value was modified internally (using SetValueFromString
// or SetValueFromInt, for example), then this should be called afterwards.
// NB: Avoid using this method, if possible.
void wxPropertyGrid::PropertyWasModified( wxPGProperty* p, int selFlags )
{
wxCHECK_RET( p, wxT("invalid property id") );
EditorsValueWasModified();
DoPropertyChanged(p, selFlags);
}
// -----------------------------------------------------------------------
// wxPropertyGrid editor control helper methods
// -----------------------------------------------------------------------
wxWindow* wxPropertyGrid::GetEditorControl() const
{
wxWindow* ctrl = m_wndPrimary;
if ( !ctrl )
return ctrl;
// If it's clipper window, return its child instead
#if wxPG_ENABLE_CLIPPER_WINDOW
if ( ctrl->IsKindOf(CLASSINFO(wxPGClipperWindow)) )
{
return ((wxPGClipperWindow*)ctrl)->GetControl();
}
#endif
return ctrl;
}
// -----------------------------------------------------------------------
// inline because it is used exactly once in the code
inline wxRect wxPropertyGrid::GetEditorWidgetRect( wxPGProperty* p )
{
//wxASSERT( p->m_y >= 0 ); // item is not visible
int itemy = p->m_y;
int vx,vy;// Top left corner of client
GetViewStart(&vx,&vy);
vy *= wxPG_PIXELS_PER_UNIT;
int cust_img_space = 0;
//m_iFlags &= ~(wxPG_FL_CUR_USES_CUSTOM_IMAGE);
// TODO: If custom image detection changes from current, change this.
if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE /*p->m_flags & wxPG_PROP_CUSTOMIMAGE*/ )
{
//m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
int imwid = p->GetImageSize().x;
if ( imwid < 1 ) imwid = wxPG_CUSTOM_IMAGE_WIDTH;
cust_img_space = imwid + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2;
}
return wxRect
(
m_splitterx+cust_img_space+wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1,
itemy-vy,
m_width-m_splitterx-wxPG_XBEFOREWIDGET-wxPG_CONTROL_MARGIN-cust_img_space-1,
m_lineHeight-1
);
}
// -----------------------------------------------------------------------
// return size of custom paint image
wxSize wxPropertyGrid::GetImageSize( wxPGId id ) const
{
if ( wxPGIdIsOk(id) )
{
wxSize cis = wxPGIdToPtr(id)->GetImageSize();
if ( cis.x < 0 )
{
if ( cis.x <= -1 )
cis.x = wxPG_CUSTOM_IMAGE_WIDTH;
}
if ( cis.y <= 0 )
{
if ( cis.y >= -1 )
cis.y = wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight);
else
cis.y = -cis.y;
}
return cis;
}
// If called with NULL property, then return default image
// size for properties that use image.
return wxSize(wxPG_CUSTOM_IMAGE_WIDTH,wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight));
}
// -----------------------------------------------------------------------
void wxPropertyGrid::CorrectEditorWidgetSizeX( int newSplitterx, int newWidth )
{
wxASSERT( m_selected );
int secWid = 0;
if ( m_wndSecondary )
{
// if width change occurred, move secondary wnd by that amount
wxRect r = m_wndSecondary->GetRect();
secWid = r.width;
r.x = newWidth - secWid;
//r.y += yAdj;
m_wndSecondary->SetSize ( r );
// if primary is textctrl, then we have to add some extra space
#ifdef __WXMAC__
if ( m_wndPrimary )
#else
if ( m_wndPrimary && m_wndPrimary->IsKindOf(CLASSINFO(wxTextCtrl)) )
#endif
secWid += wxPG_TEXTCTRL_AND_BUTTON_SPACING;
}
if ( m_wndPrimary )
{
wxRect r = m_wndPrimary->GetRect();
r.x = newSplitterx+m_ctrlXAdjust;
//r.y += yAdj;
r.width = newWidth - r.x - secWid;
m_wndPrimary->SetSize(r);
}
/*
int sec_wid = 0;
int vx, vy;
GetViewStart(&vx,&vy);
vy*=wxPG_PIXELS_PER_UNIT;
int propY = m_selected->m_y - vy;
if ( m_wndSecondary )
{
// if width change occurred, move secondary wnd by that amount
wxRect r = m_wndSecondary->GetRect();
int adjust = r.y % wxPG_PIXELS_PER_UNIT;
if ( adjust > (wxPG_PIXELS_PER_UNIT/2) )
adjust = adjust - wxPG_PIXELS_PER_UNIT;
int y = propY + adjust;
sec_wid = r.width;
m_wndSecondary->Move ( new_width-r.width,y );
// if primary is textctrl, then we have to add some extra space
if ( m_wndPrimary && m_wndPrimary->IsKindOf(CLASSINFO(wxTextCtrl)) )
sec_wid += wxPG_TEXTCTRL_AND_BUTTON_SPACING;
}
if ( m_wndPrimary )
{
wxRect r = m_wndPrimary->GetRect();
int adjust = r.y % wxPG_PIXELS_PER_UNIT;
if ( adjust > (wxPG_PIXELS_PER_UNIT/2) )
adjust = adjust - wxPG_PIXELS_PER_UNIT;
wxLogDebug(wxT("adjust: %i"),adjust);
int y = propY + adjust;
m_wndPrimary->SetSize(
new_splitterx+m_ctrlXAdjust,
y,
new_width-(new_splitterx+m_ctrlXAdjust)-sec_wid,
r.height
);
}
*/
if ( m_wndSecondary )
m_wndSecondary->Refresh();
}
// -----------------------------------------------------------------------
/*void wxPropertyGrid::CorrectEditorWidgetSizeY( int cy )
{
if ( m_selected )
{
wxPoint cp(0,cy);
if ( m_wndPrimary )
m_wndPrimary->Move ( m_wndPrimary->GetPosition() + cp );
if ( m_wndSecondary )
m_wndSecondary->Move ( m_wndSecondary->GetPosition() + cp );
}
}*/
// -----------------------------------------------------------------------
// takes scrolling into account
void wxPropertyGrid::ImprovedClientToScreen( int* px, int* py )
{
int vx, vy;
GetViewStart(&vx,&vy);
vy*=wxPG_PIXELS_PER_UNIT;
vx*=wxPG_PIXELS_PER_UNIT;
*px -= vx;
*py -= vy;
ClientToScreen ( px, py );
}
// -----------------------------------------------------------------------
// custom set cursor
void wxPropertyGrid::CustomSetCursor( int type, bool override )
{
if ( type == m_curcursor && !override ) return;
wxCursor* cursor = &wxPG_DEFAULT_CURSOR;
if ( type == wxCURSOR_SIZEWE )
cursor = m_cursorSizeWE;
SetCursor ( *cursor );
//if ( m_wndPrimary ) m_wndPrimary->SetCursor(wxNullCursor);
m_curcursor = type;
}
// -----------------------------------------------------------------------
// wxPropertyGrid property selection
// -----------------------------------------------------------------------
#define CONNECT_CHILD(EVT,FUNCTYPE,FUNC) \
wnd->Connect(id, EVT, \
(wxObjectEventFunction) (wxEventFunction) \
FUNCTYPE (&wxPropertyGrid::FUNC), \
NULL, this );
/*
class MyEvtHandler : public wxEvtHandler
{
public:
virtual bool ProcessEvent( wxEvent& event )
{
if ( event.GetEventType() == wxEVT_NAVIGATION_KEY )
wxLogDebug(wxT("wxEVT_NAVIGATION_KEY(id=%i)"),event.GetId());
else if ( event.GetEventType() == wxEVT_KEY_DOWN )
wxLogDebug(wxT("wxEVT_KEY_DOWN"));
event.Skip();
return wxEvtHandler::ProcessEvent(event);
}
};
*/
// Setups event handling for child control
void wxPropertyGrid::SetupEventHandling( wxWindow* argWnd, int id )
{
wxWindow* wnd = argWnd;
#if wxPG_ENABLE_CLIPPER_WINDOW
// Pass real control instead of clipper window
if ( wnd->IsKindOf(CLASSINFO(wxPGClipperWindow)) )
{
wnd = ((wxPGClipperWindow*)argWnd)->GetControl();
}
#endif
if ( argWnd == m_wndPrimary )
{
CONNECT_CHILD(wxEVT_MOTION,(wxMouseEventFunction),OnMouseMoveChild)
CONNECT_CHILD(wxEVT_LEFT_UP,(wxMouseEventFunction),OnMouseUpChild)
CONNECT_CHILD(wxEVT_LEFT_DOWN,(wxMouseEventFunction),OnMouseClickChild)
//CONNECT_CHILD(wxEVT_LEFT_DCLICK,(wxMouseEventFunction),OnMouseClickChild)
CONNECT_CHILD(wxEVT_RIGHT_UP,(wxMouseEventFunction),OnMouseRightClickChild)
CONNECT_CHILD(wxEVT_ENTER_WINDOW,(wxMouseEventFunction),OnMouseEntry)
CONNECT_CHILD(wxEVT_LEAVE_WINDOW,(wxMouseEventFunction),OnMouseEntry)
}
else
{
CONNECT_CHILD(wxEVT_NAVIGATION_KEY,(wxNavigationKeyEventFunction),OnNavigationKey)
}
CONNECT_CHILD(wxEVT_KEY_DOWN,(wxCharEventFunction),OnChildKeyDown)
CONNECT_CHILD(wxEVT_KEY_UP,(wxCharEventFunction),OnChildKeyUp)
CONNECT_CHILD(wxEVT_KILL_FOCUS,(wxFocusEventFunction),OnFocusEvent)
}
void wxPropertyGrid::FreeEditors()
{
// Do not free editors immediately if processing events
if ( !m_windowsToDelete )
m_windowsToDelete = new wxArrayPtrVoid;
if ( m_wndSecondary )
{
m_windowsToDelete->push_back(m_wndSecondary);
m_wndSecondary->Hide();
m_wndSecondary = (wxWindow*) NULL;
}
if ( m_wndPrimary )
{
m_windowsToDelete->push_back(m_wndPrimary);
m_wndPrimary->Hide();
m_wndPrimary = (wxWindow*) NULL;
}
}
// Call with NULL to de-select property
bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags )
{
#if __INTENSE_DEBUGGING__
if (p)
wxLogDebug(wxT("SelectProperty( %s (%s[%i]) )"),p->m_label.c_str(),
p->m_parent->m_label.c_str(),p->GetIndexInParent());
else
wxLogDebug(wxT("SelectProperty( NULL, -1 )"));
#endif
//
// Delete windows pending for deletion
if ( m_windowsToDelete && !m_processingEvent && m_windowsToDelete->size() )
{
unsigned int i;
for ( i=0; i<m_windowsToDelete->size(); i++ )
delete ((wxWindow*)((*m_windowsToDelete)[i]));
m_windowsToDelete->clear();
}
wxPGProperty* prev = m_selected;
//
// If we are frozen, then just set the values.
if ( m_frozen )
{
m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR);
m_editorFocused = 0;
m_selected = p;
FROM_STATE(m_selected) = p;
// If frozen, always free controls. But don't worry, as Thaw will
// recall SelectProperty to recreate them.
FreeEditors();
// Prevent any further selection measures in this call
p = (wxPGProperty*) NULL;
}
else
{
// Is it the same?
if ( m_selected == p && !(flags & wxPG_SEL_FORCE) )
{
// Only set focus if not deselecting
if ( p )
{
if ( flags & wxPG_SEL_FOCUS )
{
if ( m_wndPrimary )
{
m_wndPrimary->SetFocus();
m_editorFocused = 1;
}
}
else
{
wxScrolledWindow::SetFocus();
m_editorFocused = 0;
}
}
return true;
}
wxClientDC dc(this);
PrepareDC(dc);
// Don't put this earlier, due to return statements
m_iFlags |= wxPG_FL_IN_SELECT_PROPERTY;
//
// First, deactivate previous
if ( m_selected )
{
#if __INTENSE_DEBUGGING__
wxLogDebug(wxT(" (closing previous (%s))"), m_selected->m_label.c_str() );
#endif
// Must double-check if this is an selected in case of forceswitch
if ( p != prev )
{
if ( !CommitChangesFromEditor(flags) )
{
// Validation has failed, so we can't exit the previous editor
//::wxMessageBox(_("Please correct the value or press ESC to cancel the edit."),
// _("Invalid Value"),wxOK|wxICON_ERROR);
return false;
}
}
FreeEditors();
m_iFlags &= ~(wxPG_FL_SELECTED_IS_PAINT_FLEXIBLE|wxPG_FL_SELECTED_IS_FULL_PAINT);
m_selected = (wxPGProperty*) NULL;
FROM_STATE(m_selected) = (wxPGProperty*) NULL;
// Make sure the previous selection is refreshed
// JACS: must use paint handler whenever possible
Refresh(false);
/*if ( m_iFlags & wxPG_FL_ABNORMAL_EDITOR )
Refresh(false);
else if ( prev->m_y < (int)m_bottomy )
DoDrawItems( dc, prev, prev, NULL );
*/
m_iFlags &= ~(wxPG_FL_VALUE_MODIFIED|wxPG_FL_ABNORMAL_EDITOR);
}
//
// Then, activate the one given.
if ( p )
{
m_editorFocused = 0;
m_selected = p;
FROM_STATE(m_selected) = p;
m_iFlags |= wxPG_FL_PRIMARY_FILLS_ENTIRE;
if ( p != prev )
m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
//m_wndPrimary = (wxWindow*) NULL;
wxASSERT( m_wndPrimary == (wxWindow*) NULL );
// Do we need OnMeasureCalls?
wxSize imsz = p->GetImageSize();
if ( imsz.y < -1 )
m_iFlags |= wxPG_FL_SELECTED_IS_PAINT_FLEXIBLE;
// Is the entire cell/row custom painted?
if ( imsz.x == wxPG_FULL_CUSTOM_PAINT_WIDTH )
m_iFlags |= wxPG_FL_SELECTED_IS_FULL_PAINT;
//
// Only create editor for non-disabled non-caption
if ( p->GetParentingType() <= 0 && !(p->m_flags & wxPG_PROP_DISABLED) )
{
// do this for non-caption items
// Do we need to paint the custom image, if any?
m_iFlags &= ~(wxPG_FL_CUR_USES_CUSTOM_IMAGE);
if ( (p->m_flags & wxPG_PROP_CUSTOMIMAGE) &&
!p->GetEditorClass()->CanContainCustomImage()
)
m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
wxRect grect = GetEditorWidgetRect(p);
wxPoint good_pos = grect.GetPosition();
#if wxPG_CREATE_CONTROLS_HIDDEN
int coord_adjust = m_height - good_pos.y;
good_pos.y += coord_adjust;
#endif
const wxPGEditor* editor = p->GetEditorClass();
wxCHECK_MSG(editor, false,
wxT("NULL editor class not allowed"));
#ifndef __WXPYTHON__
m_wndPrimary = editor->CreateControls(this,
p,
good_pos,
grect.GetSize(),
&m_wndSecondary);
#else
wxPGWindowPair wndPair = editor->CreateControls(this,
p,
good_pos,
grect.GetSize());
m_wndPrimary = wndPair.m_primary;
m_wndSecondary = wndPair.m_secondary;
#endif
// NOTE: It is allowed for m_wndPrimary to be NULL - in this case
// value is drawn as normal, and m_wndSecondary is assumed
// to be a right-aligned button that triggers a separate editor
// window.
if ( m_wndPrimary )
{
//wxLogDebug(wxT("%s Editor created for %s"),editor->GetName(),p->GetName().c_str());
// Set validator, if any
/*#if wxUSE_VALIDATORS
if ( validator ) m_wndPrimary->SetValidator(*validator);
#endif*/
if ( m_wndPrimary->GetSize().y > (m_lineHeight+6) )
m_iFlags |= wxPG_FL_ABNORMAL_EDITOR;
// If it has modified status, use bold font
// (must be done before capturing m_ctrlXAdjust)
if ( (p->m_flags & wxPG_PROP_MODIFIED) && (m_windowStyle & wxPG_BOLD_MODIFIED) )
SetCurControlBoldFont();
//
// Fix TextCtrl indentation
#if defined(__WXMSW__) && !defined(__WXWINCE__)
wxTextCtrl* tc = wxDynamicCast(m_wndPrimary, wxTextCtrl);
if ( tc )
::SendMessage(GetHwndOf(tc), EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0));
#endif
// Store x relative to splitter (we'll need it).
m_ctrlXAdjust = m_wndPrimary->GetPosition().x - m_splitterx;
// Check if background clear is not necessary
wxPoint pos = m_wndPrimary->GetPosition();
if ( pos.x > (m_splitterx+1) || pos.y > p->m_y )
{
m_iFlags &= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE);
}
m_wndPrimary->SetSizeHints(3,3);
#if wxPG_CREATE_CONTROLS_HIDDEN
m_wndPrimary->Show(false);
m_wndPrimary->Freeze();
good_pos = m_wndPrimary->GetPosition();
good_pos.y -= coord_adjust;
m_wndPrimary->Move( good_pos );
#endif
SetupEventHandling(m_wndPrimary, wxPG_SUBID1);
// Focus and select all (wxTextCtrl, wxComboBox etc)
if ( flags & wxPG_SEL_FOCUS )
{
wxWindow* ctrl = m_wndPrimary;
ctrl->SetFocus();
#if wxPG_NAT_TEXTCTRL_BORDER_ANY
// Take into account textctrl in clipper window
if ( ctrl->IsKindOf(CLASSINFO(wxPGClipperWindow)) )
ctrl = ((wxPGClipperWindow*)ctrl)->GetControl();
#endif
p->GetEditorClass()->OnFocus(p,m_wndPrimary);
}
}
if ( m_wndSecondary )
{
m_wndSecondary->SetSizeHints(3,3);
#if wxPG_CREATE_CONTROLS_HIDDEN
wxRect sec_rect = m_wndSecondary->GetRect();
sec_rect.y -= coord_adjust;
// Fine tuning required to fix "oversized"
// button disappearance bug.
if ( sec_rect.y < 0 )
{
sec_rect.height += sec_rect.y;
sec_rect.y = 0;
}
m_wndSecondary->SetSize( sec_rect );
#endif
m_wndSecondary->Show();
SetupEventHandling(m_wndSecondary,wxPG_SUBID2);
// If no primary editor, focus to button to allow
// it to interprete ENTER etc.
// NOTE: Due to problems focusing away from it, this
// has been disabled.
/*
if ( (flags & wxPG_SEL_FOCUS) && !m_wndPrimary )
m_wndSecondary->SetFocus();
*/
}
if ( flags & wxPG_SEL_FOCUS )
m_editorFocused = 1;
}
else
{
// wxGTK atleast seems to need this (wxMSW not)
SetFocus();
}
m_iFlags &= ~(wxPG_FL_VALUE_MODIFIED);
//Update();
// If it's inside collapsed section, expand parent, scroll, etc.
// Also, if it was partially visible, scroll it into view.
int vx, vy;
GetViewStart(&vx,&vy);
vy*=wxPG_PIXELS_PER_UNIT;
int vy2 = vy + m_height;
if ( (p->m_y < vy ||
(p->m_y <= vy2 &&
(p->m_y+m_lineHeight) > vy2)) &&
!(flags & wxPG_SEL_NONVISIBLE) )
EnsureVisible( wxPGIdGen(p) );
if ( m_wndPrimary )
{
// Clear its background
// (why can't this be optimized by some other drawing?)
if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) )
{
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW) );
dc.DrawRectangle(m_splitterx+1,p->m_y,
m_width-m_splitterx,m_lineHeight-1);
}
#if wxPG_CREATE_CONTROLS_HIDDEN
m_wndPrimary->Thaw();
#endif
m_wndPrimary->Show(true);
}
DoDrawItems( dc, p, p, (const wxRect*) NULL );
}
}
#if wxUSE_STATUSBAR
//
// Show help text in status bar.
// (if found and grid not embedded in manager with help box and
// style wxPG_EX_HELP_AS_TOOLTIPS is not used).
//
if ( !(GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS) )
{
wxStatusBar* statusbar = (wxStatusBar*) NULL;
if ( !(m_iFlags & wxPG_FL_NOSTATUSBARHELP) )
{
wxFrame* frame = wxDynamicCast(::wxGetTopLevelParent(this),wxFrame);
if ( frame )
statusbar = frame->GetStatusBar();
}
if ( statusbar )
{
const wxString* pHelpString = (const wxString*) NULL;
if ( p && p->m_dataExt )
{
pHelpString = &p->m_dataExt->m_helpString;
if ( pHelpString->length() )
{
// Set help box text.
statusbar->SetStatusText( *pHelpString );
m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR;
}
}
if ( (!pHelpString || !pHelpString->length()) &&
(m_iFlags & wxPG_FL_STRING_IN_STATUSBAR) )
{
// Clear help box - but only if it was written
// by us at previous time.
statusbar->SetStatusText( m_emptyString );
m_iFlags &= ~(wxPG_FL_STRING_IN_STATUSBAR);
}
}
}
#endif
m_iFlags &= ~(wxPG_FL_IN_SELECT_PROPERTY);
// call wx event handler (here so that it also occurs on deselection)
SendEvent( wxEVT_PG_SELECTED, m_selected, flags );
return true;
}
// -----------------------------------------------------------------------
// This method is not inline because it called dozens of times
// (i.e. two-arg function calls create smaller code size).
bool wxPropertyGrid::ClearSelection()
{
return DoSelectProperty((wxPGProperty*)NULL);
}
// -----------------------------------------------------------------------
// wxPropertyGrid expand/collapse state and priority (compact mode) related
// -----------------------------------------------------------------------
bool wxPropertyGrid::_Collapse( wxPGProperty* p, bool sendEvents )
{
wxCHECK_MSG( p, false, wxT("invalid property id") );
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
if ( pwc->GetParentingType() == 0 ) return false;
if ( !pwc->m_expanded ) return false;
// If active editor was inside collapsed section, then disable it
if ( m_selected && m_selected->IsSomeParent (p) )
{
if ( !ClearSelection() )
return false;
}
// Store dont-center-splitter flag 'cause we need to temporarily set it
wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
// m_expanded must be set just before call to CalculateYs
pwc->m_expanded = 0;
// Redraw etc. only if collapsed was visible.
if (pwc->m_y >= 0 &&
!m_frozen &&
( pwc->GetParentingType() != 1 || !(m_windowStyle & wxPG_HIDE_CATEGORIES) ) )
{
/*int y_adjust = 0;
if ( m_selected && m_selected->m_y > pwc->m_y )
{
wxPGProperty* next_vis = GetNeighbourItem(pwc,true,1);
wxASSERT( next_vis );
y_adjust = next_vis->m_y - pwc->m_y - m_lineHeight;
}*/
CalculateYs( pwc->m_parent, pwc->m_arrIndex );
// Fix control position.
/*if ( y_adjust )
CorrectEditorWidgetSizeY ( -y_adjust );*/
// When item is collapsed so that scrollbar would move,
// graphics mess is about (unless we redraw everything).
Refresh();
}
// Clear dont-center-splitter flag if it wasn't set
m_iFlags = m_iFlags & ~(wxPG_FL_DONT_CENTER_SPLITTER) | old_flag;
if ( sendEvents )
SendEvent( wxEVT_PG_ITEM_COLLAPSED, p );
return true;
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::_Expand( wxPGProperty* p, bool sendEvents )
{
wxCHECK_MSG( p, false, wxT("invalid property id") );
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
if ( pwc->GetParentingType() == 0 ) return false;
if ( pwc->m_expanded ) return false;
// Store dont-center-splitter flag 'cause we need to temporarily set it
wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
// m_expanded must be set just before call to CalculateYs
pwc->m_expanded = 1;
// Redraw etc. only if expanded was visible.
if ( pwc->m_y >= 0 && !m_frozen &&
( pwc->GetParentingType() != 1 || !(m_windowStyle & wxPG_HIDE_CATEGORIES) )
)
{
CalculateYs( pwc->m_parent, pwc->m_arrIndex );
/*int y_adjust = pwc->GetCount()*m_lineHeight;
// Fix widget position as well
if ( m_selected && m_selected->m_y > pwc->m_y )
CorrectEditorWidgetSizeY ( y_adjust );*/
// Redraw
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
Refresh();
#else
//wxPG_CLIENT_DC_INIT_R(true)
//DrawItems( dc, pwc->m_y, m_bottomy );
DrawItems(pwc,(wxPGProperty*) NULL);
#endif
}
// Clear dont-center-splitter flag if it wasn't set
m_iFlags = m_iFlags & ~(wxPG_FL_DONT_CENTER_SPLITTER) | old_flag;
if ( sendEvents )
SendEvent( wxEVT_PG_ITEM_EXPANDED, p );
return true;
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::Compact( bool compact )
{
#if __INTENSE_DEBUGGING__
wxLogDebug( wxT("wxPropertyGrid::Compact()") );
#endif
if ( compact )
{
if ( !(m_iFlags & wxPG_FL_HIDE_STATE) )
{
// Deselect selected if it was hideable
if ( m_selected && ( m_selected->m_flags & wxPG_PROP_HIDEABLE ) )
{
if ( !ClearSelection() )
return false;
}
m_iFlags |= wxPG_FL_HIDE_STATE;
if ( !m_frozen )
{
CalculateYs( NULL, -1 );
RedrawAllVisible();
}
}
}
else
{
if ( m_iFlags & wxPG_FL_HIDE_STATE )
{
m_iFlags &= ~(wxPG_FL_HIDE_STATE);
if ( !m_frozen )
{
CalculateYs( NULL, -1 );
RedrawAllVisible();
}
}
}
return true;
}
// -----------------------------------------------------------------------
// Used by HideProperty as well
bool wxPropertyGrid::SetPropertyPriority( wxPGProperty* p, int priority )
{
/*
// Old code (Commented Aug-09-2007)
if ( m_frozen )
return m_pState->SetPropertyPriority(p,priority);
if ( (m_iFlags & wxPG_FL_HIDE_STATE) && m_selected &&
( m_selected == p || m_selected->IsSomeParent(p) )
)
{
if ( !ClearSelection() )
return false;
}
m_pState->SetPropertyPriority(p,priority);
if ( m_iFlags & wxPG_FL_HIDE_STATE )
{
CalculateYs(NULL,-1);
RedrawAllVisible();
}
return true;
*/
// Stefan Battmer:
// Changed in a way that this update is only forced when the
// properties new priority actually differs from the current
// priority to improve update speed
if ( p )
{
int oldPriority = ( p->IsFlagSet(wxPG_PROP_HIDEABLE) ) ? wxPG_LOW : wxPG_HIGH;
if( oldPriority != priority )
{
if ( m_frozen )
return m_pState->SetPropertyPriority(p,priority);
if ( (m_iFlags & wxPG_FL_HIDE_STATE) && m_selected &&
( m_selected == p || m_selected->IsSomeParent(p) )
)
{
if ( !ClearSelection() )
return false;
}
m_pState->SetPropertyPriority(p,priority);
if ( m_iFlags & wxPG_FL_HIDE_STATE )
{
CalculateYs(NULL,-1);
RedrawAllVisible();
}
return true;
}
}
return false;
}
// -----------------------------------------------------------------------
// wxPropertyGrid size related methods
// -----------------------------------------------------------------------
// This is called by CalculateYs (so those calling it won't need to call this)
void wxPropertyGrid::RecalculateVirtualSize()
{
int x = m_width;
int y = m_bottomy;
//SetClientSize(x,y);
// Now adjust virtual size.
SetVirtualSize(x, y);
PGAdjustScrollbars(y);
//
// FIXME: Is this really needed? I mean, can't OnResize handle this?
int width, height;
GetClientSize(&width,&height);
if ( m_selected && width != m_width )
{
CorrectEditorWidgetSizeX( m_splitterx, width );
}
m_width = width;
m_height = height;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::PGAdjustScrollbars( int y )
{
// Adjust scrollbars.
y += wxPG_PIXELS_PER_UNIT+2; // One more scrollbar unit + 2 pixels.
int y_amount = y/wxPG_PIXELS_PER_UNIT;
int y_pos = GetScrollPos( wxVERTICAL );
SetScrollbars( 0, wxPG_PIXELS_PER_UNIT, 0,
y_amount, 0, y_pos, true );
}
// -----------------------------------------------------------------------
/*
bool wxPropertyGrid::DetectScrollbar()
{
// Call at every time scrollbar may have appeared/disappeared
// Returns true if scrollbar was toggled
bool toggled = false;
// Use functions instead of m_width for total independence
wxCoord width = GetSize().x;
wxCoord cwidth = GetClientSize().x;
if ( abs(width-cwidth) >= wxPG_MIN_SCROLLBAR_WIDTH )
{
// There is a scrollbar.
if ( !(m_iFlags & wxPG_FL_SCROLLBAR_DETECTED) )
{
//wxLogDebug(wxT("Scrollbar Appeared"));
toggled = true;
m_iFlags |= wxPG_FL_SCROLLBAR_DETECTED;
}
}
else if ( m_iFlags & wxPG_FL_SCROLLBAR_DETECTED )
{
//wxLogDebug(wxT("Scrollbar Disappeared"));
toggled = true;
m_iFlags &= ~(wxPG_FL_SCROLLBAR_DETECTED);
}
return toggled;
}
*/
void wxPropertyGrid::OnResize( wxSizeEvent& event )
{
if ( !(m_iFlags & wxPG_FL_INITIALIZED) )
return;
if ( FROM_STATE(m_itemsAdded) && !m_frozen )
PrepareAfterItemsAdded();
int width, height;
GetClientSize(&width,&height);
#if __INTENSE_DEBUGGING__
wxLogDebug(wxT("wxPropertyGrid::OnResize ( %i, %i )"),width,height);
#endif
//int old_width = m_width;
//int old_height = m_height;
int old_fwidth = m_fWidth; // non-client width
int old_splitterx = m_splitterx;
int fwidth = event.GetSize().x;
m_fWidth = fwidth;
m_width = width;
m_height = height;
int widthDiff = fwidth - old_fwidth;
#if wxPG_DOUBLE_BUFFER
if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
{
int dblh = (m_lineHeight*2);
if ( !m_doubleBuffer )
{
// Create double buffer bitmap to draw on, if none
int w = (width>250)?width:250;
int h = height + dblh;
h = (h>400)?h:400;
m_doubleBuffer = new wxBitmap ( w, h );
}
else
{
int w = m_doubleBuffer->GetWidth();
int h = m_doubleBuffer->GetHeight();
// Double buffer must be large enough
if ( w < width || h < (height+dblh) )
{
if ( w < width ) w = width;
if ( h < (height+dblh) ) h = height + dblh;
delete m_doubleBuffer;
m_doubleBuffer = new wxBitmap ( w, h );
}
}
}
// Consider full update on every resize
//m_iFlags |= wxPG_FL_CHANGED;
#endif
//
// Center splitter when...
// * always when propGrid not shown yet or its full size is not realized yet
// and then only if splitter's position was not pre-set
// * auto-centering is enabled and scrollbar was not toggled
//
// Need to center splitter?
//if ( width!=old_width )
{
bool needSplitterCheck = true;
//if ( !sb_vis_toggled )
{
if ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER )
{
float centerX = float(width) * 0.5;
float splitterX = m_fSplitterX + (float(widthDiff) * 0.5);
float deviation = fabs(centerX - splitterX);
//wxLogDebug(wxT("deviation: %.1f"),deviation);
// If deviated too far from the center, reset it
if ( deviation > 30.0 )
splitterX = centerX;
DoSetSplitterPosition( (int)splitterX, false );
m_fSplitterX = splitterX; // needed to retain accuracy
needSplitterCheck = false;
}
else if ( !(m_iFlags & wxPG_FL_SPLITTER_PRE_SET) )
{
long timeSinceCreation = (::wxGetLocalTimeMillis() - m_timeCreated).ToLong();
if ( m_pState->m_properties->GetCount() || timeSinceCreation > 750 )
{
SetSplitterLeft( false );
needSplitterCheck = false;
}
else
{
DoSetSplitterPosition( width / 2, false );
m_iFlags &= ~(wxPG_FL_SPLITTER_PRE_SET);
needSplitterCheck = false;
}
}
}
if ( needSplitterCheck && (m_splitterx + wxPG_DRAG_MARGIN) > width )
{
long timeSinceCreation = (::wxGetLocalTimeMillis() - m_timeCreated).ToLong();
if ( timeSinceCreation >= 750 )
{
DoSetSplitterPosition( width - wxPG_DRAG_MARGIN - 1, false );
}
}
}
// Need to correct widget position?
if ( m_selected /*&& (width != old_width || sb_vis_toggled)*/ )
{
// Take splitter position change into account
CorrectEditorWidgetSizeX( m_splitterx, width );
}
if ( !m_frozen )
{
// Need to recalculate visibles array?
//if ( height != old_height )
if ( height > m_calcVisHeight )
CalculateVisibles( -1, false );
/*if ( sb_vis_toggled )
{
Refresh();
}
else*/
if ( m_splitterx != old_splitterx )
{
Refresh();
/*if ( abs(height-old_height) < 100 )
{
Update(); // Necessary, atleast on wxMSW
RedrawAllVisible();
}
else
{
Refresh();
}*/
}
}
// Without this, virtual size (atleast under wxGTK) will be skewed
RecalculateVirtualSize();
}
// -----------------------------------------------------------------------
// wxPropertyGrid mouse event handling
// -----------------------------------------------------------------------
// selFlags uses same values DoSelectProperty's flags
void wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p, unsigned int selFlags )
{
// Send property grid event of specific type and with specific property
wxPropertyGridEvent evt( eventType, GetId() );
evt.SetPropertyGrid(this);
evt.SetEventObject(m_eventObject);
evt.SetProperty(p);
wxEvtHandler* evtHandler = GetEventHandler();
// Always need to process event immediately if the property in question is
// about to be deleted.
if ( (selFlags & wxPG_SEL_DELETING) ||
(GetExtraStyle() & wxPG_EX_PROCESS_EVENTS_IMMEDIATELY) )
{
evtHandler->ProcessEvent(evt);
}
else
{
evt.SetPending(true);
evtHandler->AddPendingEvent(evt);
}
}
// -----------------------------------------------------------------------
// Return false if should be skipped
bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &event )
{
bool res = true;
#if __MOUSE_DEBUGGING__
wxLogDebug( wxT(" \\--> HandleMouseClick") );
#endif
// Need to set focus?
if ( !(m_iFlags & wxPG_FL_FOCUSED) )
{
SetFocus();
}
if ( y < m_bottomy )
{
wxPGProperty* p = DoGetItemAtY(y);
if ( p )
{
int parenting = p->GetParentingType();
int depth = (int)p->GetDepth() - 1;
int marginEnds = m_marginWidth + ( depth * m_subgroup_extramargin );
if ( x >= marginEnds )
{
// Outside margin.
if ( parenting > 0 )
{
// This is category.
wxPropertyCategoryClass* pwc = (wxPropertyCategoryClass*)p;
int text_x = m_marginWidth + ((unsigned int)((pwc->m_depth-1)*m_subgroup_extramargin));
// Expand, collapse, activate etc. if click on text or left of splitter.
if ( x >= text_x
&&
( x < (text_x+pwc->GetTextExtent()+(wxPG_CAPRECTXMARGIN*2))
||
x < m_splitterx
)
)
{
if ( !DoSelectProperty( p ) )
return res;
// On double-click, expand/collapse.
if ( event.ButtonDClick() && !(m_windowStyle & wxPG_HIDE_MARGIN) )
{
if ( pwc->m_expanded ) _Collapse ( p, true );
else _Expand ( p, true );
}
}
}
else if ( x > (m_splitterx + wxPG_SPLITTERX_DETECTMARGIN2) ||
x < (m_splitterx - wxPG_SPLITTERX_DETECTMARGIN1) )
{
// Click on value.
unsigned int selFlag = 0;
if ( x > m_splitterx )
{
m_iFlags |= wxPG_FL_ACTIVATION_BY_CLICK;
selFlag = wxPG_SEL_FOCUS;
}
if ( !DoSelectProperty( p, selFlag ) )
return res;
m_iFlags &= ~(wxPG_FL_ACTIVATION_BY_CLICK);
if ( p->GetParentingType() < 0 )
// On double-click, expand/collapse.
if ( event.ButtonDClick() && !(m_windowStyle & wxPG_HIDE_MARGIN) )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
if ( pwc->m_expanded ) _Collapse ( p, true );
else _Expand ( p, true );
}
res = false;
}
else
{
// click on splitter
if ( !(m_windowStyle & wxPG_STATIC_SPLITTER) )
{
if ( event.GetEventType() == wxEVT_LEFT_DCLICK )
{
// Double-clicking the splitter causes auto-centering
CenterSplitter( true );
// TODO: Would this be more natural?
// .NET grid doesn't do it but maybe we should.
//CustomSetCursor ( wxCURSOR_ARROW );
}
else if ( m_dragStatus == 0 )
{
//
// Begin draggin the splitter
//
#if __MOUSE_DEBUGGING__
wxLogDebug( wxT(" dragging begins at splitter + %i"),
(int)(x - m_splitterx) );
#endif
if ( m_wndPrimary )
{
// Changes must be committed here or the
// value won't be drawn correctly
if ( !CommitChangesFromEditor() )
return res;
m_wndPrimary->Show ( false );
}
BEGIN_MOUSE_CAPTURE
m_dragStatus = 1;
m_dragOffset = x - m_splitterx;
wxPG_CLIENT_DC_INIT()
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
// Fixes button disappearance bug
if ( m_wndSecondary )
m_wndSecondary->Show ( false );
#endif
m_startingSplitterX = m_splitterx;
#if wxPG_HEAVY_GFX
#else
Update(); // clear graphics mess
DrawSplitterDragColumn( dc, m_splitterx );
m_splitterprevdrawnx = m_splitterx;
#endif
}
}
}
}
else
{
// Click on margin.
if ( parenting != 0 )
{
int nx = x + m_marginWidth - marginEnds; // Normalize x.
if ( (nx >= m_gutterWidth && nx < (m_gutterWidth+m_iconWidth)) )
{
int y2 = y - p->m_y;
if ( (y2 >= m_buttonSpacingY && y2 < (m_buttonSpacingY+m_iconHeight)) )
{
// On click on expander button, expand/collapse
if ( ((wxPGPropertyWithChildren*)p)->m_expanded )
_Collapse ( p, true );
else
_Expand ( p, true );
}
}
}
}
}
}
return res;
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x), unsigned int y,
wxMouseEvent& WXUNUSED(event) )
{
if ( y < m_bottomy )
{
// Select property here as well
wxPGProperty* p = m_propHover;
if ( p != m_selected )
DoSelectProperty( p );
// Send right click event.
SendEvent( wxEVT_PG_RIGHT_CLICK, p );
return true;
}
return false;
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x), unsigned int y,
wxMouseEvent& WXUNUSED(event) )
{
if ( y < m_bottomy )
{
// Select property here as well
wxPGProperty* p = m_propHover;
if ( p != m_selected )
DoSelectProperty( p );
// Send double-click event.
SendEvent( wxEVT_PG_DOUBLE_CLICK, m_propHover );
return true;
}
return false;
}
// -----------------------------------------------------------------------
/*
// Splits text into lines so that each will have width less than arg maxWidth.
// * Returns string with line breaks inserted into appropriate positions.
// * Keeps words together.
// * Useful in conjunction with wxWindow::SetToolTip and wxDC::DrawLabel.
static wxString SplitTextByPixelWidth(wxDC& dc, const wxString& text, int lineWidth)
{
if ( !text.length() )
return text;
wxString resultLine;
wxArrayInt extents;
unsigned int index = 0;
unsigned int maxIndex = text.length() - 1;
unsigned int prevSplitIndex = 0;
unsigned int prevCanSplitIndex = 0;
int lineCheckWidth = lineWidth;
wxChar prevA = wxT('\0');
dc.GetPartialTextExtents(text,extents);
wxASSERT( text.length() == extents.GetCount() );
while ( index <= maxIndex )
{
const wxChar A = text[index];
if ( !wxIsalnum(prevA) )
{
// Can split here
prevCanSplitIndex = index;
}
else
{
// Can't split here
}
if ( ( (extents[index] >= lineCheckWidth || A == wxT('\n')) &&
index > prevCanSplitIndex ) ||
index == maxIndex )
{
// Need to split now
unsigned int useSplit = prevCanSplitIndex;
if ( useSplit <= prevSplitIndex ||
index >= maxIndex )
useSplit = index;
resultLine << text.Mid(prevSplitIndex,useSplit-prevSplitIndex);
if ( index >= maxIndex )
break;
else
if ( A != wxT('\n') )
{
resultLine.Append(_T("\n"));
//resultLine.Append(text.Mid(useSplit,text.length()-useSplit));
//break;
}
prevSplitIndex = useSplit;
lineCheckWidth = extents[useSplit] + lineWidth;
//widSum = 0;
index = useSplit;
prevA = wxT('\0');
}
else
{
index++;
prevA = A;
}
}
return resultLine;
}
*/
// -----------------------------------------------------------------------
#if wxPG_SUPPORT_TOOLTIPS
void wxPropertyGrid::SetToolTip( const wxString& tipString )
{
if ( tipString.length() )
{
//wxClientDC dc(this);
//wxString finalString = SplitTextByPixelWidth(dc,tipString,350);
//wxScrolledWindow::SetToolTip(finalString);
wxScrolledWindow::SetToolTip(tipString);
}
else
{
#if wxPG_ALLOW_EMPTY_TOOLTIPS
wxScrolledWindow::SetToolTip( m_emptyString );
#else
wxScrolledWindow::SetToolTip( NULL );
#endif
}
}
#endif // #if wxPG_SUPPORT_TOOLTIPS
// -----------------------------------------------------------------------
// Return false if should be skipped
bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event )
{
// Safety check (needed because mouse capturing may
// otherwise freeze the control)
if ( m_dragStatus > 0 && !event.Dragging() )
{
//wxLogDebug(wxT("MOUSE CAPTURE SAFETY RELEASE TRIGGERED"));
HandleMouseUp(x,y,event);
}
if ( m_dragStatus > 0 )
{
if ( x > (m_marginWidth + wxPG_DRAG_MARGIN) &&
x < (m_width - wxPG_DRAG_MARGIN) )
{
#if wxPG_HEAVY_GFX
int new_splitterx = x - m_dragOffset;
// Splitter redraw required?
if ( new_splitterx != m_splitterx )
{
if ( m_selected )
CorrectEditorWidgetSizeX( new_splitterx, m_width );
// Move everything
m_splitterx = new_splitterx;
m_fSplitterX = (float) new_splitterx;
Update();
RedrawAllVisible();
}
#else
if ( x != m_splitterx )
{
wxPG_CLIENT_DC_INIT_R(false)
if ( m_splitterprevdrawnx != -1 )
DrawSplitterDragColumn( dc, m_splitterprevdrawnx );
m_splitterx = x;
m_fSplitterX = (float) x;
DrawSplitterDragColumn( dc, x );
m_splitterprevdrawnx = x;
}
#endif
m_dragStatus = 2;
}
return false;
}
else
{
int ih = m_lineHeight;
int sy = y;
#if wxPG_SUPPORT_TOOLTIPS
wxPGProperty* prevHover = m_propHover;
unsigned char prevSide = m_mouseSide;
#endif
// On which item it hovers
if ( ( !m_propHover && y < m_bottomy)
||
( m_propHover && ( sy < m_propHover->m_y || sy >= (m_propHover->m_y+ih) ) )
)
{
// Mouse moves on another property
m_propHover = DoGetItemAtY(y);
// Send hover event
SendEvent( wxEVT_PG_HIGHLIGHTED, m_propHover );
}
#if wxPG_SUPPORT_TOOLTIPS
// Store which side we are on
m_mouseSide = 0;
if ( x >= m_splitterx )
m_mouseSide = 2;
else if ( x >= m_marginWidth )
m_mouseSide = 1;
//
// If tooltips are enabled, show label or value as a tip
// in case it doesn't otherwise show in full length.
//
if ( m_windowStyle & wxPG_TOOLTIPS )
{
wxToolTip* tooltip = GetToolTip();
if ( m_propHover != prevHover || prevSide != m_mouseSide )
{
if ( m_propHover && m_propHover->GetParentingType() <= 0 )
{
if ( GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS )
{
// Show help string as a tooltip
wxString tipString = m_propHover->GetHelpString();
SetToolTip(tipString);
}
else
{
// Show cropped value string as a tooltip
wxString tipString;
int space = 0;
if ( m_mouseSide == 1 )
{
tipString = m_propHover->m_label;
space = m_splitterx-m_marginWidth-3;
}
else if ( m_mouseSide == 2 )
{
tipString = m_propHover->GetDisplayedString();
space = m_width - m_splitterx;
if ( m_propHover->m_flags & wxPG_PROP_CUSTOMIMAGE )
space -= wxPG_CUSTOM_IMAGE_WIDTH + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2;
}
if ( space )
{
int tw, th;
GetTextExtent( tipString, &tw, &th, 0, 0, &m_font );
if ( tw > space )
{
SetToolTip( tipString );
}
}
else
{
if ( tooltip )
{
#if wxPG_ALLOW_EMPTY_TOOLTIPS
wxScrolledWindow::SetToolTip( m_emptyString );
#else
wxScrolledWindow::SetToolTip( NULL );
#endif
}
}
}
}
else
{
if ( tooltip )
{
#if wxPG_ALLOW_EMPTY_TOOLTIPS
wxScrolledWindow::SetToolTip( m_emptyString );
#else
wxScrolledWindow::SetToolTip( NULL );
#endif
}
}
}
}
#endif
if ( x > (m_splitterx + wxPG_SPLITTERX_DETECTMARGIN2) ||
x < (m_splitterx - wxPG_SPLITTERX_DETECTMARGIN1) ||
y >= m_bottomy ||
(m_windowStyle & wxPG_STATIC_SPLITTER) )
{
// hovering on something else
if ( m_curcursor != wxCURSOR_ARROW )
CustomSetCursor( wxCURSOR_ARROW );
}
else
{
// Do not allow splitter cursor on caption items.
// (also not if we were dragging and its started
// outside the splitter region)
if ( m_propHover &&
m_propHover->GetParentingType() <= 0 &&
!event.Dragging() )
{
// hovering on splitter
// NB: Condition disabled since MouseLeave event (from the editor control) cannot be
// reliably detected.
//if ( m_curcursor != wxCURSOR_SIZEWE )
CustomSetCursor( wxCURSOR_SIZEWE, true );
return false;
}
else
{
// hovering on something else
if ( m_curcursor != wxCURSOR_ARROW )
CustomSetCursor( wxCURSOR_ARROW );
}
}
}
return true;
}
// -----------------------------------------------------------------------
// Also handles Leaving event
bool wxPropertyGrid::HandleMouseUp( int x, unsigned int y, wxMouseEvent &WXUNUSED(event) )
{
bool res = false;
#if __MOUSE_DEBUGGING__
wxLogDebug( wxT(" \\--> HandleMouseUp") );
#endif
// No event type check - basicly calling this method should
// just stop dragging.
//if( event.LeftUp() || event.Leaving() )
//{
// Left up after dragged?
if ( m_dragStatus >= 1 )
{
//
// End Splitter Dragging
//
#if __MOUSE_DEBUGGING__
wxLogDebug( wxT(" dragging ends") );
#endif
// DO NOT ENABLE FOLLOWING LINE!
// (it is only here as a reminder to not to do it)
//m_splitterx = x;
#if wxPG_HEAVY_GFX
//Refresh();
#else
DoSetSplitterPosition( -1 ); // -1 tells not to make change
// Hack to clear-up editor graphics mess (on wxMSW, atleast)
if ( m_selected )
DrawItem ( m_selected );
#endif
// Disable splitter auto-centering
m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
// This is necessary to return cursor
END_MOUSE_CAPTURE
// Set back the default cursor, if necessary
if ( x > (m_splitterx + wxPG_SPLITTERX_DETECTMARGIN2) ||
x < (m_splitterx - wxPG_SPLITTERX_DETECTMARGIN1) ||
y >= m_bottomy )
{
CustomSetCursor( wxCURSOR_ARROW );
}
m_dragStatus = 0;
#if wxPG_HEAVY_GFX
// Control background needs to be cleared
if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) && m_selected )
DrawItem ( m_selected );
#endif
if ( m_wndPrimary )
{
m_wndPrimary->Show ( true );
}
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
// Fixes button disappearance bug
if ( m_wndSecondary )
m_wndSecondary->Show ( true );
#endif
// This clears the focus.
m_editorFocused = 0;
}
//}
return res;
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::OnMouseCommon( wxMouseEvent& event, int* px, int* py )
{
int ux, uy;
CalcUnscrolledPosition( event.m_x, event.m_y, &ux, &uy );
// Hide popup on clicks
// FIXME: Not necessary after transient window implemented
if ( event.GetEventType() != wxEVT_MOTION )
if ( m_wndPrimary && m_wndPrimary->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
{
((wxPGOwnerDrawnComboBox*)m_wndPrimary)->HidePopup();
}
//if (printmsg) wxLogDebug( wxT("On") wxT(#func) wxT("( %i, %i )"),(int)ux,(int)uy );
wxRect r;
wxWindow* wnd = m_wndPrimary;
if ( wnd )
r = wnd->GetRect();
if ( wnd == (wxWindow*) NULL || m_dragStatus ||
(
ux <= (m_splitterx + wxPG_SPLITTERX_DETECTMARGIN2) ||
event.m_y < r.y ||
event.m_y >= (r.y+r.height)
)
)
{
*px = ux;
*py = uy;
return true;
}
else
{
if ( m_curcursor != wxCURSOR_ARROW ) CustomSetCursor ( wxCURSOR_ARROW );
}
return false;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnMouseClick( wxMouseEvent &event )
{
int x, y;
if ( OnMouseCommon( event, &x, &y ) )
{
HandleMouseClick(x,y,event);
}
event.Skip();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnMouseRightClick( wxMouseEvent &event )
{
int x, y;
CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
HandleMouseRightClick(x,y,event);
event.Skip();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnMouseDoubleClick( wxMouseEvent &event )
{
// Always run standard mouse-down handler as well
OnMouseClick(event);
int x, y;
CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
HandleMouseDoubleClick(x,y,event);
event.Skip();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnMouseMove( wxMouseEvent &event )
{
int x, y;
if ( OnMouseCommon ( event, &x, &y ) )
{
HandleMouseMove(x,y,event);
}
event.Skip();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnMouseUp( wxMouseEvent &event )
{
int x, y;
if ( OnMouseCommon ( event, &x, &y ) )
{
HandleMouseUp(x,y,event);
}
event.Skip();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnMouseEntry( wxMouseEvent &event )
{
// This may get called from child control as well, so event's
// mouse position cannot be relied on.
//int x = event.m_x;
//int y = event.m_y;
if ( event.Entering() )
{
if ( !(m_iFlags & wxPG_FL_MOUSE_INSIDE) )
{
#if __MOUSE_DEBUGGING__
wxLogDebug(wxT("Mouse Enters Window"));
#endif
//SetCursor ( *wxSTANDARD_CURSOR );
// TODO: Fix this (detect parent and only do
// cursor trick if it is a manager).
wxASSERT( GetParent() );
GetParent()->SetCursor(wxNullCursor);
m_iFlags |= wxPG_FL_MOUSE_INSIDE;
//if ( m_wndPrimary ) m_wndPrimary->Show ( true );
}
else
GetParent()->SetCursor(wxNullCursor);
}
else if ( event.Leaving() )
{
// Without this, wxSpinCtrl editor will sometimes have wrong cursor
SetCursor( wxNullCursor );
// Get real cursor position
wxPoint pt = ScreenToClient(::wxGetMousePosition());
if ( ( pt.x <= 0 || pt.y <= 0 || pt.x >= m_width || pt.y >= m_height ) )
{
//if ( CommitChangesFromEditor() )
{
if ( (m_iFlags & wxPG_FL_MOUSE_INSIDE) )
{
#if __MOUSE_DEBUGGING__
wxLogDebug(wxT("Mouse Leaves Window"));
#endif
m_iFlags &= ~(wxPG_FL_MOUSE_INSIDE);
//if ( m_wndPrimary ) m_wndPrimary->Show ( false );
}
if ( m_dragStatus )
wxPropertyGrid::HandleMouseUp ( -1, 10000, event );
}
}
else
{
/*#if wxPG_NO_CHILD_EVT_MOTION
// cursor must be reset because EVT_MOTION handler is not there to do it
if ( m_curcursor != wxCURSOR_ARROW ) CustomSetCursor ( wxCURSOR_ARROW );
#endif*/
}
}
event.Skip();
}
// -----------------------------------------------------------------------
// if (printmsg) wxLogDebug( wxT("On") wxT(#func) wxT("Child ( %i, %i )"),(int)event.m_x,(int)event.m_y );
// Common code used by various OnMouseXXXChild methods.
bool wxPropertyGrid::OnMouseChildCommon( wxMouseEvent &event, int* px, int *py )
{
wxWindow* topCtrlWnd = (wxWindow*)event.GetEventObject();
wxASSERT( topCtrlWnd );
int x, y;
event.GetPosition(&x,&y);
#if wxPG_ENABLE_CLIPPER_WINDOW
// Take clipper window into account
if (topCtrlWnd->GetPosition().x < 1 &&
!topCtrlWnd->IsKindOf(CLASSINFO(wxPGClipperWindow)))
{
topCtrlWnd = topCtrlWnd->GetParent();
wxASSERT( topCtrlWnd->IsKindOf(CLASSINFO(wxPGClipperWindow)) );
x -= ((wxPGClipperWindow*)topCtrlWnd)->GetXClip();
y -= ((wxPGClipperWindow*)topCtrlWnd)->GetYClip();
}
#endif
wxRect r = topCtrlWnd->GetRect();
if ( !m_dragStatus &&
x > (m_splitterx-r.x+wxPG_SPLITTERX_DETECTMARGIN2) &&
y >= 0 && y < r.height \
)
{
if ( m_curcursor != wxCURSOR_ARROW ) CustomSetCursor ( wxCURSOR_ARROW );
event.Skip();
}
else
{
CalcUnscrolledPosition( event.m_x + r.x, event.m_y + r.y, \
px, py );
return true;
}
return false;
}
/*void wxPropertyGrid::OnMouseEntryChild ( wxMouseEvent &event )
{
wxLogDebug(wxT("Entering/Leaving Child..."));
event.Skip();
}*/
void wxPropertyGrid::OnMouseClickChild( wxMouseEvent &event )
{
int x,y;
if ( OnMouseChildCommon(event,&x,&y) )
{
bool res = HandleMouseClick(x,y,event);
if ( !res ) event.Skip();
/*if ( event.GetEventType() == wxEVT_LEFT_DCLICK )
{
HandleMouseDoubleClick( x, y, event );
event.Skip();
}*/
}
}
void wxPropertyGrid::OnMouseRightClickChild( wxMouseEvent &event )
{
int x,y;
wxASSERT( m_wndPrimary );
// These coords may not be exact (about +-2),
// but that should not matter (right click is about item, not position).
wxPoint pt = m_wndPrimary->GetPosition();
CalcUnscrolledPosition( event.m_x + pt.x, event.m_y + pt.y, &x, &y );
wxASSERT( m_selected );
m_propHover = m_selected;
bool res = HandleMouseRightClick(x,y,event);
if ( !res ) event.Skip();
}
void wxPropertyGrid::OnMouseMoveChild( wxMouseEvent &event )
{
int x,y;
if ( OnMouseChildCommon(event,&x,&y) )
{
bool res = HandleMouseMove(x,y,event);
if ( !res ) event.Skip();
}
}
void wxPropertyGrid::OnMouseUpChild( wxMouseEvent &event )
{
int x,y;
if ( OnMouseChildCommon(event,&x,&y) )
{
bool res = HandleMouseUp(x,y,event);
if ( !res ) event.Skip();
}
}
// -----------------------------------------------------------------------
// wxPropertyGrid keyboard event handling
// -----------------------------------------------------------------------
void wxPropertyGrid::SendNavigationKeyEvent( int dir )
{
wxNavigationKeyEvent evt;
evt.SetFlags(wxNavigationKeyEvent::FromTab|
(dir?wxNavigationKeyEvent::IsForward:
wxNavigationKeyEvent::IsBackward));
evt.SetEventObject(this);
GetEventHandler()->AddPendingEvent(evt);
}
void wxPropertyGrid::HandleKeyEvent(wxKeyEvent &event)
{
//
// Handles key event when editor control is not focused.
//
#if __INTENSE_DEBUGGING__
wxLogDebug( wxT("wxPropertyGrid::HandleKeyEvent(%i)"),(int)event.GetKeyCode() );
#endif
wxASSERT( !m_frozen );
if ( m_frozen )
return;
// Travelsal between items, collapsing/expanding, etc.
int keycode = event.GetKeyCode();
if ( keycode == WXK_TAB )
{
SendNavigationKeyEvent( event.ShiftDown()?0:1 );
return;
}
// Ignore Alt and Control when they are down alone
if ( keycode == WXK_ALT ||
keycode == WXK_CONTROL )
{
event.Skip();
return;
}
if ( m_selected )
{
// Show dialog?
if ( ButtonTriggerKeyTest(event) )
return;
wxPGProperty* p = m_selected;
int selectDir = -2;
if ( p->GetParentingType() != 0 &&
!(p->m_flags & wxPG_PROP_DISABLED)
)
{
//wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
if ( keycode == WXK_LEFT )
{
if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Collapse ( p ) )
keycode = 0;
}
else if ( keycode == WXK_RIGHT )
{
if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Expand ( p ) )
keycode = 0;
}
}
if ( keycode )
{
if ( keycode == WXK_UP || keycode == WXK_LEFT )
{
selectDir = 0;
}
else if ( keycode == WXK_DOWN || keycode == WXK_RIGHT )
{
selectDir = 1;
}
else
{
event.Skip();
}
}
if ( selectDir >= -1 )
{
p = GetNeighbourItem( p, true, selectDir );
if ( p )
DoSelectProperty(p);
}
}
else
{
// If nothing was selected, select the first item now
// (or navigate out of tab).
if ( keycode != WXK_ESCAPE )
{
wxPGProperty* p = GetFirst();
if ( p ) DoSelectProperty(p);
}
}
}
// -----------------------------------------------------------------------
// Potentially handles a keyboard event for editor controls.
// Returns false if event should *not* be skipped (on true it can
// be optionally skipped).
// Basicly, false means that SelectProperty was called (or was about
// to be called, if canDestroy was false).
bool wxPropertyGrid::HandleChildKey( wxKeyEvent& event, bool canDestroy )
{
int keycode = event.GetKeyCode();
bool res = true;
#if __INTENSE_DEBUGGING__
wxLogDebug( wxT("wxPropertyGrid::HandleChildKey(%i)"),(int)event.GetKeyCode() );
#endif
// Unfocus?
if ( keycode == WXK_ESCAPE )
{
// Esc cancels any changes
EditorsValueWasNotModified();
wxPGProperty* p = m_selected;
res = false;
if ( canDestroy )
{
DoSelectProperty( (wxPGProperty*)NULL, wxPG_SEL_NOVALIDATE );
DoSelectProperty( p );
}
}
return res;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnKey( wxKeyEvent &event )
{
//
// Events to editor controls should get relayed here.
//
wxWindow* focused = wxWindow::FindFocus();
//wxLogDebug(wxT("OnKey"));
if ( m_wndPrimary &&
(focused==m_wndPrimary
|| m_editorFocused
#if wxPG_ENABLE_CLIPPER_WINDOW
|| ((m_wndPrimary->IsKindOf(CLASSINFO(wxPGClipperWindow))) &&
((wxPGClipperWindow*)m_wndPrimary)->GetControl() == focused)
#endif
) )
{
// Child key must be processed here, since it can
// destroy the control which is referred by its own
// event handling.
HandleChildKey( event, true );
}
else
HandleKeyEvent( event );
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnKeyUp(wxKeyEvent &event)
{
m_keyComboConsumed = 0;
event.Skip();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnNavigationKey( wxNavigationKeyEvent& event )
{
// Ignore events that occur very close to focus set
if ( m_iFlags & wxPG_FL_IGNORE_NEXT_NAVKEY )
{
m_iFlags &= ~(wxPG_FL_IGNORE_NEXT_NAVKEY);
event.Skip();
return;
}
wxPGProperty* next = (wxPGProperty*) NULL;
int dir = event.GetDirection()?1:0;
if ( m_selected )
{
if ( dir == 1 && (m_wndPrimary || m_wndSecondary) )
{
wxWindow* focused = wxWindow::FindFocus();
wxWindow* wndToCheck = GetEditorControl();
// ODComboBox focus goes to its text ctrl, so we need to use it instead
if ( wndToCheck && wndToCheck->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
{
wxTextCtrl* comboTextCtrl = ((wxPGOwnerDrawnComboBox*)wndToCheck)->GetTextCtrl();
if ( comboTextCtrl )
wndToCheck = comboTextCtrl;
}
/*
// Because of problems navigating from wxButton, do not go to it.
if ( !wndToCheck )
{
// No primary, use secondary
wndToCheck = m_wndSecondary;
}
// If it has editor button, focus to it after the primary editor.
// NB: Doesn't work since wxButton on wxMSW doesn't seem to propagate
// key events (yes, I'm using wxWANTS_CHARS with it, and yes I
// have somewhat debugged in window.cpp itself).
else if ( focused == wndToCheck &&
m_wndSecondary &&
!(GetExtraStyle() & wxPG_EX_NO_TAB_TO_BUTTON) )
{
wndToCheck = m_wndSecondary;
wxLogDebug(wxT("Exp1"));
}
*/
if ( focused != wndToCheck &&
wndToCheck )
{
wndToCheck->SetFocus();
// Select all text in wxTextCtrl etc.
if ( m_wndPrimary && wndToCheck == m_wndPrimary )
m_selected->GetEditorClass()->OnFocus(m_selected,wndToCheck);
m_editorFocused = 1;
next = m_selected;
}
}
if ( !next )
{
next = GetNeighbourItem(m_selected,true,dir);
if ( next )
{
// This allows preventing NavigateOut to occur
DoSelectProperty( next, wxPG_SEL_FOCUS );
}
}
}
if ( !next )
event.Skip();
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::ButtonTriggerKeyTest( wxKeyEvent &event )
{
int keycode = event.GetKeyCode();
// Does the keycode trigger button?
if ( keycode == m_pushButKeyCode &&
m_wndSecondary &&
(!m_pushButKeyCodeNeedsAlt || event.AltDown()) &&
(!m_pushButKeyCodeNeedsCtrl || event.ControlDown()) )
{
m_keyComboConsumed = 1;
wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,m_wndSecondary->GetId());
GetEventHandler()->AddPendingEvent(evt);
return true;
}
return false;
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnChildKeyDown( wxKeyEvent &event )
{
int keycode = event.GetKeyCode();
// Ignore Alt and Control when they are down alone
if ( keycode == WXK_ALT ||
keycode == WXK_CONTROL )
{
event.Skip();
return;
}
if ( ButtonTriggerKeyTest(event) )
return;
// Since event handling may destroy the control which
// triggered this event, we need to send it separately
// to the wxPropertyGrid itself. Also, to allow pushed
// event handler to grab ENTER, ESC and such, this
// has been changed to add all keys as events.
if ( HandleChildKey(event,false) == true )
event.Skip();
GetEventHandler()->AddPendingEvent(event);
}
void wxPropertyGrid::OnChildKeyUp( wxKeyEvent &event )
{
m_keyComboConsumed = 0;
GetEventHandler()->AddPendingEvent(event);
event.Skip();
}
// -----------------------------------------------------------------------
// wxPropertyGrid miscellaneous event handling
// -----------------------------------------------------------------------
void wxPropertyGrid::OnIdle( wxIdleEvent& WXUNUSED(event) )
{
//
// Check if the focus is in this control or one of its children
wxWindow* newFocused = wxWindow::FindFocus();
if ( newFocused != m_curFocused )
HandleFocusChange( newFocused );
}
// Called by focus event handlers. newFocused is the window that becomes focused.
void wxPropertyGrid::HandleFocusChange( wxWindow* newFocused )
{
unsigned int oldFlags = m_iFlags;
//wxLogDebug(wxT("HandleFocusChange: %s"),newFocused?newFocused->GetClassInfo()->GetClassName():wxT("NULL"));
m_iFlags &= ~(wxPG_FL_FOCUSED);
wxWindow* parent = newFocused;
// This must be one of nextFocus' parents.
while ( parent )
{
// Use m_eventObject, which is either wxPropertyGrid or
// wxPropertyGridManager, as appropriate.
if ( parent == m_eventObject )
{
m_iFlags |= wxPG_FL_FOCUSED;
break;
}
parent = parent->GetParent();
}
m_curFocused = newFocused;
if ( (m_iFlags & wxPG_FL_FOCUSED) !=
(oldFlags & wxPG_FL_FOCUSED) )
{
// On each focus kill, mark the next nav key event
// to be ignored (can't do on set focus since the
// event would occur before it).
if ( !(m_iFlags & wxPG_FL_FOCUSED) )
{
m_iFlags |= wxPG_FL_IGNORE_NEXT_NAVKEY;
// Need to store changed value
CommitChangesFromEditor();
}
else
{
/*
//
// Preliminary code for tab-order respecting
// tab-traversal (but should be moved to
// OnNav handler)
//
wxWindow* prevFocus = event.GetWindow();
wxWindow* useThis = this;
if ( m_iFlags & wxPG_FL_IN_MANAGER )
useThis = GetParent();
if ( prevFocus &&
prevFocus->GetParent() == useThis->GetParent() )
{
wxList& children = useThis->GetParent()->GetChildren();
wxNode* node = children.Find(prevFocus);
if ( node->GetNext() &&
useThis == node->GetNext()->GetData() )
DoSelectProperty(GetFirst());
else if ( node->GetPrevious () &&
useThis == node->GetPrevious()->GetData() )
DoSelectProperty(GetLastProperty());
}
*/
m_iFlags &= ~(wxPG_FL_IGNORE_NEXT_NAVKEY);
}
// Redraw selected
if ( m_selected && (m_iFlags & wxPG_FL_INITIALIZED) )
DrawItem( m_selected );
}
}
void wxPropertyGrid::OnFocusEvent( wxFocusEvent& event )
{
#if 1
if ( event.GetEventType() == wxEVT_SET_FOCUS )
HandleFocusChange((wxWindow*)event.GetEventObject());
// Line changed to "else" when applying patch #1675902
//else if ( event.GetWindow() )
else
HandleFocusChange(event.GetWindow());
event.Skip();
#else
unsigned int oldFlags = m_iFlags;
//
// Determine the current focus state
if ( event.GetEventType() == wxEVT_SET_FOCUS ||
event.GetEventType() == wxEVT_CHILD_FOCUS )
{
m_iFlags |= wxPG_FL_FOCUSED;
}
else
{
wxWindow* nextFocus = event.GetWindow();
m_iFlags &= ~(wxPG_FL_FOCUSED);
wxWindow* parent = nextFocus;
//wxLogDebug(wxT("KillFocus: %s"),parent->GetClassInfo()->GetClassName());
// This must be one of nextFocus' parents.
while ( parent )
{
if ( parent == this )
{
m_iFlags |= wxPG_FL_FOCUSED;
break;
}
parent = parent->GetParent();
}
}
if ( (m_iFlags & wxPG_FL_FOCUSED) !=
(oldFlags & wxPG_FL_FOCUSED) )
{
// On each focus kill, mark the next nav key event
// to be ignored (can't do on set focus since the
// event would occur before it).
if ( !(m_iFlags & wxPG_FL_FOCUSED) )
{
m_iFlags |= wxPG_FL_IGNORE_NEXT_NAVKEY;
// Need to store changed value
CommitChangesFromEditor();
}
else
{
/*
//
// Preliminary code for tab-order respecting
// tab-traversal (but should be moved to
// OnNav handler)
//
wxWindow* prevFocus = event.GetWindow();
wxWindow* useThis = this;
if ( m_iFlags & wxPG_FL_IN_MANAGER )
useThis = GetParent();
if ( prevFocus &&
prevFocus->GetParent() == useThis->GetParent() )
{
wxList& children = useThis->GetParent()->GetChildren();
wxNode* node = children.Find(prevFocus);
if ( node->GetNext() &&
useThis == node->GetNext()->GetData() )
DoSelectProperty(GetFirst());
else if ( node->GetPrevious () &&
useThis == node->GetPrevious()->GetData() )
DoSelectProperty(GetLastProperty());
}
*/
m_iFlags &= ~(wxPG_FL_IGNORE_NEXT_NAVKEY);
}
// Redraw selected
if ( m_selected && (m_iFlags & wxPG_FL_INITIALIZED) )
DrawItem( m_selected );
}
event.Skip();
#endif
}
void wxPropertyGrid::OnChildFocusEvent( wxChildFocusEvent& event )
{
HandleFocusChange((wxWindow*)event.GetEventObject());
event.Skip();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnScrollEvent( wxScrollWinEvent &event )
{
m_iFlags |= wxPG_FL_SCROLLED;
event.Skip();
}
// -----------------------------------------------------------------------
void wxPropertyGrid::OnCaptureChange( wxMouseCaptureChangedEvent& WXUNUSED(event) )
{
if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
{
#if __MOUSE_DEBUGGING__
wxLogDebug( wxT("wxPropertyGrid: mouse capture lost") );
#endif
m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED);
}
}
// -----------------------------------------------------------------------
// Property text-based storage
// -----------------------------------------------------------------------
#define wxPG_PROPERTY_FLAGS_COUNT 8
// property-flag-to-text array
static const wxChar* gs_property_flag_to_string[wxPG_PROPERTY_FLAGS_COUNT] =
{
wxT("Modified"),
wxT("Disabled"),
wxT("LowPriority"),
(const wxChar*) NULL, // wxPG_PROP_CUSTOMIMAGE is auto-generated flag
wxT("LimitedEditing"),
wxT("Unspecified"),
(const wxChar*) NULL, // Special flags cannot be stored as-is
(const wxChar*) NULL //
};
wxString wxPGProperty::GetAttributes( unsigned int flagmask )
{
wxASSERT(this);
wxString s;
unsigned int i;
unsigned int flags = ((unsigned int)m_flags) &
flagmask &
~(wxPG_PROP_CUSTOMIMAGE |
wxPG_PROP_CLASS_SPECIFIC_1 |
wxPG_PROP_CLASS_SPECIFIC_2);
if ( !flags )
return wxEmptyString;
for ( i=0; i<wxPG_PROPERTY_FLAGS_COUNT; i++ )
{
if ( flags & (1<<i) )
{
s.append( gs_property_flag_to_string[i] );
flags &= ~(1<<i);
if ( !flags )
break;
s.append(wxT(", "));
}
}
return s;
}
// -----------------------------------------------------------------------
void wxPGProperty::SetAttributes( const wxString& attributes )
{
wxASSERT(this);
size_t i;
WX_PG_TOKENIZER1_BEGIN(attributes,wxT(','))
for (i=0;i<wxPG_PROPERTY_FLAGS_COUNT;i++)
{
const wxChar* flagText = gs_property_flag_to_string[i];
if ( flagText && token == flagText )
{
m_flags |= ( 1<<i );
break;
}
}
WX_PG_TOKENIZER1_END()
}
// -----------------------------------------------------------------------
// Returns name of property without 'Property' at the end, and 'wx'
// in the beginning (if any).
wxString wxPropertyContainerMethods::GetPropertyShortClassName( wxPGId id )
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxEmptyString)
if ( p->GetParentingType() != 1 )
{
const wxChar* src = p->GetClassName();
wxString s;
if ( src[0] == wxT('w') && src[1] == wxT('x') )
s = &src[2];
else
s = src;
wxASSERT( (((int)s.length())-8) > 0 );
s.Truncate(s.length()-8);
//s.LowerCase();
return s;
}
return wxT("Category");
}
wxPGId wxPropertyContainerMethods::GetPropertyByNameA( wxPGPropNameStr name ) const
{
wxPGId id = GetPropertyByName(name);
wxASSERT_MSG(wxPGIdIsOk(id),wxString::Format(wxT("no property with name '%s'"),name.c_str()));
return id;
}
// ----------------------------------------------------------------------------
// VariantDatas
// ----------------------------------------------------------------------------
#if wxPG_PGVARIANT_IS_VARIANT
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataPoint, wxVariantData)
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataSize, wxVariantData)
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataArrayInt, wxVariantData)
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataLongLong, wxVariantData)
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataULongLong, wxVariantData)
#ifdef __WXPYTHON__
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataPyObject, wxVariantData)
#endif
#endif
// -----------------------------------------------------------------------
// Value type related methods (should all be pretty much static).
wxPGValueType::~wxPGValueType()
{
}
wxPG_CONST_WXCHAR_PTR wxPGValueType::GetCustomTypeName() const
{
return GetTypeName();
}
// Implement default types.
WX_PG_IMPLEMENT_VALUE_TYPE(wxString,wxStringProperty,wxPGTypeName_wxString,GetString,wxEmptyString)
WX_PG_IMPLEMENT_VALUE_TYPE(long,wxIntProperty,wxPGTypeName_long,GetLong,(long)0)
WX_PG_IMPLEMENT_VALUE_TYPE(double,wxFloatProperty,wxPGTypeName_double,GetDouble,0.0)
WX_PG_IMPLEMENT_VALUE_TYPE(wxArrayString,wxArrayStringProperty,wxPGTypeName_wxArrayString,GetArrayString,wxArrayString())
// Bool is a special case... thanks to the C++'s bool vs int vs long inconsistency issues.
const wxPGValueType *wxPGValueType_bool = (wxPGValueType *) NULL;
class wxPGValueTypeboolClass : public wxPGValueType
{
public:
virtual wxPG_CONST_WXCHAR_PTR GetTypeName() const { return wxPGTypeName_long; }
virtual wxPG_CONST_WXCHAR_PTR GetCustomTypeName() const { return wxPGTypeName_bool; }
virtual wxPGVariant GetDefaultValue() const { return wxPGVariant((long)0); }
virtual wxVariant GenerateVariant( wxPGVariant value, const wxString& name ) const
{ return wxVariant ( value.GetBool(), name ); }
virtual wxPGProperty* GenerateProperty( const wxString& label, const wxString& name ) const
{
return wxPG_NEWPROPERTY(Bool,label,name,false);
}
virtual void SetValueFromVariant( wxPGProperty* property, wxVariant& value ) const
{
#if defined(__WXDEBUG__) || defined(__WXPYTHON__)
wxCHECK_RET( wxStrcmp(wxPGTypeName_bool,value.GetType().c_str()) == 0,
wxT("SetValueFromVariant: wxVariant type mismatch.") );
#endif
property->DoSetValue(value.GetBool()?(long)1:(long)0);
}
};
// Implement nonetype.
const wxPGValueType *wxPGValueType_none = (wxPGValueType*) NULL;
class wxPGValueTypenoneClass : public wxPGValueType
{
public:
virtual wxPG_CONST_WXCHAR_PTR GetTypeName() const { return wxT("null"); }
virtual wxPGVariant GetDefaultValue() const { return wxPGVariant((long)0); }
virtual wxVariant GenerateVariant( wxPGVariant, const wxString& name ) const
{ return wxVariant( (long)0, name ); }
virtual wxPGProperty* GenerateProperty( const wxString&, const wxString& ) const
{ return (wxPGProperty*) NULL; }
virtual void SetValueFromVariant( wxPGProperty*, wxVariant& ) const
{ }
};
// Implement void* type.
const wxPGValueType *wxPGValueType_void = (wxPGValueType*) NULL;
class wxPGValueTypevoidClass : public wxPGValueType
{
public:
virtual wxPG_CONST_WXCHAR_PTR GetTypeName() const { return wxPGTypeName_void; }
virtual wxPGVariant GetDefaultValue() const { return wxPGVariant((void*)NULL); }
virtual wxVariant GenerateVariant( wxPGVariant value, const wxString& name ) const
{ return wxVariant( wxPGVariantToVoidPtr(value), name ); }
virtual wxPGProperty* GenerateProperty( const wxString&, const wxString& ) const
{ return (wxPGProperty*) NULL; }
virtual void SetValueFromVariant( wxPGProperty* property, wxVariant& value ) const
{
#if defined(__WXDEBUG__) || defined(__WXPYTHON__)
wxCHECK_RET( wxStrcmp(GetTypeName(),value.GetType().c_str()) == 0,
wxT("SetValueFromVariant: wxVariant type mismatch.") );
#endif
property->DoSetValue(value.GetVoidPtr());
}
};
#ifdef __WXPYTHON__
// Implement PyObject* type.
const wxPGValueType *wxPGValueType_PyObject = (wxPGValueType*) NULL;
class wxPGValueTypePyObjectClass : public wxPGValueType
{
public:
virtual wxPG_CONST_WXCHAR_PTR GetTypeName() const { return wxT("PyObject"); }
virtual wxPGVariant GetDefaultValue() const
{
return wxVariant( new wxPGVariantDataPyObject(Py_None) );
}
virtual wxVariant GenerateVariant( wxPGVariant value, const wxString& name ) const
{
value.SetName( name );
return value; // Can be done since under wxPython, wxPGVariant is wxVariant
}
virtual wxPGProperty* GenerateProperty( const wxString&, const wxString& ) const
{
return (wxPGProperty*) NULL;
}
virtual void SetValueFromVariant( wxPGProperty* property, wxVariant& value ) const
{
#if defined(__WXDEBUG__) || defined(__WXPYTHON__)
wxCHECK_RET( wxStrcmp(GetTypeName(),value.GetType().c_str()) == 0,
wxT("SetValueFromVariant: wxVariant type mismatch.") );
#endif
property->DoSetValue(value);
}
};
#endif // __WXPYTHON__
// Registers all default value types
void wxPropertyGrid::RegisterDefaultValues()
{
wxPGRegisterDefaultValueType( none );
wxPGRegisterDefaultValueType( wxString );
wxPGRegisterDefaultValueType( long );
wxPGRegisterDefaultValueType( bool );
wxPGRegisterDefaultValueType( double );
wxPGRegisterDefaultValueType( void );
wxPGRegisterDefaultValueType( wxArrayString );
#ifdef __WXPYTHON__
wxPGRegisterDefaultValueType( PyObject );
#endif
}
// noDefCheck = true prevents infinite recursion.
wxPGValueType* wxPropertyGrid::RegisterValueType( wxPGValueType* valueclass, bool noDefCheck, const wxString& className )
{
wxASSERT( valueclass );
WX_PG_GLOBALS_LOCKER()
if ( !noDefCheck && wxPGGlobalVars->m_dictValueType.empty() )
RegisterDefaultValues();
wxString temp_str;
wxPG_CONST_WXCHAR_PTR name_ = valueclass->GetType();
const wxChar* name = wxPG_TO_WXCHAR_PTR(name_);
wxPGValueType* p_at_slot = (wxPGValueType*) wxPGGlobalVars->m_dictValueType[name];
if ( !p_at_slot )
{
wxPGGlobalVars->m_dictValueType[name] = (void*) valueclass;
#if wxPG_VALUETYPE_IS_STRING
wxPGGlobalVars->m_dictValueTypeByClass[className] = (void*) valueclass;
#else
wxUnusedVar(className);
#endif
return valueclass;
}
// Delete given object instance, but only if it wasn't the same as in the hashmap.
if ( p_at_slot != valueclass )
{
delete valueclass;
}
return p_at_slot;
}
/*
* wxPGVariantDataWxObj
*/
//IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataWxObj, wxVariantData)
wxPGVariantDataWxObj::wxPGVariantDataWxObj()
: wxVariantData()
{
}
wxPGVariantDataWxObj::~wxPGVariantDataWxObj()
{
}
#if wxUSE_STD_IOSTREAM
bool wxPGVariantDataWxObj::Write(wxSTD ostream&) const
{
// Not implemented
return true;
}
#endif
bool wxPGVariantDataWxObj::Write(wxString&) const
{
// Not implemented
return true;
}
#if wxUSE_STD_IOSTREAM
bool wxPGVariantDataWxObj::Read(wxSTD istream& WXUNUSED(str))
{
// Not implemented
return false;
}
#endif
bool wxPGVariantDataWxObj::Read(wxString& WXUNUSED(str))
{
// Not implemented
return false;
}
// -----------------------------------------------------------------------
// Editor class specific.
// noDefCheck = true prevents infinite recursion.
wxPGEditor* wxPropertyGrid::RegisterEditorClass( wxPGEditor* editorclass,
const wxString& name,
bool noDefCheck )
{
wxASSERT( editorclass );
WX_PG_GLOBALS_LOCKER()
if ( !noDefCheck && wxPGGlobalVars->m_mapEditorClasses.empty() )
RegisterDefaultEditors();
wxPGGlobalVars->m_mapEditorClasses[name] = (void*)editorclass;
return editorclass;
}
// Registers all default editor classes
void wxPropertyGrid::RegisterDefaultEditors()
{
wxPGRegisterDefaultEditorClass( TextCtrl );
wxPGRegisterDefaultEditorClass( Choice );
wxPGRegisterDefaultEditorClass( ComboBox );
wxPGRegisterDefaultEditorClass( TextCtrlAndButton );
#if wxPG_INCLUDE_CHECKBOX
wxPGRegisterDefaultEditorClass( CheckBox );
#endif
wxPGRegisterDefaultEditorClass( ChoiceAndButton );
// Register SpinCtrl etc. editors before use
RegisterAdditionalEditors();
}
wxPGEditor* wxPropertyContainerMethods::GetEditorByName( const wxString& editor_name )
{
wxPGEditor* editor = (wxPGEditor*) wxPGGlobalVars->m_mapEditorClasses[editor_name];
wxASSERT_MSG( editor,
wxT("unregistered editor name") );
return editor;
}
// -----------------------------------------------------------------------
// wxPGStringTokenizer
// Needed to handle C-style string lists (e.g. "str1" "str2")
// -----------------------------------------------------------------------
wxPGStringTokenizer::wxPGStringTokenizer( const wxString& str, wxChar delimeter )
: m_str(&str), m_curPos(str.begin()), m_delimeter(delimeter)
{
}
wxPGStringTokenizer::~wxPGStringTokenizer()
{
}
bool wxPGStringTokenizer::HasMoreTokens()
{
const wxString& str = *m_str;
//wxASSERT_MSG( m_curPos != str.end(), wxT("Do not call wxPGStringTokenizer methods after HasMoreTokens has returned false."));
wxString::const_iterator i = m_curPos;
wxUniChar delim = m_delimeter;
wxUniChar a;
wxUniChar prev_a = wxT('\0');
bool inToken = false;
while ( i != str.end() )
{
a = wxPGGetIterChar(str, i);
if ( !inToken )
{
if ( a == delim )
{
inToken = true;
m_readyToken.clear();
}
}
else
{
if ( prev_a != wxT('\\') )
{
if ( a != delim )
{
if ( a != wxT('\\') )
m_readyToken << a;
}
else
{
//wxLogDebug(m_readyToken);
i++;
m_curPos = i;
return true;
}
prev_a = a;
}
else
{
m_readyToken << a;
prev_a = wxT('\0');
}
}
i++;
}
m_curPos = str.end();
if ( inToken )
return true;
return false;
/*
const wxChar* ptr = m_curPos;
const wxChar* ptr_end = &m_str->c_str()[m_str->length()];
size_t store_index = 0xFFFFFFFF;
#if !wxUSE_STL
wxChar* store_ptr_base = (wxChar*) NULL;
#endif
wxChar delim = m_delimeter;
wxChar a = *ptr;
wxChar prev_a = 0;
while ( a )
{
if ( store_index == 0xFFFFFFFF )
{
if ( a == delim )
{
size_t req_len = ptr_end-ptr+1;
#if wxUSE_STL
if ( m_readyToken.length() < req_len )
m_readyToken.resize( req_len, wxT(' ') );
#else
store_ptr_base = m_readyToken.GetWriteBuf( req_len );
#endif
store_index = 0;
prev_a = 0;
}
}
else
{
if ( prev_a != wxT('\\') )
{
if ( a != delim )
{
if ( a != wxT('\\') )
{
#if wxUSE_STL
m_readyToken[store_index] = a;
#else
store_ptr_base[store_index] = a;
#endif
store_index++;
}
}
else
{
#if wxUSE_STL
m_readyToken[store_index] = 0;
m_readyToken.resize(store_index,wxT(' '));
#else
store_ptr_base[store_index] = 0;
m_readyToken.UngetWriteBuf( store_index );
#endif
m_curPos = ptr+1;
return true;
}
prev_a = a;
}
else
{
#if wxUSE_STL
m_readyToken[store_index] = a;
#else
store_ptr_base[store_index] = a;
#endif
store_index++;
prev_a = 0;
}
}
ptr++;
a = *ptr;
}
#if !wxUSE_STL
if ( store_index != 0xFFFFFFFF )
m_readyToken.UngetWriteBuf( store_index );
#endif
m_curPos = (const wxChar*) NULL;
return false;
*/
}
wxString wxPGStringTokenizer::GetNextToken()
{
//wxASSERT_MSG( m_curPos != m_str->end(), wxT("Do not call wxPGStringTokenizer methods after HasMoreTokens has returned false."));
return m_readyToken;
}
// -----------------------------------------------------------------------
// wxPGChoicesData
// -----------------------------------------------------------------------
wxPGChoicesData::wxPGChoicesData()
{
m_refCount = 1;
}
wxPGChoicesData::~wxPGChoicesData()
{
}
// -----------------------------------------------------------------------
// wxPGChoices
// -----------------------------------------------------------------------
void wxPGChoices::Add( const wxChar* label, int value )
{
EnsureData();
if ( value != wxPG_INVALID_VALUE && m_data->m_arrLabels.GetCount() == m_data->m_arrValues.GetCount() )
m_data->m_arrValues.Add( value );
else if ( m_data->m_arrValues.GetCount() > 0 )
m_data->m_arrValues.Add( 0 );
m_data->m_arrLabels.Add ( label );
}
// -----------------------------------------------------------------------
#if wxCHECK_VERSION(2,9,0)
void wxPGChoices::Insert( const wxString& label, int index, int value )
#else
void wxPGChoices::Insert( const wxChar* label, int index, int value )
#endif
{
EnsureData();
if ( value != wxPG_INVALID_VALUE && m_data->m_arrLabels.GetCount() == m_data->m_arrValues.GetCount() )
m_data->m_arrValues.Insert( value, index );
else if ( m_data->m_arrValues.GetCount() > 0 )
m_data->m_arrValues.Insert( 0, index );
m_data->m_arrLabels.Insert( label, index );
}
// -----------------------------------------------------------------------
void wxPGChoices::AddAsSorted( const wxString& label, int value )
{
//wxASSERT_MSG( IsOk(),
// wxT("do not add items to invalid wxPGChoices") );
EnsureData();
size_t index = 0;
wxArrayString& labels = m_data->m_arrLabels;
wxArrayInt& values = m_data->m_arrValues;
while ( index < labels.GetCount() )
{
int cmpRes = labels[index].Cmp(label);
if ( cmpRes > 0 )
break;
index++;
}
if ( value != wxPG_INVALID_VALUE &&
labels.GetCount() == values.GetCount() )
values.Insert ( value, index );
labels.Insert ( label, index );
}
// -----------------------------------------------------------------------
void wxPGChoices::Add( const wxChar** labels, const long* values )
{
//wxASSERT_MSG( IsOk(),
// wxT("do not add items to invalid wxPGChoices") );
EnsureData();
unsigned int itemcount = 0;
const wxChar** p = &labels[0];
while ( *p ) { p++; itemcount++; }
wxArrayString& i_labels = m_data->m_arrLabels;
wxArrayInt& i_values = m_data->m_arrValues;
unsigned int i;
for ( i = 0; i < itemcount; i++ )
{
i_labels.Add ( labels[i] );
}
if ( values )
{
for ( i = 0; i < itemcount; i++ )
{
i_values.Add ( values[i] );
}
}
}
// -----------------------------------------------------------------------
void wxPGChoices::Add( const wxArrayString& arr, const long* values )
{
//wxASSERT_MSG( IsOk(),
// wxT("do not add items to invalid wxPGChoices") );
EnsureData();
wxArrayString& labels = m_data->m_arrLabels;
wxArrayInt& i_values = m_data->m_arrValues;
unsigned int i;
unsigned int itemcount = arr.GetCount();
for ( i = 0; i < itemcount; i++ )
{
labels.Add ( arr[i] );
}
if ( values )
{
for ( i = 0; i < itemcount; i++ )
i_values.Add ( values[i] );
}
}
// -----------------------------------------------------------------------
void wxPGChoices::Add( const wxArrayString& arr, const wxArrayInt& arrint )
{
//wxASSERT_MSG( IsOk(),
// wxT("do not add items to invalid wxPGChoices") );
EnsureData();
wxArrayString& labels = m_data->m_arrLabels;
wxArrayInt& values = m_data->m_arrValues;
unsigned int i;
unsigned int itemcount = arr.GetCount();
for ( i = 0; i < itemcount; i++ )
{
labels.Add ( arr[i] );
}
if ( &arrint && arrint.GetCount() )
for ( i = 0; i < itemcount; i++ )
{
values.Add ( arrint[i] );
}
}
// -----------------------------------------------------------------------
void wxPGChoices::AssignData( wxPGChoicesData* data )
{
Free();
if ( data != wxPGChoicesEmptyData )
{
m_data = data;
data->m_refCount++;
}
}
// -----------------------------------------------------------------------
void wxPGChoices::Init()
{
m_data = wxPGChoicesEmptyData;
}
// -----------------------------------------------------------------------
void wxPGChoices::Free()
{
if ( m_data != wxPGChoicesEmptyData )
{
m_data->m_refCount--;
if ( m_data->m_refCount < 1 )
delete m_data;
m_data = wxPGChoicesEmptyData;
}
}
// -----------------------------------------------------------------------
// wxPropertyGridEvent
// -----------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxPropertyGridEvent, wxCommandEvent)
DEFINE_EVENT_TYPE( wxEVT_PG_SELECTED )
DEFINE_EVENT_TYPE( wxEVT_PG_CHANGED )
DEFINE_EVENT_TYPE( wxEVT_PG_HIGHLIGHTED )
DEFINE_EVENT_TYPE( wxEVT_PG_RIGHT_CLICK )
DEFINE_EVENT_TYPE( wxEVT_PG_PAGE_CHANGED )
DEFINE_EVENT_TYPE( wxEVT_PG_ITEM_EXPANDED )
DEFINE_EVENT_TYPE( wxEVT_PG_ITEM_COLLAPSED )
DEFINE_EVENT_TYPE( wxEVT_PG_DOUBLE_CLICK )
DEFINE_EVENT_TYPE( wxEVT_PG_COMPACT_MODE_ENTERED )
DEFINE_EVENT_TYPE( wxEVT_PG_EXPANDED_MODE_ENTERED )
wxPropertyGridEvent::wxPropertyGridEvent(wxEventType commandType, int id)
: wxCommandEvent(commandType,id)
{
m_property = NULL;
m_pending = false;
}
// -----------------------------------------------------------------------
wxPropertyGridEvent::wxPropertyGridEvent(const wxPropertyGridEvent& event)
: wxCommandEvent(event)
{
m_eventType = event.GetEventType();
m_eventObject = event.m_eventObject;
m_pg = event.m_pg;
m_property = event.m_property;
m_pending = false;
}
// -----------------------------------------------------------------------
wxPropertyGridEvent::~wxPropertyGridEvent()
{
}
// -----------------------------------------------------------------------
wxEvent* wxPropertyGridEvent::Clone() const
{
return new wxPropertyGridEvent( *this );
}
// -----------------------------------------------------------------------
// wxPropertyContainerMethods
// - common methods for wxPropertyGrid and wxPropertyGridManager -
// -----------------------------------------------------------------------
void wxPropertyContainerMethods::DoSetPropertyAttribute( wxPGId id, int attrid,
wxVariant& value, long argFlags )
{
wxPG_PROP_ID_CALL_PROLOG()
p->SetAttribute(attrid,value);
if ( ( argFlags & wxPG_RECURSE ) && p->GetParentingType() != 0 )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
size_t i;
for ( i = 0; i < pwc->GetCount(); i++ )
DoSetPropertyAttribute(pwc->Item(i),attrid,value,argFlags);
}
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetPropertyAttributeAll( int attrid, wxVariant value )
{
DoSetPropertyAttribute(GetRoot(),attrid,value,wxPG_RECURSE);
}
// -----------------------------------------------------------------------
void wxPropertyContainerMethods::SetBoolChoices( const wxChar* true_choice,
const wxChar* false_choice )
{
WX_PG_GLOBALS_LOCKER()
wxPGGlobalVars->m_boolChoices[0] = false_choice;
wxPGGlobalVars->m_boolChoices[1] = true_choice;
}
// -----------------------------------------------------------------------
wxPGChoices gs_emptyChoices;
wxPGChoices& wxPropertyContainerMethods::GetPropertyChoices( wxPGId id )
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(gs_emptyChoices)
wxPGChoiceInfo ci;
ci.m_choices = (wxPGChoices*) NULL;
p->GetChoiceInfo(&ci);
if ( !ci.m_choices )
return gs_emptyChoices;
return *ci.m_choices;
}
// -----------------------------------------------------------------------
wxPGChoices& wxPropertyContainerMethods::GetPropertyChoices( wxPGPropNameStr name )
{
wxPG_PROP_NAME_CALL_PROLOG_RETVAL(gs_emptyChoices)
return GetPropertyChoices(wxPGIdGen(p));
}
// -----------------------------------------------------------------------
wxPGId wxPropertyContainerMethods::DoGetPropertyByName( wxPGPropNameStr name ) const
{
return m_pState->BaseGetPropertyByName(name);
}
// -----------------------------------------------------------------------
wxPGId wxPropertyContainerMethods::GetPropertyByName( wxPGPropNameStr name,
wxPGPropNameStr subname ) const
{
wxPGId id = DoGetPropertyByName(name);
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*) wxPGIdToPtr(id);
if ( !pwc || !pwc->GetParentingType() )
return wxNullProperty;
return wxPGIdGen(pwc->GetPropertyByName(subname));
}
// -----------------------------------------------------------------------
// Since GetPropertyByName is used *a lot*, this makes sense
// since non-virtual method can be called with less code.
wxPGId wxPropertyContainerMethods::GetPropertyByName( wxPGPropNameStr name ) const
{
wxPGId id = DoGetPropertyByName(name);
if ( wxPGIdIsOk(id) )
return id;
// Check if its "Property.SubProperty" format
int pos = name.Find(wxT('.'));
if ( pos <= 0 )
return id;
return GetPropertyByName(name.substr(0,pos),
name.substr(pos+1,name.length()-pos-1));
}
// -----------------------------------------------------------------------
bool wxPropertyContainerMethods::HideProperty( wxPGId id, bool hide )
{
// Hiding properties requires that we are always in the compact mode
m_pState->GetGrid()->Compact(true);
return SetPropertyPriority(id,hide?wxPG_LOW:wxPG_HIGH);
}
// -----------------------------------------------------------------------
// Used by HideProperty as well
bool wxPropertyContainerMethods::SetPropertyPriority( wxPGId id, int priority )
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
wxPropertyGrid* pg = m_pState->GetGrid();
if ( pg == p->GetGrid() )
return pg->SetPropertyPriority(p,priority);
else
m_pState->SetPropertyPriority(p,priority);
return true;
}
// -----------------------------------------------------------------------
bool wxPropertyContainerMethods::SetPropertyMaxLength( wxPGId id, int maxLen )
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
wxPropertyGrid* pg = m_pState->GetGrid();
p->m_maxLen = (short) maxLen;
// Adjust control if selected currently
if ( pg == p->GetGrid() && p == m_pState->GetSelection() )
{
wxWindow* wnd = pg->GetEditorControl();
wxTextCtrl* tc = wxDynamicCast(wnd,wxTextCtrl);
if ( tc )
tc->SetMaxLength( maxLen );
else
// Not a text ctrl
return false;
}
return true;
}
// -----------------------------------------------------------------------
// GetPropertyValueAsXXX methods
#define IMPLEMENT_GET_VALUE(T,TRET,BIGNAME,DEFRETVAL) \
TRET wxPropertyContainerMethods::GetPropertyValueAs##BIGNAME( wxPGId id ) wxPG_GETVALUE_CONST \
{ \
wxPG_PROP_ID_CALL_PROLOG_RETVAL(DEFRETVAL) \
if ( p->GetValueTypePtr()->GetTypeName() != wxPGTypeName_##T ) \
{ \
wxPGGetFailed(p,wxPGTypeName_##T); \
return (TRET)DEFRETVAL; \
} \
return (TRET)wxPGVariantTo##BIGNAME(p->DoGetValue()); \
}
// String is different than others.
wxString wxPropertyContainerMethods::GetPropertyValueAsString( wxPGId id ) wxPG_GETVALUE_CONST
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxEmptyString)
return p->GetValueAsString(wxPG_FULL_VALUE);
}
IMPLEMENT_GET_VALUE(long,long,Long,0)
IMPLEMENT_GET_VALUE(long,bool,Bool,false)
IMPLEMENT_GET_VALUE(double,double,Double,0.0)
IMPLEMENT_GET_VALUE(void,void*,VoidPtr,NULL)
#ifdef __WXPYTHON__
IMPLEMENT_GET_VALUE(PyObject,PyObject*,PyObject,Py_None)
#endif
#if !wxPG_PGVARIANT_IS_VARIANT
IMPLEMENT_GET_VALUE(wxArrayString,const wxArrayString&,ArrayString,*((wxArrayString*)NULL))
#endif
// wxObject is different than others.
const wxObject* wxPropertyContainerMethods::GetPropertyValueAsWxObjectPtr( wxPGId id ) wxPG_GETVALUE_CONST
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL((const wxObject*)NULL)
wxPG_CONST_WXCHAR_PTR typestr = p->GetValueTypePtr()->GetTypeName();
if ( typestr[0] != wxT('w') || typestr[1] != wxT('x') )
{
wxPGGetFailed(p,wxT("wxObject"));
return (const wxObject*) NULL;
}
return (const wxObject*)wxPGVariantGetWxObjectPtr(p->DoGetValue());
}
// -----------------------------------------------------------------------
bool wxPropertyContainerMethods::IsPropertyExpanded( wxPGId id )
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
if ( pwc->GetParentingType() == 0 )
return false;
return pwc->IsExpanded();
}
// -----------------------------------------------------------------------
// returns value type class for type name
wxPGValueType* wxPropertyContainerMethods::GetValueType(const wxString &type)
{
wxPGHashMapS2P::iterator it;
it = wxPGGlobalVars->m_dictValueType.find(type);
if ( it != wxPGGlobalVars->m_dictValueType.end() )
return (wxPGValueType*) it->second;
return (wxPGValueType*) NULL;
}
// -----------------------------------------------------------------------
#if wxPG_VALUETYPE_IS_STRING
wxPGValueType* wxPropertyContainerMethods::GetValueTypeByName(const wxString &className)
{
wxPGHashMapS2P::iterator it;
it = wxPGGlobalVars->m_dictValueTypeByClass.find(className);
if ( it != wxPGGlobalVars->m_dictValueTypeByClass.end() )
return (wxPGValueType*) it->second;
return (wxPGValueType*) NULL;
}
#endif
// -----------------------------------------------------------------------
wxPGProperty* wxPropertyContainerMethods::CreatePropertyByType(const wxString &valuetype,
const wxString &label,
const wxString &name)
{
wxPGHashMapS2P::iterator it;
it = wxPGGlobalVars->m_dictValueType.find(valuetype);
if ( it != wxPGGlobalVars->m_dictValueType.end() )
{
wxPGValueType* vt = (wxPGValueType*) it->second;
wxPGProperty* p = vt->GenerateProperty(label,name);
#ifdef __WXDEBUG__
if ( !p )
{
wxLogDebug(wxT("WARNING: CreatePropertyByValueType generated NULL property for ValueType \"%s\""),valuetype.c_str());
return (wxPGProperty*) NULL;
}
#endif
return p;
}
wxLogDebug(wxT("WARNING: No value type registered with name \"%s\""),valuetype.c_str());
return (wxPGProperty*) NULL;
}
// -----------------------------------------------------------------------
wxPGProperty* wxPropertyContainerMethods::CreatePropertyByClass(const wxString &classname,
const wxString &label,
const wxString &name)
{
wxPGHashMapS2P* cis =
(wxPGHashMapS2P*) &wxPGGlobalVars->m_dictPropertyClassInfo;
const wxString* pClassname = &classname;
wxString s;
// Translate to long name, if necessary
if ( (pClassname->GetChar(0) != wxT('w') || pClassname->GetChar(1) != wxT('x')) &&
pClassname->Find(wxT("Property")) < 0 )
{
if ( classname != wxT("Category") )
s.Printf(wxT("wx%sProperty"),pClassname->c_str());
else
s = wxT("wxPropertyCategory");
pClassname = &s;
}
wxPGHashMapS2P::iterator it;
it = cis->find(*pClassname);
if ( it != cis->end() )
{
wxPGPropertyClassInfo* pci = (wxPGPropertyClassInfo*) it->second;
wxPGProperty* p = pci->m_constructor(label,name);
return p;
}
wxLogError(wxT("No such property class: %s"),pClassname->c_str());
return (wxPGProperty*) NULL;
}
// -----------------------------------------------------------------------
// lazy way to prevent RegisterPropertyClass infinite recursion
static int gs_registering_standard_props = 0;
bool wxPropertyContainerMethods::RegisterPropertyClass( const wxChar* name,
wxPGPropertyClassInfo* classinfo )
{
WX_PG_GLOBALS_LOCKER()
// Standard classes must be registered first!
if ( !gs_registering_standard_props &&
wxPGGlobalVars->m_dictPropertyClassInfo.empty()
)
wxPGRegisterStandardPropertyClasses();
wxPGHashMapS2P::iterator it;
it = wxPGGlobalVars->m_dictPropertyClassInfo.find(name);
// only register if not registered already
if ( it == wxPGGlobalVars->m_dictPropertyClassInfo.end() )
{
wxPGGlobalVars->m_dictPropertyClassInfo[name] = classinfo;
return true;
}
wxLogDebug(wxT("WARNING: Property class named \"%s\" was already registered."),name);
return false;
}
// -----------------------------------------------------------------------
static void wxPGRegisterStandardPropertyClasses()
{
if ( gs_registering_standard_props )
return;
gs_registering_standard_props = 1; // no need to reset this
wxPGRegisterPropertyClass(wxStringProperty);
wxPGRegisterPropertyClass(wxIntProperty);
wxPGRegisterPropertyClass(wxUIntProperty);
wxPGRegisterPropertyClass(wxFloatProperty);
wxPGRegisterPropertyClass(wxBoolProperty);
wxPGRegisterPropertyClass(wxEnumProperty);
wxPGRegisterPropertyClass(wxFlagsProperty);
wxPGRegisterPropertyClass(wxLongStringProperty);
wxPGRegisterPropertyClass(wxPropertyCategory);
wxPGRegisterPropertyClass(wxParentProperty);
wxPGRegisterPropertyClass(wxCustomProperty);
// TODO: Are these really "standard" ?
wxPGRegisterPropertyClass(wxArrayStringProperty);
wxPGRegisterPropertyClass(wxFileProperty);
wxPGRegisterPropertyClass(wxDirProperty);
#ifdef __WXPYTHON__
wxPropertyContainerMethods::RegisterAdvancedPropertyClasses();
#endif
}
// -----------------------------------------------------------------------
// wxPropertyGridState
// -----------------------------------------------------------------------
// reset helper macro
#undef FROM_STATE
#define FROM_STATE(A) A
// -----------------------------------------------------------------------
// wxPropertyGridState item iteration methods
// -----------------------------------------------------------------------
// Skips categories and sub-properties (unless in wxCustomProperty/wxParentProperty).
wxPGId wxPropertyGridState::GetFirstProperty() const
{
if ( !m_properties->GetCount() ) return wxPGIdGen((wxPGProperty*)NULL);
wxPGProperty* p = m_properties->Item(0);
int parenting = p->GetParentingType();
if ( parenting > 0 )
return GetNextProperty ( wxPGIdGen(p) );
return wxPGIdGen(p);
}
// -----------------------------------------------------------------------
// Skips categories and sub-properties (unless in wxParentProperty).
wxPGId wxPropertyGridState::GetNextProperty( wxPGId id ) const
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
// Go with first child?
int parenting = pwc->GetParentingType();
if ( parenting == 0 || parenting == -1 || !pwc->GetCount() )
{
// No...
wxPGPropertyWithChildren* parent = pwc->m_parent;
// As long as last item, go up and get parent' sibling
while ( pwc->m_arrIndex >= (parent->GetCount()-1) )
{
pwc = parent;
if ( pwc == m_properties ) return wxPGIdGen((wxPGProperty*)NULL);
parent = parent->m_parent;
}
pwc = (wxPGPropertyWithChildren*)parent->Item(pwc->m_arrIndex+1);
// Go with the next sibling of parent's parent?
}
else
{
// Yes...
pwc = (wxPGPropertyWithChildren*)pwc->Item(0);
}
// If it's category or parentproperty, then go recursive
parenting = pwc->GetParentingType();
if ( parenting > PT_NONE )
return GetNextProperty( wxPGIdGen(pwc) );
return wxPGIdGen(pwc);
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGridState::GetNextSibling( wxPGId id )
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
wxPGPropertyWithChildren* parent = p->m_parent;
size_t next_ind = p->m_arrIndex + 1;
if ( next_ind >= parent->GetCount() ) return wxPGIdGen((wxPGProperty*)NULL);
return wxPGIdGen(parent->Item(next_ind));
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGridState::GetPrevSibling( wxPGId id )
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
size_t ind = p->m_arrIndex;
if ( ind < 1 ) return wxPGIdGen((wxPGProperty*)NULL);
return wxPGIdGen(p->m_parent->Item(ind-1));
}
// -----------------------------------------------------------------------
// Skips categories and sub-properties (unless in wxParentProperty).
wxPGId wxPropertyGridState::GetPrevProperty( wxPGId id ) const
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
wxPGPropertyWithChildren* p2 = (wxPGPropertyWithChildren*) p;
wxPGPropertyWithChildren* parent = p2->m_parent;
// Is there a previous sibling?
if ( p2->m_arrIndex > 0 )
{
// There is!
p2 = (wxPGPropertyWithChildren*)parent->Item ( p2->m_arrIndex-1 );
int parenting = p2->GetParentingType();
// Do we return it's last child?
while ( (parenting > 0 || parenting == PT_CUSTOMPROPERTY) && p2->GetCount() )
{
p2 = (wxPGPropertyWithChildren*)p2->Last();
parenting = p2->GetParentingType();
}
}
else if ( parent != m_properties )
// Return parent if it isnt' the root
p2 = parent;
else
return wxPGIdGen((wxPGProperty*)NULL);
// Skip category and parentproperty.
int parenting = p2->GetParentingType();
if ( parenting > PT_NONE )
return GetPrevProperty ( wxPGIdGen(p2) );
return wxPGIdGen(p2);
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGridState::GetFirstCategory() const
{
//if ( IsInNonCatMode() )
// return wxPGIdGen((wxPGProperty*)NULL);
wxPGProperty* found = (wxPGProperty*)NULL;
size_t i;
for ( i=0; i<m_regularArray.GetCount(); i++ )
{
wxPGProperty* p = m_regularArray.Item(i);
if ( p->GetParentingType() > 0 )
{
found = p;
break;
}
}
return wxPGIdGen(found);
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGridState::GetNextCategory( wxPGId id ) const
{
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
wxPGPropertyWithChildren* current = (wxPGPropertyWithChildren*)p;
wxCHECK_MSG( !IsInNonCatMode() || current->GetParentingType() == 1, wxPGIdGen((wxPGProperty*)NULL),
wxT("GetNextCategory should not be called with non-category argument in non-categoric mode.") );
wxPGPropertyWithChildren* parent = current->m_parent;
wxPGProperty* found = (wxPGProperty*) NULL;
size_t i;
// Find sub-category, if any.
if ( current->GetParentingType() > 0 )
{
// Find first sub-category in current's array.
for ( i = 0; i<current->GetCount(); i++ )
{
wxPGProperty* p = current->Item(i);
if ( p->GetParentingType() > 0 )
{
found = p;
break;
}
}
if ( found )
return wxPGIdGen(found);
}
// Find next category in parent's array.
// (and go up in hierarchy until one found or
// top is reached).
do
{
for ( i = current->m_arrIndex+1; i<parent->GetCount(); i++ )
{
wxPGProperty* p = parent->Item(i);
if ( p->GetParentingType() > 0 )
{
found = p;
break;
}
}
current = parent;
parent = parent->m_parent;
} while ( !found && parent );
return wxPGIdGen(found);
}
// -----------------------------------------------------------------------
// wxPropertyGridState GetPropertyXXX methods
// -----------------------------------------------------------------------
wxPGId wxPropertyGridState::GetPropertyByLabel( const wxString& label,
wxPGPropertyWithChildren* parent ) const
{
size_t i;
if ( !parent ) parent = (wxPGPropertyWithChildren*) &m_regularArray;
for ( i=0; i<parent->GetCount(); i++ )
{
wxPGProperty* p = parent->Item(i);
if ( p->m_label == label )
return wxPGIdGen(p);
// Check children recursively.
if ( p->GetParentingType() != 0 )
{
p = wxPGIdToPtr(GetPropertyByLabel(label,(wxPGPropertyWithChildren*)p));
if ( p )
return wxPGIdGen(p);
}
}
return wxPGIdGen((wxPGProperty*) NULL);
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGridState::BaseGetPropertyByName( wxPGPropNameStr name ) const
{
wxPGHashMapS2P::const_iterator it;
it = m_dictName.find(name);
if ( it != m_dictName.end() )
return wxPGIdGen( (wxPGProperty*) it->second );
return wxPGIdGen( (wxPGProperty*) NULL );
}
// -----------------------------------------------------------------------
// wxPropertyGridState global operations
// -----------------------------------------------------------------------
bool wxPropertyGridState::EnableCategories( bool enable )
{
ITEM_ITERATION_VARIABLES
if ( enable )
{
//
// Enable categories
//
if ( !IsInNonCatMode() )
return false;
m_properties = &m_regularArray;
// fix parents, indexes, and depths
ITEM_ITERATION_INIT_FROM_THE_TOP
ITEM_ITERATION_LOOP_BEGIN
p->m_arrIndex = i;
p->m_parent = parent;
// If parent was category, and this is not,
// then the depth stays the same.
if ( parent->GetParentingType() == 1 &&
p->GetParentingType() <= 0 )
p->m_depth = parent->m_depth;
else
p->m_depth = parent->m_depth + 1;
ITEM_ITERATION_LOOP_END
}
else
{
//
// Disable categories
//
if ( IsInNonCatMode() )
return false;
// Create array, if necessary.
if ( !m_abcArray )
InitNonCatMode();
m_properties = m_abcArray;
// fix parents, indexes, and depths
ITEM_ITERATION_INIT_FROM_THE_TOP
//ITEM_ITERATION_DCAE_ISP_LOOP_BEGIN
ITEM_ITERATION_DCAE_LOOP_BEGIN
p->m_arrIndex = i;
p->m_parent = parent;
p->m_depth = parent->m_depth + 1;
//ITEM_ITERATION_DCAE_ISP_LOOP_END
ITEM_ITERATION_DCAE_LOOP_END
}
return true;
}
// -----------------------------------------------------------------------
static int wxPG_SortFunc(void **p1, void **p2)
{
wxPGProperty *pp1 = *((wxPGProperty**)p1);
wxPGProperty *pp2 = *((wxPGProperty**)p2);
return pp1->GetLabel().compare( pp2->GetLabel() );
}
void wxPropertyGridState::Sort( wxPGProperty* p )
{
if ( !p )
p = (wxPGProperty*)m_properties;
wxCHECK_RET( p->GetParentingType() != 0,
wxT("cannot sort non-parenting property") );
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
// Can only sort items with children
if ( pwc->m_children.GetCount() < 1 )
return;
pwc->m_children.Sort( wxPG_SortFunc );
// Fix indexes
pwc->FixIndexesOfChildren();
}
// -----------------------------------------------------------------------
void wxPropertyGridState::Sort()
{
Sort( m_properties );
// Sort categories as well
if ( !IsInNonCatMode() )
{
size_t i;
for ( i=0;i<m_properties->GetCount();i++)
{
wxPGProperty* p = m_properties->Item(i);
if ( p->GetParentingType() > 0 )
Sort ( p );
}
}
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::ExpandAll( unsigned char doExpand )
{
ITEM_ITERATION_DCAE_VARIABLES
bool isGrid = m_pPropGrid->GetState() == this;
if ( isGrid &&
m_selected &&
m_selected->GetParent() != m_properties )
{
if ( !m_pPropGrid->ClearSelection() )
return false;
}
if ( !doExpand )
{
if ( isGrid )
{
if ( !m_pPropGrid->ClearSelection() )
return false;
}
else m_selected = (wxPGProperty*) NULL;
}
ITEM_ITERATION_INIT_FROM_THE_TOP
ITEM_ITERATION_DCAE_LOOP_BEGIN
if ( parenting != 0 )
((wxPGPropertyWithChildren*)p)->m_expanded = doExpand;
ITEM_ITERATION_DCAE_LOOP_END
if ( m_pPropGrid->GetState() == this )
{
m_pPropGrid->CalculateYs((wxPGPropertyWithChildren*)NULL,-1);
m_pPropGrid->RedrawAllVisible();
}
return true;
}
// -----------------------------------------------------------------------
// Used by SetSplitterLeft
int wxPropertyGridState::GetLeftSplitterPos(wxClientDC& dc,
wxPGPropertyWithChildren* pwc,
bool subProps)
{
wxPropertyGrid* pg = m_pPropGrid;
size_t i;
int maxW = 0;
int w, h;
for ( i=0; i<pwc->GetCount(); i++ )
{
wxPGProperty* p = pwc->Item(i);
if ( p->GetParentingType() <= 0 )
{
dc.GetTextExtent( p->GetLabel(), &w, &h );
w += pg->m_marginWidth + ( ((int)p->m_depth-1) * pg->m_subgroup_extramargin ) + (wxPG_XBEFORETEXT*2);
if ( w > maxW )
maxW = w;
}
if ( p->GetParentingType() &&
( subProps || p->GetParentingType() > 0 ) )
{
w = GetLeftSplitterPos( dc, (wxPGPropertyWithChildren*) p, subProps );
if ( w > maxW )
maxW = w;
}
}
return maxW;
}
// -----------------------------------------------------------------------
// wxPropertyGridState property value setting and getting
// -----------------------------------------------------------------------
void wxPropertyGridState::SetPropVal( wxPGProperty* p, const wxPGVariant& value )
{
p->DoSetValue(value);
if ( m_selected==p && this==m_pPropGrid->GetState() )
p->UpdateControl(m_pPropGrid->m_wndPrimary);
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::ClearPropertyValue( wxPGProperty* p )
{
if ( p )
{
const wxPGValueType* valueclass = p->GetValueTypePtr();
if ( valueclass != wxPG_VALUETYPE_PTR(none) )
{
// wnd_primary has to be given so the editor control can be updated as well.
SetPropVal(p,valueclass->GetDefaultValue());
return true;
}
}
return false;
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::SetPropertyValue( wxPGProperty* p,
const wxPGValueType* typeclass,
const wxPGVariant& value )
{
if ( p )
{
if ( p->GetValueTypePtr()->GetTypeName() == typeclass->GetTypeName() )
{
CLEAR_PROPERTY_UNSPECIFIED_FLAG(p);
SetPropVal(p,value);
return true;
}
wxPGTypeOperationFailed ( p, typeclass->GetTypeName(), wxT("Set") );
}
return false;
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::SetPropertyValue( wxPGProperty* p, const wxChar* typestring, const wxPGVariant& value )
{
if ( p )
{
if ( wxStrcmp(p->GetValueTypePtr()->GetCustomTypeName(),typestring) == 0 )
{
// wnd_primary has to be given so the control can be updated as well.
SetPropVal(p,value);
return true;
}
wxPGTypeOperationFailed ( p, typestring, wxT("Set") );
}
return false;
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::SetPropertyValueString( wxPGProperty* p, const wxString& value )
{
if ( p )
{
int flags = wxPG_REPORT_ERROR|wxPG_FULL_VALUE;
CLEAR_PROPERTY_UNSPECIFIED_FLAG(p);
if ( p->GetMaxLength() <= 0 )
p->SetValueFromString( value, flags );
else
p->SetValueFromString( value.Mid(0,p->GetMaxLength()), flags );
if ( m_selected==p && this==m_pPropGrid->GetState() )
p->UpdateControl(m_pPropGrid->m_wndPrimary);
return true;
}
return false;
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::SetPropertyValue( wxPGProperty* p, wxVariant& value )
{
if ( p )
{
CLEAR_PROPERTY_UNSPECIFIED_FLAG(p);
p->GetValueTypePtr()->SetValueFromVariant(p,value);
if ( m_selected==p && this==m_pPropGrid->GetState() )
p->UpdateControl(m_pPropGrid->m_wndPrimary);
return true;
}
return false;
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::SetPropertyValueWxObjectPtr( wxPGProperty* p, wxObject* value )
{
if ( p )
{
if ( wxStrcmp( p->GetValueTypePtr()->GetTypeName(),
value->GetClassInfo()->GetClassName()
) == 0
)
{
CLEAR_PROPERTY_UNSPECIFIED_FLAG(p);
// wnd_primary has to be given so the control can be updated as well.
SetPropVal(p,wxPGVariantFromWxObject(value));
return true;
}
wxPGTypeOperationFailed ( p, wxT("wxObject"), wxT("Set") );
}
return false;
}
// -----------------------------------------------------------------------
void wxPropertyGridState::SetPropertyUnspecified( wxPGProperty* p )
{
wxCHECK_RET( p, wxT("invalid property id") );
if ( !(p->m_flags & wxPG_PROP_UNSPECIFIED) )
{
// Flag should be set first - editor class methods may need it
p->m_flags |= wxPG_PROP_UNSPECIFIED;
wxASSERT( m_pPropGrid );
if ( m_pPropGrid->GetState() == this )
{
if ( m_pPropGrid->m_selected == p && m_pPropGrid->m_wndPrimary )
{
p->GetEditorClass()->SetValueToUnspecified(m_pPropGrid->m_wndPrimary);
}
}
if ( p->GetParentingType() != 0 )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
size_t i;
for ( i = 0; i < pwc->GetCount(); i++ )
SetPropertyUnspecified( pwc->Item(i) );
}
}
}
// -----------------------------------------------------------------------
// wxPropertyGridState property operations
// -----------------------------------------------------------------------
void wxPropertyGridState::LimitPropertyEditing( wxPGProperty* p, bool limit )
{
if ( p )
{
if ( limit )
p->m_flags |= wxPG_PROP_NOEDITOR;
else
p->m_flags &= ~(wxPG_PROP_NOEDITOR);
}
}
// -----------------------------------------------------------------------
void wxPropertyGridState::ClearModifiedStatus( wxPGProperty* p )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
if ( p->m_flags & wxPG_PROP_MODIFIED )
{
p->m_flags &= ~(wxPG_PROP_MODIFIED);
if ( m_pPropGrid->GetState() == this )
{
// Clear active editor bold
if ( p == m_selected && m_pPropGrid->m_wndPrimary )
m_pPropGrid->m_wndPrimary->SetFont( m_pPropGrid->GetFont() );
m_pPropGrid->DrawItem( p );
}
}
if ( pwc->GetParentingType() != 0 )
{
size_t i;
for ( i = 0; i < pwc->GetCount(); i++ )
ClearModifiedStatus( pwc->Item(i) );
}
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::Collapse( wxPGProperty* p )
{
wxCHECK_MSG( p, false, wxT("invalid property id") );
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
if ( pwc->GetParentingType() == 0 ) return false;
if ( !pwc->m_expanded ) return false;
// m_expanded must be set just before call to CalculateYs
pwc->m_expanded = 0;
return true;
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::Expand( wxPGProperty* p )
{
wxCHECK_MSG( p, false, wxT("invalid property id") );
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
if ( pwc->GetParentingType() == 0 ) return false;
if ( pwc->m_expanded ) return false;
// m_expanded must be set just before call to CalculateYs
pwc->m_expanded = 1;
return true;
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::DoSelectProperty( wxPGProperty* p, unsigned int flags )
{
if ( this == m_pPropGrid->GetState() )
return m_pPropGrid->DoSelectProperty( p, flags );
m_selected = p;
return true;
}
// -----------------------------------------------------------------------
void wxPropertyGridState::SetPropertyLabel( wxPGProperty* p, const wxString& newlabel )
{
wxCHECK_RET(p, wxT("invalid property id"));
p->SetLabel(newlabel);
if ( m_pPropGrid->GetWindowStyleFlag() & wxPG_AUTO_SORT )
Sort(p->GetParent());
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::SetPropertyPriority( wxPGProperty* p, int priority )
{
int parenting = p->GetParentingType();
if ( priority == wxPG_HIGH ) p->ClearFlag( wxPG_PROP_HIDEABLE );
else p->SetFlag( wxPG_PROP_HIDEABLE );
if ( parenting != 0 )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
size_t i;
for ( i = 0; i < pwc->GetCount(); i++ )
SetPropertyPriority(pwc->Item(i),priority);
}
return true;
}
// -----------------------------------------------------------------------
void wxPropertyGridState::SetPropertyAndChildrenFlags( wxPGProperty* p, long flags )
{
p->m_flags |= flags;
if ( p->GetParentingType() != 0 )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
size_t i;
for ( i = 0; i < pwc->GetCount(); i++ )
ClearPropertyAndChildrenFlags ( pwc->Item(i), flags );
}
}
// -----------------------------------------------------------------------
void wxPropertyGridState::ClearPropertyAndChildrenFlags( wxPGProperty* p, long flags )
{
p->m_flags &= ~(flags);
if ( p->GetParentingType() != 0 )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
size_t i;
for ( i = 0; i < pwc->GetCount(); i++ )
ClearPropertyAndChildrenFlags ( pwc->Item(i), flags );
}
}
// -----------------------------------------------------------------------
bool wxPropertyGridState::EnableProperty( wxPGProperty* p, bool enable )
{
if ( p )
{
if ( enable )
{
if ( !(p->m_flags & wxPG_PROP_DISABLED) )
return false;
// Enabling
p->m_flags &= ~(wxPG_PROP_DISABLED);
}
else
{
if ( p->m_flags & wxPG_PROP_DISABLED )
return false;
// Disabling
p->m_flags |= wxPG_PROP_DISABLED;
}
if ( p->GetParentingType() == 0 )
return true;
// Apply same to sub-properties as well
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
size_t i;
for ( i = 0; i < pwc->GetCount(); i++ )
EnableProperty ( pwc->Item(i), enable );
return true;
}
return false;
}
// -----------------------------------------------------------------------
// wxPropertyGridState wxVariant related routines
// -----------------------------------------------------------------------
// Returns list of wxVariant objects (non-categories and non-sub-properties only).
// Never includes sub-properties (unless they are parented by wxParentProperty).
wxVariant wxPropertyGridState::GetPropertyValues( const wxString& listname,
wxPGId baseparent,
long flags ) const
{
ITEM_ITERATION_DCAE_VARIABLES
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)wxPGIdToPtr(baseparent);
// Root is the default base-parent.
if ( !pwc )
pwc = m_properties;
wxVariantList temp_list;
wxVariant v( temp_list, listname );
if ( flags & wxPG_KEEP_STRUCTURE )
{
wxASSERT( (pwc->GetParentingType() < -1) || (pwc->GetParentingType() > 0) );
size_t i;
for ( i=0; i<pwc->GetCount(); i++ )
{
wxPGProperty* p = pwc->Item(i);
int parenting = p->GetParentingType();
if ( parenting == 0 || parenting == -1 )
{
v.Append( p->GetValueAsVariant() );
}
else
{
v.Append( GetPropertyValues(p->m_name,wxPGIdGen(p),wxPG_KEEP_STRUCTURE) );
}
}
}
else
{
ITEM_ITERATION_INIT((wxPGPropertyWithChildren*)wxPGIdToPtr(baseparent),0)
ITEM_ITERATION_DCAE_ISP_LOOP_BEGIN
// Use a trick to ignore wxParentProperty itself, but not its sub-properties.
if ( parenting == PT_CUSTOMPROPERTY )
{
parenting = PT_CAPTION;
}
else if ( parenting <= 0 )
{
v.Append ( p->GetValueAsVariant() );
}
ITEM_ITERATION_DCAE_ISP_LOOP_END
}
return v;
}
// -----------------------------------------------------------------------
void wxPropertyGridState::SetPropertyValues( const wxVariantList& list, wxPGId default_category )
{
unsigned char origFrozen = 1;
if ( m_pPropGrid->GetState() == this )
{
origFrozen = m_pPropGrid->m_frozen;
if ( !origFrozen ) m_pPropGrid->Freeze();
}
wxPropertyCategoryClass* use_category = (wxPropertyCategoryClass*)wxPGIdToPtr(default_category);
if ( !use_category )
use_category = (wxPropertyCategoryClass*)m_properties;
// Let's iterate over the list of variants.
wxVariantList::const_iterator node;
//for ( wxVariantList::Node *node = list.GetFirst(); node; node = node->GetNext() )
for ( node = list.begin(); node != list.end(); node++ )
{
wxVariant *current = (wxVariant*)*node;
// Make sure it is wxVariant.
wxASSERT( current );
wxASSERT( wxStrcmp(current->GetClassInfo()->GetClassName(),wxT("wxVariant")) == 0 );
if ( current->GetName().length() > 0 )
{
wxPGId foundProp = BaseGetPropertyByName(current->GetName());
if ( wxPGIdIsOk(foundProp) )
{
wxPGProperty* p = wxPGIdToPtr(foundProp);
const wxPGValueType* vtype = p->GetValueTypePtr();
// If it was a list, we still have to go through it.
if ( current->GetType() == wxT("list") )
{
SetPropertyValues( current->GetList(),
wxPGIdGen(
p->GetParentingType()>0?p:((wxPGProperty*)NULL)
) );
}
else
{
#ifdef __WXDEBUG__
if ( current->GetType() != vtype->GetTypeName() &&
current->GetType() != vtype->GetCustomTypeName() )
{
wxLogDebug(wxT("wxPropertyGridState::SetPropertyValues Warning: Setting value of property \"%s\" from variant"),
p->m_name.c_str());
wxLogDebug(wxT(" but variant's type name (%s) doesn't match either base type name (%s) nor custom type name (%s)."),
#ifndef __WXPYTHON__
current->GetType().c_str(),vtype->GetTypeName(),vtype->GetCustomTypeName());
#else
current->GetType().c_str(),vtype->GetTypeName().c_str(),vtype->GetCustomTypeName().c_str());
#endif
}
#endif
vtype->SetValueFromVariant(p,*current);
}
}
else
{
// Is it list?
if ( current->GetType() != wxT("list") )
{
// Not.
AppendIn(use_category,current->GetName(),wxPG_LABEL,(wxVariant&)*current);
}
else
{
// Yes, it is; create a sub category and append contents there.
wxPGId newCat = DoInsert(use_category,-1,new wxPropertyCategoryClass(current->GetName(),wxPG_LABEL));
SetPropertyValues( current->GetList(), newCat );
}
}
}
}
if ( !origFrozen )
{
m_pPropGrid->Thaw();
if ( this == m_pPropGrid->GetState() )
{
m_selected->UpdateControl(m_pPropGrid->m_wndPrimary);
}
}
}
// -----------------------------------------------------------------------
// wxPropertyGridState property adding and removal
// -----------------------------------------------------------------------
// Call for after sub-properties added with AddChild
void wxPGPropertyWithChildren::PrepareSubProperties()
{
// TODO: When in 1.0.5, move extra stuff from AddChild to here.
wxPropertyGridState* state = GetParentState();
wxASSERT(state);
if ( !GetCount() )
return;
wxByte depth = m_depth + 1;
wxByte depthBgCol = m_depthBgCol;
wxByte inheritFlags = m_flags & wxPG_INHERITED_PROPFLAGS;
wxByte bgColIndex = m_bgColIndex;
wxByte fgColIndex = m_fgColIndex;
//
// Set some values to the children
//
size_t i = 0;
wxPGPropertyWithChildren* nparent = this;
while ( i < nparent->GetCount() )
{
wxPGProperty* np = nparent->Item(i);
np->m_flags |= inheritFlags; // Hideable also if parent.
np->m_depth = depth;
np->m_depthBgCol = depthBgCol;
np->m_bgColIndex = bgColIndex;
np->m_fgColIndex = fgColIndex;
// Also handle children of children
if ( np->GetParentingType() != 0 &&
((wxPGPropertyWithChildren*)np)->GetCount() > 0 )
{
nparent = (wxPGPropertyWithChildren*) np;
i = 0;
// Init
nparent->m_expanded = 0;
nparent->m_parentState = state;
depth++;
}
else
{
// Next sibling
i++;
}
// After reaching last sibling, go back to processing
// siblings of the parent
while ( i >= nparent->GetCount() )
{
// Exit the loop when top parent hit
if ( nparent == this )
break;
depth--;
i = nparent->GetArrIndex() + 1;
nparent = nparent->GetParent();
}
}
}
// -----------------------------------------------------------------------
// Call after fixed sub-properties added/removed after creation.
// if oldSelInd >= 0 and < new max items, then selection is
// moved to it. Note: oldSelInd -2 indicates that this property
// should be selected.
void wxPGPropertyWithChildren::SubPropsChanged( int oldSelInd )
{
wxPropertyGridState* state = GetParentState();
wxPropertyGrid* grid = state->GetGrid();
PrepareSubProperties();
wxPGProperty* sel = (wxPGProperty*) NULL;
if ( oldSelInd >= (int)m_children.GetCount() )
oldSelInd = (int)m_children.GetCount() - 1;
if ( oldSelInd >= 0 )
sel = (wxPGProperty*) m_children[oldSelInd];
else if ( oldSelInd == -2 )
sel = this;
if ( sel )
state->DoSelectProperty(sel);
if ( state == grid->GetState() )
{
if ( m_expanded )
grid->CalculateYs( GetParent(), m_arrIndex );
grid->Refresh();
}
}
// -----------------------------------------------------------------------
int wxPropertyGridState::PrepareToAddItem( wxPGProperty* property,
wxPGPropertyWithChildren* scheduledParent )
{
wxPropertyGrid* propGrid = m_pPropGrid;
wxASSERT( propGrid );
int parenting = property->GetParentingType();
// This will allow better behaviour.
if ( scheduledParent == m_properties )
scheduledParent = (wxPGPropertyWithChildren*) NULL;
if ( parenting > 0 )
{
/*
if ( scheduledParent )
wxLogDebug(wxT("scheduledParent= %s, %i"),
scheduledParent->GetName().c_str(), (int)scheduledParent->GetParentingType());
*/
// Parent of a category must be either root or another category
// (otherwise Bad Things might happen).
wxASSERT_MSG( scheduledParent == (wxPGPropertyWithChildren*) NULL ||
scheduledParent == m_properties ||
scheduledParent->GetParentingType() > 0,
wxT("Parent of a category must be either root or another category."));
/*
wxASSERT_MSG( m_properties == &m_regularArray,
wxT("Do not add categories in non-categoric mode!"));
*/
// If we already have category with same name, delete given property
// and use it instead as most recent caption item.
wxPGId found_id = BaseGetPropertyByName( property->GetName() );
if ( wxPGIdIsOk(found_id) )
{
wxPropertyCategoryClass* pwc = (wxPropertyCategoryClass*)wxPGIdToPtr(found_id);
if ( pwc->GetParentingType() > 0 ) // Must be a category.
{
delete property;
m_currentCategory = pwc;
return 2; // Tells the caller what we did.
}
}
}
#ifdef __WXDEBUG__
// Warn for identical names in debug mode.
if ( property->GetName().length() &&
wxPGIdIsOk(BaseGetPropertyByName(property->GetName())) &&
(!scheduledParent || scheduledParent->GetParentingType() >= 1) )
wxLogError(wxT("wxPropertyGrid: Warning - item with name \"%s\" already exists."),
property->GetName().c_str());
#endif
// Make sure nothing is selected.
if ( propGrid && propGrid->m_selected )
{
bool selRes = propGrid->ClearSelection();
wxPG_CHECK_MSG_DBG( selRes,
-1,
wxT("failed to deselect a property (editor probably had invalid value)") );
}
property->m_y = -1;
if ( scheduledParent )
{
// Use parent's colours.
property->m_bgColIndex = scheduledParent->m_bgColIndex;
property->m_fgColIndex = scheduledParent->m_fgColIndex;
}
// If in hideable adding mode, or if assigned parent is hideable, then
// make this one hideable.
if (
( scheduledParent && (scheduledParent->m_flags & wxPG_PROP_HIDEABLE) ) ||
( propGrid && (propGrid->m_iFlags & wxPG_FL_ADDING_HIDEABLES) )
)
property->SetFlag ( wxPG_PROP_HIDEABLE );
// Set custom image flag.
int custImgHeight = property->GetImageSize().y;
if ( custImgHeight < 0 /*|| custImgHeight > 1*/ )
{
property->m_flags |= wxPG_PROP_CUSTOMIMAGE;
}
if ( propGrid->GetWindowStyleFlag() & wxPG_LIMITED_EDITING )
property->m_flags |= wxPG_PROP_NOEDITOR;
if ( parenting < 1 )
{
// This is not a category.
wxASSERT_MSG( property->GetEditorClass(), wxT("Editor class not initialized!") );
// Depth.
//
unsigned char depth = 1;
if ( scheduledParent )
{
depth = scheduledParent->m_depth;
if ( scheduledParent->GetParentingType() != PT_CAPTION )
depth++;
}
property->m_depth = depth;
unsigned char greyDepth = depth;
if ( scheduledParent )
{
wxPropertyCategoryClass* pc;
if ( scheduledParent->GetParentingType() >= PT_CAPTION )
pc = (wxPropertyCategoryClass*)scheduledParent;
else
// This conditional compile is necessary to
// bypass some compiler bug.
pc = wxPropertyGrid::_GetPropertyCategory(scheduledParent);
if ( pc )
greyDepth = pc->GetDepth();
else
greyDepth = scheduledParent->m_depthBgCol;
}
property->m_depthBgCol = greyDepth;
// Add children to propertywithchildren.
if ( parenting < PT_NONE )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)property;
pwc->m_parentState = this;
pwc->m_expanded = 0; // Properties with children are not expanded by default.
if ( propGrid && propGrid->GetWindowStyleFlag() & wxPG_HIDE_MARGIN )
pwc->m_expanded = 1; // ...unless it cannot not be expanded.
if ( pwc->GetCount() )
{
pwc->PrepareSubProperties();
}
//
// If children were added prior to append, then this is considered
// a "fixed" parent (otherwise the PT_CUSTOMPROPERTY is set, see below,
// to mark it as customizable).
/*if ( pwc->GetCount() )
{
pwc->PrepareSubProperties();
}
else
{
pwc->m_parentingType = PT_CUSTOMPROPERTY;
}*/
}
}
else
{
// This is a category.
// depth
unsigned char depth = 1;
if ( scheduledParent )
{
depth = scheduledParent->m_depth + 1;
}
property->m_depth = depth;
property->m_depthBgCol = depth;
m_currentCategory = (wxPropertyCategoryClass*)property;
wxPropertyCategoryClass* pc = (wxPropertyCategoryClass*)property;
pc->m_parentState = this;
// Calculate text extent for caption item.
pc->CalculateTextExtent(propGrid,propGrid->GetCaptionFont());
}
return parenting;
}
// -----------------------------------------------------------------------
void wxPropertyContainerMethods::BeginAddChildren( wxPGId id )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*) wxPGIdToPtr(id);
wxCHECK_RET( pwc, wxT("NULL property") );
wxCHECK_RET( pwc->GetParentingType() == PT_FIXEDCHILDREN, wxT("only call on properties with fixed children") );
pwc->m_parentingType = PT_CUSTOMPROPERTY;
}
// -----------------------------------------------------------------------
void wxPropertyContainerMethods::EndAddChildren( wxPGId id )
{
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*) wxPGIdToPtr(id);
wxCHECK_RET( pwc, wxT("NULL property") );
wxCHECK_RET( pwc->GetParentingType() == PT_CUSTOMPROPERTY, wxT("only call on properties for which BeginAddChildren was called prior") );
pwc->m_parentingType = PT_FIXEDCHILDREN;
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGridState::Append( wxPGProperty* property )
{
wxPropertyCategoryClass* cur_cat = m_currentCategory;
if ( property->GetParentingType() > 0 )
cur_cat = (wxPropertyCategoryClass*) NULL;
return DoInsert( cur_cat, -1, property );
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGridState::DoInsert( wxPGPropertyWithChildren* parent, int index, wxPGProperty* property )
{
if ( !parent )
parent = m_properties;
wxPropertyGrid* propGrid = m_pPropGrid;
wxCHECK_MSG( parent->GetParentingType() != PT_NONE,
wxNullProperty,
wxT("this parent cannot accomodate children") );
wxCHECK_MSG( parent->GetParentingType() != PT_FIXEDCHILDREN,
wxNullProperty,
wxT("when adding properties to fixed parents, use BeginAddChildren and EndAddChildren.") );
int parenting = PrepareToAddItem( property, (wxPropertyCategoryClass*)parent );
// This type of invalid parenting value indicates we should exit now, returning
// id of most recent category.
if ( parenting > PT_CAPTION )
return wxPGIdGen(m_currentCategory);
// Note that item must be added into current mode later.
// If parent is wxParentProperty, just stick it in...
// If parent is root (m_properties), then...
// In categoric mode: Add as last item in m_abcArray (if not category).
// Add to given index in m_regularArray.
// In non-cat mode: Add as last item in m_regularArray.
// Add to given index in m_abcArray.
// If parent is category, then...
// 1) Add to given category in given index.
// 2) Add as last item in m_abcArray.
int parents_parenting = parent->GetParentingType();
if ( parents_parenting < 0 )
{
// Parent is wxParentingProperty: Just stick it in...
parent->AddChild2( property, index );
}
else
{
// Parent is Category or Root.
if ( m_properties == &m_regularArray )
{
// Categorized mode
// Only add non-categories to m_abcArray.
if ( m_abcArray && parenting <= 0 )
m_abcArray->AddChild2( property, -1, false );
// Add to current mode.
parent->AddChild2( property, index );
}
else
{
// Non-categorized mode.
if ( parent != m_properties )
// Parent is category.
parent->AddChild2( property, index, false );
else
// Parent is root.
m_regularArray.AddChild2( property, -1, false );
// Add to current mode (no categories).
if ( parenting <= 0 )
m_abcArray->AddChild2( property, index );
}
}
// category stuff
if ( parenting > PT_NONE )
{
// This is a category caption item.
// Last caption is not the bottom one (this info required by append)
m_lastCaptionBottomnest = 0;
}
// Only add name to hashmap if parent is root or category
if ( parent->GetParentingType() >= PT_CAPTION && property->m_name.length() )
m_dictName[property->m_name] = (void*) property;
m_itemsAdded = 1;
if ( propGrid )
propGrid->m_bottomy = 0; // this signals y recalculation
return wxPGIdGen(property);
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGridState::AppendIn( wxPGPropertyWithChildren* pwc,
const wxString& label,
const wxString& propname,
wxVariant& value )
{
wxPGProperty* p = wxPropertyContainerMethods::
CreatePropertyByType(value.GetType(),label,propname);
if ( p )
{
p->GetValueTypePtr()->SetValueFromVariant(p,value);
return DoInsert(pwc,-1,p);
}
return wxPGIdGen((wxPGProperty*)NULL);
}
// -----------------------------------------------------------------------
void wxPropertyGridState::DoDelete( wxPGProperty* item )
{
wxCHECK_RET( item != &m_regularArray && item != m_abcArray,
wxT("wxPropertyGrid: Do not attempt to remove the root item.") );
size_t i;
int parenting = item->GetParentingType();
unsigned int indinparent = item->GetIndexInParent();
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)item;
wxCHECK_RET( item->GetParent()->GetParentingType() != -1,
wxT("wxPropertyGrid: Do not attempt to remove sub-properties.") );
if ( parenting > 0 )
{
// deleting a category
// erase category entries from the hash table
for ( i=0; i<pwc->GetCount(); i++ )
{
wxPGProperty* sp = pwc->Item( i );
if ( sp->GetName().Len() ) m_dictName.erase( wxPGNameConv(sp->GetName()) );
}
if ( pwc == m_currentCategory )
m_currentCategory = (wxPropertyCategoryClass*) NULL;
if ( m_abcArray )
{
// Remove children from non-categorized array.
for ( i=0; i<pwc->GetCount(); i++ )
{
wxPGProperty * p = pwc->Item( i );
wxASSERT( p != NULL );
if ( p->GetParentingType() <= PT_NONE )
m_abcArray->m_children.Remove( (void*)p );
}
if ( IsInNonCatMode() )
m_abcArray->FixIndexesOfChildren();
}
}
if ( !IsInNonCatMode() )
{
// categorized mode - non-categorized array
// Remove from non-cat array, but only if parent is in it
if ( parenting <= 0 && item->GetParent()->GetParentingType() == PT_CAPTION )
{
if ( m_abcArray )
{
m_abcArray->m_children.Remove( item );
}
}
// categorized mode - categorized array
item->m_parent->m_children.RemoveAt(indinparent);
item->m_parent->FixIndexesOfChildren(/*indinparent*/);
}
else
{
// non-categorized mode - categorized array
// We need to find location of item.
wxPGPropertyWithChildren* cat_parent = &m_regularArray;
int cat_index = m_regularArray.GetCount();
size_t i;
for ( i = 0; i < m_regularArray.GetCount(); i++ )
{
wxPGProperty* p = m_regularArray.Item(i);
if ( p == item ) { cat_index = i; break; }
if ( p->GetParentingType() > 0 )
{
int subind = ((wxPGPropertyWithChildren*)p)->Index(item);
if ( subind != wxNOT_FOUND )
{
cat_parent = ((wxPGPropertyWithChildren*)p);
cat_index = subind;
break;
}
}
}
cat_parent->m_children.RemoveAt(cat_index);
// non-categorized mode - non-categorized array
if ( parenting <= 0 )
{
wxASSERT( item->m_parent == m_abcArray );
item->m_parent->m_children.RemoveAt(indinparent);
item->m_parent->FixIndexesOfChildren(indinparent);
}
}
if ( item->GetName().Len() ) m_dictName.erase( wxPGNameConv(item->GetName()) );
#ifdef __WXPYTHON__
// For some reason, Py_DECREF always crashes, even though we make
// matching Py_INCREF call in propgrid_cbacks.cpp. Maybe refcount is decremented
// somewhere automatically? Unlikely though...
//if ( item->m_scriptObject )
// Py_DECREF( item->m_scriptObject );
#endif
// We can actually delete it now
delete item;
m_itemsAdded = 1; // Not a logical assignment (but required nonetheless).
if ( this == m_pPropGrid->GetState() )
{
//m_pPropGrid->m_clearThisMany = 1;
m_pPropGrid->m_bottomy = 0; // this signals y recalculation
}
}
// -----------------------------------------------------------------------
// wxPropertyGridState init etc.
// -----------------------------------------------------------------------
void wxPropertyGridState::InitNonCatMode()
{
ITEM_ITERATION_DCAE_VARIABLES
if ( !m_abcArray )
{
m_abcArray = new wxPGRootPropertyClass();
m_abcArray->SetParentState(this);
m_abcArray->m_expanded = wxPG_EXP_OF_COPYARRAY;
}
// Must be called when FROM_STATE(m_properties) still points to regularArray.
wxPGPropertyWithChildren* oldProperties = m_properties;
// Must use temp value in FROM_STATE(m_properties) for item iteration loop
// to run as expected.
m_properties = &m_regularArray;
// Copy items.
ITEM_ITERATION_INIT_FROM_THE_TOP
ITEM_ITERATION_DCAE_ISP_LOOP_BEGIN
if ( parenting < 1 &&
( parent == m_properties || parent->GetParentingType() > 0 ) )
{
m_abcArray->AddChild2 ( p );
p->m_parent = &FROM_STATE(m_regularArray);
}
//else wxLogDebug("OUT: %s",p->m_label.c_str());
ITEM_ITERATION_DCAE_ISP_LOOP_END
m_properties = oldProperties;
}
// -----------------------------------------------------------------------
void wxPropertyGridState::Clear()
{
m_regularArray.Empty();
if ( m_abcArray )
m_abcArray->Empty();
m_dictName.clear();
m_currentCategory = (wxPropertyCategoryClass*) NULL;
m_lastCaptionBottomnest = 1;
m_itemsAdded = 0;
m_selected = (wxPGProperty*) NULL;
}
// -----------------------------------------------------------------------
wxPropertyGridState::wxPropertyGridState()
{
m_pPropGrid = (wxPropertyGrid*) NULL;
m_regularArray.SetParentState(this);
m_properties = &m_regularArray;
m_abcArray = (wxPGRootPropertyClass*) NULL;
m_currentCategory = (wxPropertyCategoryClass*) NULL;
m_selected = (wxPGProperty*) NULL;
m_lastCaptionBottomnest = 1;
m_itemsAdded = 0;
m_anyModified = 0;
}
// -----------------------------------------------------------------------
wxPropertyGridState::~wxPropertyGridState()
{
delete m_abcArray;
}
// -----------------------------------------------------------------------
// wxPropertyGridPopulator
// -----------------------------------------------------------------------
void wxPropertyGridPopulator::Init( wxPropertyGrid* pg, wxPGId popRoot )
{
WX_PG_GLOBALS_LOCKER()
m_propGrid = pg;
m_popRoot = popRoot;
wxPGGlobalVars->m_offline++;
}
// -----------------------------------------------------------------------
wxPropertyGridPopulator::~wxPropertyGridPopulator()
{
//
// Free unused sets of choices
wxPGHashMapP2P::iterator it;
for( it = m_dictIdChoices.begin(); it != m_dictIdChoices.end(); ++it )
{
wxPGChoicesData* data = (wxPGChoicesData*) it->second;
data->m_refCount--;
if ( data->m_refCount < 1 )
delete data;
}
wxPGGlobalVars->m_offline--;
}
// -----------------------------------------------------------------------
bool wxPropertyGridPopulator::HasChoices( wxPGChoicesId id ) const
{
wxPGHashMapP2P::const_iterator it = m_dictIdChoices.find(id);
return ( it != m_dictIdChoices.end() );
}
// -----------------------------------------------------------------------
bool wxPropertyGridPopulator::BeginChildren()
{
if ( wxPGIdIsOk(m_lastProperty) &&
wxPGIdToPtr(m_lastProperty)->CanHaveExtraChildren() )
{
wxLogDebug(wxT("New Parent: %s"),wxPGIdToPtr(m_lastProperty)->GetLabel().c_str());
m_curParent = m_lastProperty;
return true;
}
return false;
}
// -----------------------------------------------------------------------
void wxPropertyGridPopulator::AddChoices(wxPGChoicesId choicesId,
const wxArrayString& choiceLabels,
const wxArrayInt& choiceValues)
{
#ifdef __WXDEBUG__
// Make sure the id is not used yet
wxPGHashMapP2P::iterator it = m_dictIdChoices.find(choicesId);
wxCHECK_RET( it == m_dictIdChoices.end(),
wxT("added set of choices to same id twice (use HasChoices if necessary)") );
#endif
wxCHECK_RET( choicesId != (wxPGChoicesId)0,
wxT("choicesId must not be 0/NULL"));
wxPGChoices chs(choiceLabels,choiceValues);
wxPGChoicesData* data = chs.ExtractData();
m_dictIdChoices[choicesId] = (void*) data;
// Artifically reduce refcount to 0 (since nothing uses it yet)
//data->m_refCount = 0;
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGridPopulator::DoAppend(wxPGProperty* p,
const wxString& value,
const wxString& attributes,
wxPGChoicesId choicesId,
const wxArrayString& choiceLabels,
const wxArrayInt& choiceValues)
{
wxASSERT( m_propGrid );
// Make sure m_curParent is ok
if ( !wxPGIdIsOk(m_curParent) )
{
if ( !wxPGIdIsOk(m_popRoot) )
m_popRoot = m_propGrid->GetRoot();
m_curParent = m_popRoot;
}
if ( p )
{
// Set choices
if ( choicesId )
{
wxPGHashMapP2P::iterator it = m_dictIdChoices.find(choicesId);
wxPGChoices chs;
if ( it != m_dictIdChoices.end() )
{
// Already found
wxPGChoicesData* foundData = (wxPGChoicesData*) it->second;
chs.AssignData(foundData);
}
else
{
chs.Set(choiceLabels,choiceValues);
m_dictIdChoices[choicesId] = (void*) chs.GetData();
}
p->SetChoices(chs);
}
// Value setter must be before append
if ( value.length() )
{
p->SetValueFromString(value,wxPG_FULL_VALUE);
}
// Set attributes
if ( attributes.length() )
wxPropertyGrid::SetPropertyAttributes(p,attributes);
// Append to grid
m_propGrid->AppendIn(m_curParent,p);
m_lastProperty = p;
}
return wxPGIdGen(p);
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGridPopulator::AppendByClass(const wxString& classname,
const wxString& label,
const wxString& name,
const wxString& value,
const wxString& attributes,
wxPGChoicesId choicesId,
const wxArrayString& choiceLabels,
const wxArrayInt& choiceValues)
{
wxPGProperty* p = m_propGrid->CreatePropertyByClass(classname,label,name);
return DoAppend(p,value,attributes,choicesId,choiceLabels,choiceValues);
}
// -----------------------------------------------------------------------
wxPGId wxPropertyGridPopulator::AppendByType(const wxString& valuetype,
const wxString& label,
const wxString& name,
const wxString& value,
const wxString& attributes,
wxPGChoicesId choicesId,
const wxArrayString& choiceLabels,
const wxArrayInt& choiceValues)
{
wxPGProperty* p = m_propGrid->CreatePropertyByType(valuetype,label,name);
return DoAppend(p,value,attributes,choicesId,choiceLabels,choiceValues);
}
// -----------------------------------------------------------------------