forked from mia/Aegisub
dc404a954e
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.
13298 lines
376 KiB
C++
13298 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);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|