mirror of https://github.com/odrling/Aegisub
13299 lines
376 KiB
C++
13299 lines
376 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: propgrid.cpp
|
|
// Purpose: wxPropertyGrid
|
|
// Author: Jaakko Salli
|
|
// Modified by:
|
|
// Created: Sep-25-2004
|
|
// RCS-ID: $Id:
|
|
// Copyright: (c) Jaakko Salli
|
|
// Licence: wxWindows license
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
|
|
#pragma implementation "propgrid.h"
|
|
#endif
|
|
|
|
// For compilers that support precompilation, includes "wx/wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/defs.h"
|
|
#include "wx/object.h"
|
|
#include "wx/hash.h"
|
|
#include "wx/string.h"
|
|
#include "wx/log.h"
|
|
#include "wx/event.h"
|
|
#include "wx/window.h"
|
|
#include "wx/panel.h"
|
|
#include "wx/dc.h"
|
|
#include "wx/dcclient.h"
|
|
#include "wx/dcmemory.h"
|
|
#include "wx/button.h"
|
|
#include "wx/pen.h"
|
|
#include "wx/brush.h"
|
|
#include "wx/cursor.h"
|
|
#include "wx/dialog.h"
|
|
#include "wx/settings.h"
|
|
#include "wx/msgdlg.h"
|
|
#include "wx/choice.h"
|
|
#include "wx/stattext.h"
|
|
#include "wx/scrolwin.h"
|
|
#include "wx/dirdlg.h"
|
|
#include "wx/combobox.h"
|
|
#include "wx/layout.h"
|
|
#include "wx/sizer.h"
|
|
#include "wx/textdlg.h"
|
|
#include "wx/filedlg.h"
|
|
#include "wx/statusbr.h"
|
|
#include "wx/intl.h"
|
|
#include "wx/frame.h"
|
|
#endif
|
|
|
|
|
|
#include "wx/timer.h"
|
|
#include "wx/dcbuffer.h"
|
|
|
|
|
|
// This define is necessary to prevent macro clearing
|
|
#define __wxPG_SOURCE_FILE__
|
|
|
|
#include <wx/propgrid/propgrid.h>
|
|
|
|
#include <wx/propgrid/propdev.h>
|
|
|
|
#ifdef __WXPYTHON__
|
|
#include <wx/propgrid/advprops.h>
|
|
#include <wx/propgrid/extras.h>
|
|
#endif
|
|
|
|
#if wxPG_USE_RENDERER_NATIVE
|
|
#include <wx/renderer.h>
|
|
#endif
|
|
|
|
#include <wx/propgrid/odcombo.h>
|
|
|
|
#ifdef __WXMSW__
|
|
#include <wx/msw/private.h>
|
|
#endif
|
|
|
|
|
|
// Two pics for the expand / collapse buttons.
|
|
// Files are not supplied with this project (since it is
|
|
// recommended to use either custom or native rendering).
|
|
// If you want them, get wxTreeMultiCtrl by Jorgen Bodde,
|
|
// and copy xpm files from archive to wxPropertyGrid src directory
|
|
// (and also comment/undef wxPG_ICON_WIDTH in propGrid.h
|
|
// and set wxPG_USE_RENDERER_NATIVE to 0).
|
|
#ifndef wxPG_ICON_WIDTH
|
|
#if defined(__WXMAC__)
|
|
#include "mac_collapse.xpm"
|
|
#include "mac_expand.xpm"
|
|
#elif defined(__WXGTK__)
|
|
#include "linux_collapse.xpm"
|
|
#include "linux_expand.xpm"
|
|
#else
|
|
#include "default_collapse.xpm"
|
|
#include "default_expand.xpm"
|
|
#endif
|
|
#endif
|
|
|
|
|
|
//#define wxPG_TEXT_INDENT 4 // For the wxComboControl
|
|
#define wxPG_ALLOW_CLIPPING 1 // If 1, GetUpdateRegion() in OnPaint event handler is not ignored
|
|
#define wxPG_GUTTER_DIV 3 // gutter is max(iconwidth/gutter_div,gutter_min)
|
|
#define wxPG_GUTTER_MIN 3 // gutter before and after image of [+] or [-]
|
|
#define wxPG_YSPACING_MIN 1
|
|
#define wxPG_BUTTON_SIZEDEC 0
|
|
#define wxPG_DEFAULT_VSPACING 2 // This matches .NET propertygrid's value,
|
|
// but causes normal combobox to spill out under MSW
|
|
|
|
#define wxPG_OPTIMAL_WIDTH 200 // Arbitrary
|
|
|
|
#define wxPG_CAPRECTXMARGIN 2 // space between caption and selection rectangle,
|
|
#define wxPG_CAPRECTYMARGIN 1 // horizontally and vertically
|
|
|
|
#define PWC_CHILD_SUMMARY_LIMIT 16 // Maximum number of children summarized in a parent property's
|
|
// value field.
|
|
|
|
#define PWC_CHILD_SUMMARY_CHAR_LIMIT 64 // Character limit of summary field when not editing
|
|
|
|
|
|
#define wxPG_MIN_SCROLLBAR_WIDTH 10 // Smallest scrollbar width on any platform
|
|
// Must be larger than largest control border
|
|
// width * 2.
|
|
|
|
|
|
#define wxPG_DEFAULT_CURSOR wxNullCursor
|
|
#define RedrawAllVisible Refresh
|
|
|
|
|
|
//
|
|
// Here are some extra platform dependent defines.
|
|
//
|
|
|
|
#if defined(__WXMSW__)
|
|
// tested
|
|
|
|
#define wxPG_DEFAULT_SPLITTERX 110 // default splitter position
|
|
|
|
#define wxPG_CREATE_CONTROLS_HIDDEN 0 // 1 to create controls out of sight, hide them, and then move them into correct position
|
|
|
|
#define wxPG_NO_CHILD_EVT_MOTION 0 // 1 if splitter drag detect margin and control cannot overlap
|
|
|
|
#define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
|
|
|
|
#define wxPG_ALLOW_EMPTY_TOOLTIPS 1 // If 1, then setting empty tooltip actually hides it
|
|
|
|
#define wxPG_NAT_TEXTCTRL_BORDER_X 0 // Unremovable border of native textctrl.
|
|
#define wxPG_NAT_TEXTCTRL_BORDER_Y 0 // Unremovable border of native textctrl.
|
|
|
|
#define wxPG_NAT_BUTTON_BORDER_ANY 1
|
|
#define wxPG_NAT_BUTTON_BORDER_X 1
|
|
#define wxPG_NAT_BUTTON_BORDER_Y 1
|
|
|
|
#define wxPG_TEXTCTRLYADJUST (m_spacingy+0)
|
|
|
|
#define wxPG_CHOICEXADJUST (-1) // Extra pixels next to wxChoice/ComboBox.
|
|
#define wxPG_CHOICEYADJUST 0 // Extra pixels above wxChoice/ComboBox.
|
|
|
|
#define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 0 // If 1 then controls are refreshed after selected was drawn.
|
|
|
|
#define wxPG_CHECKMARK_XADJ 1
|
|
#define wxPG_CHECKMARK_YADJ (-1)
|
|
#define wxPG_CHECKMARK_WADJ 0
|
|
#define wxPG_CHECKMARK_HADJ 0
|
|
#define wxPG_CHECKMARK_DEFLATE 0
|
|
|
|
#elif defined(__WXGTK__)
|
|
// tested
|
|
|
|
#define wxPG_DEFAULT_SPLITTERX 110
|
|
|
|
#define wxPG_CREATE_CONTROLS_HIDDEN 0 // 1 to create controls out of sight, hide them, and then move them into correct position
|
|
|
|
#define wxPG_NO_CHILD_EVT_MOTION 1 // 1 if splitter drag detect margin and control cannot overlap
|
|
|
|
#define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
|
|
|
|
#define wxPG_ALLOW_EMPTY_TOOLTIPS 0 // If 1, then setting empty tooltip actually hides it
|
|
|
|
#define wxPG_NAT_TEXTCTRL_BORDER_X 3 // Unremovable border of native textctrl.
|
|
#define wxPG_NAT_TEXTCTRL_BORDER_Y 3 // Unremovable border of native textctrl.
|
|
|
|
#define wxPG_NAT_BUTTON_BORDER_ANY 1
|
|
#define wxPG_NAT_BUTTON_BORDER_X 1
|
|
#define wxPG_NAT_BUTTON_BORDER_Y 1
|
|
|
|
#define wxPG_TEXTCTRLYADJUST 0
|
|
|
|
#define wxPG_CHOICEXADJUST 2 // Extra pixels next to wxChoice/ComboBox.
|
|
#define wxPG_CHOICEYADJUST 0
|
|
|
|
#define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 1 // If 1 then controls are refreshed after selected was drawn.
|
|
|
|
#define wxPG_CHECKMARK_XADJ 0
|
|
#define wxPG_CHECKMARK_YADJ 0
|
|
#define wxPG_CHECKMARK_WADJ (-1)
|
|
#define wxPG_CHECKMARK_HADJ (-1)
|
|
#define wxPG_CHECKMARK_DEFLATE 3
|
|
|
|
#elif defined(__WXMAC__)
|
|
// *not* tested
|
|
|
|
#define wxPG_DEFAULT_SPLITTERX 110
|
|
|
|
#define wxPG_CREATE_CONTROLS_HIDDEN 0 // 1 to create controls out of sight, hide them, and then move them into correct position
|
|
|
|
#define wxPG_NO_CHILD_EVT_MOTION 0 // 1 if splitter drag detect margin and control cannot overlap
|
|
|
|
#define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
|
|
|
|
#define wxPG_ALLOW_EMPTY_TOOLTIPS 1 // If 1, then setting empty tooltip actually hides it
|
|
|
|
#define wxPG_NAT_TEXTCTRL_BORDER_X 0 // Unremovable border of native textctrl.
|
|
#define wxPG_NAT_TEXTCTRL_BORDER_Y 0 // Unremovable border of native textctrl.
|
|
|
|
#define wxPG_NAT_BUTTON_BORDER_ANY 0
|
|
#define wxPG_NAT_BUTTON_BORDER_X 0
|
|
#define wxPG_NAT_BUTTON_BORDER_Y 0
|
|
|
|
#define wxPG_TEXTCTRLYADJUST 3
|
|
|
|
#define wxPG_CHOICEXADJUST 0 // Extra pixels next to wxChoice/ComboBox.
|
|
#define wxPG_CHOICEYADJUST 0
|
|
|
|
#define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 0 // If 1 then controls are refreshed after selected was drawn.
|
|
|
|
#define wxPG_CHECKMARK_XADJ 0
|
|
#define wxPG_CHECKMARK_YADJ 0
|
|
#define wxPG_CHECKMARK_WADJ 0
|
|
#define wxPG_CHECKMARK_HADJ 0
|
|
#define wxPG_CHECKMARK_DEFLATE 0
|
|
|
|
#else
|
|
// defaults
|
|
|
|
#define wxPG_DEFAULT_SPLITTERX 110
|
|
|
|
#define wxPG_CREATE_CONTROLS_HIDDEN 0 // 1 to create controls out of sight, hide them, and then move them into correct position
|
|
|
|
#define wxPG_NO_CHILD_EVT_MOTION 1 // 1 if splitter drag detect margin and control cannot overlap
|
|
|
|
#define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
|
|
|
|
#define wxPG_ALLOW_EMPTY_TOOLTIPS 0 // If 1, then setting empty tooltip actually hides it
|
|
|
|
#define wxPG_NAT_TEXTCTRL_BORDER_X 0 // Unremovable border of native textctrl.
|
|
#define wxPG_NAT_TEXTCTRL_BORDER_Y 0 // Unremovable border of native textctrl.
|
|
|
|
#define wxPG_NAT_BUTTON_BORDER_ANY 0
|
|
#define wxPG_NAT_BUTTON_BORDER_X 0
|
|
#define wxPG_NAT_BUTTON_BORDER_Y 0
|
|
|
|
#define wxPG_TEXTCTRLYADJUST 0
|
|
|
|
#define wxPG_CHOICEXADJUST 0 // Extra pixels next to wxChoice/ComboBox.
|
|
#define wxPG_CHOICEYADJUST 0
|
|
|
|
#define wxPG_REFRESH_CONTROLS_AFTER_REPAINT 1 // If 1 then controls are refreshed after selected was drawn.
|
|
|
|
#define wxPG_CHECKMARK_XADJ 0
|
|
#define wxPG_CHECKMARK_YADJ 0
|
|
#define wxPG_CHECKMARK_WADJ 0
|
|
#define wxPG_CHECKMARK_HADJ 0
|
|
#define wxPG_CHECKMARK_DEFLATE 0
|
|
|
|
#endif
|
|
|
|
|
|
#if wxPG_NO_CHILD_EVT_MOTION
|
|
|
|
#define wxPG_SPLITTERX_DETECTMARGIN1 3 // this much on left
|
|
#define wxPG_SPLITTERX_DETECTMARGIN2 2 // this much on right
|
|
#define wxPG_CONTROL_MARGIN 0 // space between splitter and control
|
|
|
|
#else
|
|
|
|
#define wxPG_SPLITTERX_DETECTMARGIN1 3 // this much on left
|
|
#define wxPG_SPLITTERX_DETECTMARGIN2 2 // this much on right
|
|
#define wxPG_CONTROL_MARGIN 0 // space between splitter and control
|
|
|
|
#endif
|
|
|
|
|
|
#define wxCC_CUSTOM_IMAGE_MARGIN1 4 // before image
|
|
#define wxCC_CUSTOM_IMAGE_MARGIN2 5 // after image
|
|
|
|
|
|
#if (!wxPG_NAT_TEXTCTRL_BORDER_X && !wxPG_NAT_TEXTCTRL_BORDER_Y)
|
|
#define wxPG_ENABLE_CLIPPER_WINDOW 0
|
|
#else
|
|
#define wxPG_ENABLE_CLIPPER_WINDOW 1
|
|
#endif
|
|
|
|
|
|
//#define wxPG_NAT_CHOICE_BORDER_ANY 0
|
|
|
|
|
|
// for odcombo
|
|
#undef wxPG_CHOICEXADJUST
|
|
#define wxPG_CHOICEXADJUST 0
|
|
#undef wxPG_CHOICEYADJUST
|
|
#define wxPG_CHOICEYADJUST 0
|
|
|
|
#define wxPG_DRAG_MARGIN 30
|
|
|
|
#define wxPG_CUSTOM_IMAGE_SPACINGY 1 // space between vertical sides of a custom image
|
|
|
|
// Use this macro to generate standard custom image height from
|
|
#define wxPG_STD_CUST_IMAGE_HEIGHT(LINEHEIGHT) (LINEHEIGHT-3)
|
|
|
|
// How many pixels between textctrl and button
|
|
#ifdef __WXMAC__
|
|
#define wxPG_TEXTCTRL_AND_BUTTON_SPACING 8
|
|
#else
|
|
#define wxPG_TEXTCTRL_AND_BUTTON_SPACING 2
|
|
#endif
|
|
|
|
#define wxPG_HIDER_BUTTON_HEIGHT 25
|
|
|
|
// m_expanded of wxPGPropertyWithChildren is set to this code if children should
|
|
// not be deleted in destructor.
|
|
#define wxPG_EXP_OF_COPYARRAY 127
|
|
|
|
#define wxPG_PIXELS_PER_UNIT m_lineHeight
|
|
|
|
#ifdef wxPG_ICON_WIDTH
|
|
#define m_iconHeight m_iconWidth
|
|
#endif
|
|
|
|
#define wxPG_TOOLTIP_DELAY 1000
|
|
|
|
// Colour for the empty but visible space below last property.
|
|
#define wxPG_SLACK_BACKROUND m_colPropBack
|
|
|
|
// Milliseconds to wait for two mouse-ups after focus inorder
|
|
// to trigger a double-click.
|
|
#define DOUBLE_CLICK_CONVERSION_TRESHOLD 500
|
|
|
|
|
|
//
|
|
// Parenting types
|
|
enum
|
|
{
|
|
PT_CUSTOMPROPERTY = -2,
|
|
PT_FIXEDCHILDREN = -1,
|
|
PT_NONE = 0,
|
|
PT_CAPTION = 1,
|
|
PT_ROOT = 2
|
|
};
|
|
|
|
|
|
// Helper to decide which way is better (ie. first macro clears
|
|
// "unspecified" state of siblings of child properties as well, while the latter is
|
|
// more precise).
|
|
//#define CLEAR_PROPERTY_UNSPECIFIED_FLAG(p) wxPropertyGridState::ClearPropertyAndChildrenFlags(p,wxPG_PROP_UNSPECIFIED)
|
|
#define CLEAR_PROPERTY_UNSPECIFIED_FLAG(p) p->ClearFlag(wxPG_PROP_UNSPECIFIED)
|
|
|
|
#define __INTENSE_DEBUGGING__ 0
|
|
#define __PAINT_DEBUGGING__ 0
|
|
#define __MOUSE_DEBUGGING__ 0
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
#if wxUSE_INTL
|
|
void wxPropertyGrid::AutoGetTranslation ( bool enable )
|
|
{
|
|
WX_PG_GLOBALS_LOCKER()
|
|
|
|
wxPGGlobalVars->m_autoGetTranslation = enable;
|
|
}
|
|
#else
|
|
void wxPropertyGrid::AutoGetTranslation ( bool ) { }
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// This was needed to make quicker progress towards wxPropertyGridState
|
|
#define FROM_STATE(X) m_pState->X
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
#if !wxCHECK_VERSION(2, 7, 1)
|
|
|
|
#if defined(__WXMSW__)
|
|
|
|
#ifndef WS_EX_COMPOSITED
|
|
#define WS_EX_COMPOSITED 0x02000000L
|
|
#endif
|
|
static bool wxPGIsWindowBuffered( const wxWindow* wnd )
|
|
{
|
|
while ( wnd )
|
|
{
|
|
if ( GetWindowLong((HWND)wnd->GetHWND(), GWL_EXSTYLE) & WS_EX_COMPOSITED )
|
|
return true;
|
|
if ( wnd->IsTopLevel() )
|
|
break;
|
|
wnd = wnd->GetParent();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#elif defined(__WXGTK20__)
|
|
|
|
#include <gtk/gtk.h>
|
|
static bool wxPGIsWindowBuffered( const wxWindow* wnd )
|
|
{
|
|
return GTK_WIDGET_DOUBLE_BUFFERED(wnd->GetHandle());
|
|
}
|
|
|
|
#elif defined(__WXMAC_OSX__) || defined(__WXCOCOA__) || defined(__WXDFB__)
|
|
|
|
static bool wxPGIsWindowBuffered( const wxWindow* WXUNUSED(wnd) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#else
|
|
|
|
static bool wxPGIsWindowBuffered( const wxWindow* WXUNUSED(wnd) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
static bool wxPGIsWindowBuffered( const wxWindow* wnd )
|
|
{
|
|
return wnd->IsDoubleBuffered();
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// DeviceContext Init Macros.
|
|
|
|
#define wxPG_CLIENT_DC_INIT() \
|
|
wxClientDC dc(this); \
|
|
PrepareDC(dc);
|
|
|
|
#define wxPG_CLIENT_DC_INIT_R(RETVAL) \
|
|
wxClientDC dc(this); \
|
|
PrepareDC(dc);
|
|
|
|
#define wxPG_PAINT_DC_INIT() \
|
|
wxPaintDC dc(this); \
|
|
PrepareDC(dc);
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// For wxMSW cursor consistency, we must do mouse capturing even
|
|
// when using custom controls.
|
|
|
|
#define BEGIN_MOUSE_CAPTURE \
|
|
if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) ) \
|
|
{ \
|
|
CaptureMouse(); \
|
|
m_iFlags |= wxPG_FL_MOUSE_CAPTURED; \
|
|
}
|
|
|
|
#define END_MOUSE_CAPTURE \
|
|
if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED ) \
|
|
{ \
|
|
ReleaseMouse(); \
|
|
m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED); \
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// NOTES
|
|
// -----------------------------------------------------------------------
|
|
|
|
//
|
|
// -----------------------------------------------------------------------
|
|
// TODO
|
|
// -----------------------------------------------------------------------
|
|
//
|
|
|
|
//
|
|
// For Next Release:
|
|
// * Fix NULL(?) focus after odcombo closed.
|
|
//
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
const wxChar *wxPropertyGridNameStr = wxT("wxPropertyGrid");
|
|
|
|
const wxChar *wxPGTypeName_long = wxT("long");
|
|
const wxChar *wxPGTypeName_bool = wxT("bool");
|
|
const wxChar *wxPGTypeName_double = wxT("double");
|
|
const wxChar *wxPGTypeName_wxString = wxT("string");
|
|
const wxChar *wxPGTypeName_void = wxT("void*");
|
|
const wxChar *wxPGTypeName_wxArrayString = wxT("arrstring");
|
|
|
|
#ifdef __WXPYTHON__
|
|
const wxChar *wxPGTypeName_PyObject = wxT("PyObject");
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
static void wxPGDrawFocusRect( wxDC& dc, const wxRect& rect )
|
|
{
|
|
#if defined(__WXMSW__) && !defined(__WXWINCE__)
|
|
/*
|
|
RECT mswRect;
|
|
mswRect.left = rect.x;
|
|
mswRect.top = rect.y;
|
|
mswRect.right = rect.x + rect.width;
|
|
mswRect.bottom = rect.y + rect.height;
|
|
HDC hdc = (HDC) dc.GetHDC();
|
|
SetMapMode(hdc,MM_TEXT); // Just in case...
|
|
DrawFocusRect(hdc,&mswRect);
|
|
*/
|
|
// FIXME: Use DrawFocusRect code above (currently it draws solid line
|
|
// for caption focus but works ok for other stuff).
|
|
// Also, it seems that this code may not work in future wx versions.
|
|
dc.SetLogicalFunction(wxINVERT);
|
|
|
|
wxPen pen(*wxBLACK,1,wxDOT);
|
|
pen.SetCap(wxCAP_BUTT);
|
|
dc.SetPen(pen);
|
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
|
|
|
dc.DrawRectangle(rect);
|
|
|
|
dc.SetLogicalFunction(wxCOPY);
|
|
#else
|
|
dc.SetLogicalFunction(wxINVERT);
|
|
|
|
dc.SetPen(wxPen(*wxBLACK,1,wxDOT));
|
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
|
|
|
dc.DrawRectangle(rect);
|
|
|
|
dc.SetLogicalFunction(wxCOPY);
|
|
#endif
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Choice related methods from various classes
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyContainerMethods::AddPropertyChoice( wxPGId id,
|
|
const wxString& label,
|
|
int value )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
p->InsertChoice(label,-1,value);
|
|
}
|
|
|
|
|
|
void wxPropertyContainerMethods::InsertPropertyChoice( wxPGId id,
|
|
const wxString& label,
|
|
int index,
|
|
int value )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
p->InsertChoice(label,index,value);
|
|
}
|
|
|
|
|
|
void wxPropertyContainerMethods::DeletePropertyChoice( wxPGId id,
|
|
int index )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
p->DeleteChoice(index);
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Statics in one class for easy destruction.
|
|
// NB: We prefer to use wxModule, as it offers more consistent behaviour
|
|
// across platforms. However, for those rare problem situations, we
|
|
// also need to offer option to use simpler approach.
|
|
// -----------------------------------------------------------------------
|
|
|
|
#ifndef wxPG_USE_WXMODULE
|
|
#define wxPG_USE_WXMODULE 1
|
|
#endif
|
|
|
|
#if wxPG_USE_WXMODULE
|
|
|
|
#include <wx/module.h>
|
|
|
|
class wxPGGlobalVarsClassManager : public wxModule
|
|
{
|
|
DECLARE_DYNAMIC_CLASS(wxPGGlobalVarsClassManager)
|
|
public:
|
|
wxPGGlobalVarsClassManager() {}
|
|
virtual bool OnInit() { wxPGGlobalVars = new wxPGGlobalVarsClass(); return true; }
|
|
virtual void OnExit() { delete wxPGGlobalVars; wxPGGlobalVars = NULL; }
|
|
};
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager, wxModule)
|
|
|
|
#else // !wxPG_USE_WXMODULE
|
|
|
|
class wxPGGlobalVarsClassManager
|
|
{
|
|
public:
|
|
wxPGGlobalVarsClassManager() {}
|
|
~wxPGGlobalVarsClassManager() { delete wxPGGlobalVars; }
|
|
};
|
|
|
|
static wxPGGlobalVarsClassManager gs_pgGlobalVarsClassManager;
|
|
|
|
#endif
|
|
|
|
|
|
wxPGGlobalVarsClass* wxPGGlobalVars = (wxPGGlobalVarsClass*) NULL;
|
|
|
|
|
|
wxPGGlobalVarsClass::wxPGGlobalVarsClass()
|
|
{
|
|
m_boolChoices[0] = _("False");
|
|
m_boolChoices[1] = _("True");
|
|
m_boolChoices[2] = _("Unspecified");
|
|
m_numBoolChoices = 2;
|
|
|
|
m_fontFamilyChoices = (wxPGChoices*) NULL;
|
|
|
|
m_autoGetTranslation = false;
|
|
|
|
m_offline = 0;
|
|
}
|
|
|
|
|
|
wxPGGlobalVarsClass::~wxPGGlobalVarsClass()
|
|
{
|
|
size_t i;
|
|
|
|
// This will always have one ref
|
|
delete m_fontFamilyChoices;
|
|
|
|
#if wxUSE_VALIDATORS
|
|
for ( i=0; i<m_arrValidators.GetCount(); i++ )
|
|
delete ((wxValidator*)m_arrValidators[i]);
|
|
#endif
|
|
|
|
//
|
|
// Destroy value type class instances.
|
|
wxPGHashMapS2P::iterator vt_it;
|
|
|
|
for( vt_it = m_dictValueType.begin(); vt_it != m_dictValueType.end(); ++vt_it )
|
|
{
|
|
wxPGValueType* pcls = (wxPGValueType*) vt_it->second;
|
|
wxASSERT( pcls );
|
|
delete pcls;
|
|
}
|
|
|
|
// Destroy editor class instances.
|
|
// iterate over all the elements in the class
|
|
for( vt_it = m_mapEditorClasses.begin(); vt_it != m_mapEditorClasses.end(); ++vt_it )
|
|
{
|
|
delete ((wxPGEditor*)vt_it->second);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGProperty
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGPropertyClassInfo wxBasePropertyClassInfo = {wxT("wxBaseProperty"),
|
|
(const wxPGPropertyClassInfo*) NULL,
|
|
(wxPGPropertyConstructor) NULL};
|
|
|
|
void wxPGProperty::Init()
|
|
{
|
|
#ifdef __WXPYTHON__
|
|
m_scriptObject = NULL;
|
|
#endif
|
|
|
|
m_y = -3;
|
|
m_arrIndex = 0xFFFF;
|
|
m_parent = (wxPGPropertyWithChildren*) NULL;
|
|
|
|
#if wxPG_USE_CLIENT_DATA
|
|
m_clientData = NULL;
|
|
#endif
|
|
|
|
m_dataExt = (wxPGPropertyDataExt*) NULL;
|
|
|
|
m_maxLen = 0; // infinite maximum length
|
|
|
|
m_flags = 0;
|
|
|
|
m_depth = 1;
|
|
m_parentingType = 0;
|
|
m_bgColIndex = 0;
|
|
m_fgColIndex = 0;
|
|
}
|
|
|
|
|
|
void wxPGProperty::Init( const wxString& label, const wxString& name )
|
|
{
|
|
m_label = label;
|
|
#ifndef __WXPYTHON__
|
|
if ( &name != ((wxString*)NULL) )
|
|
#else
|
|
if ( (&name != ((wxString*)NULL)) && name != wxT("_LABEL_AS_NAME") )
|
|
#endif
|
|
DoSetName( name );
|
|
else
|
|
DoSetName( label );
|
|
|
|
Init();
|
|
}
|
|
|
|
wxPGProperty::wxPGProperty()
|
|
#if wxPG_INCLUDE_WXOBJECT
|
|
: wxObject()
|
|
#endif
|
|
{
|
|
Init();
|
|
}
|
|
|
|
|
|
wxPGProperty::wxPGProperty( const wxString& label, const wxString& name )
|
|
#if wxPG_INCLUDE_WXOBJECT
|
|
: wxObject()
|
|
#endif
|
|
{
|
|
Init( label, name );
|
|
}
|
|
|
|
|
|
wxPGProperty::~wxPGProperty()
|
|
{
|
|
#ifdef __WXPYTHON__
|
|
#if wxPG_USE_CLIENT_DATA
|
|
if ( m_clientData )
|
|
Py_DECREF( m_clientData );
|
|
#endif
|
|
#endif
|
|
|
|
delete m_dataExt;
|
|
}
|
|
|
|
|
|
bool wxPGProperty::IsSomeParent( wxPGProperty* candidate ) const
|
|
{
|
|
wxPGPropertyWithChildren* parent = m_parent;
|
|
do
|
|
{
|
|
if ( parent == (wxPGPropertyWithChildren*)candidate )
|
|
return true;
|
|
parent = parent->m_parent;
|
|
} while ( parent );
|
|
return false;
|
|
}
|
|
|
|
|
|
wxPropertyGridState* wxPGProperty::GetParentState() const
|
|
{
|
|
wxASSERT( m_parent );
|
|
return m_parent->GetParentState();
|
|
}
|
|
|
|
|
|
size_t wxPGProperty::GetChildCount() const
|
|
{
|
|
int cc = GetParentingType();
|
|
if ( cc == 0 ) return 0;
|
|
return ((wxPGPropertyWithChildren*)this)->GetCount();
|
|
}
|
|
|
|
|
|
void wxPGProperty::ShowError( const wxString& msg )
|
|
{
|
|
if ( !msg.length() )
|
|
return;
|
|
|
|
#if wxUSE_STATUSBAR
|
|
if ( !wxPGGlobalVars->m_offline )
|
|
{
|
|
wxPropertyGrid* pg = GetParentState()->m_pPropGrid;
|
|
wxASSERT(pg);
|
|
wxWindow* topWnd = ::wxGetTopLevelParent(pg);
|
|
if ( topWnd )
|
|
{
|
|
wxFrame* pFrame = wxDynamicCast(topWnd,wxFrame);
|
|
if ( pFrame )
|
|
{
|
|
wxStatusBar* pStatusBar = pFrame->GetStatusBar();
|
|
if ( pStatusBar )
|
|
{
|
|
pStatusBar->SetStatusText(msg);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
::wxLogError(msg);
|
|
}
|
|
|
|
|
|
wxPropertyGrid* wxPGProperty::GetGrid() const
|
|
{
|
|
return GetParentState()->GetGrid();
|
|
}
|
|
|
|
|
|
void wxPGProperty::UpdateControl( wxWindow* primary )
|
|
{
|
|
if ( primary )
|
|
GetEditorClass()->UpdateControl(this,primary);
|
|
}
|
|
|
|
|
|
void wxPGProperty::DoSetValue( wxPGVariant )
|
|
{
|
|
// Actually, this should never get called
|
|
wxFAIL_MSG( wxT("must be overridden") );
|
|
}
|
|
|
|
|
|
// wxPGRootPropertyClass, at least, should make use of this.
|
|
wxPGVariant wxPGProperty::DoGetValue() const
|
|
{
|
|
return wxPGVariant((long)0);
|
|
}
|
|
|
|
|
|
wxString wxPGProperty::GetValueAsString( int ) const
|
|
{
|
|
wxFAIL_MSG( wxT("must be overridden") );
|
|
return m_name;
|
|
}
|
|
|
|
wxVariant wxPGProperty::GetValueAsVariant() const
|
|
{
|
|
// Return NULL variant for unspecified value
|
|
//if ( HasFlag(wxPG_PROP_UNSPECIFIED) )
|
|
// return wxVariant();
|
|
|
|
wxPGVariant value = DoGetValue();
|
|
const wxPGValueType* typeClass = GetValueTypePtr();
|
|
wxASSERT_MSG( typeClass, wxT("Did you forgot to use wxPG_INIT_REQUIRED_TYPE(T) in constructor?") );
|
|
return typeClass->GenerateVariant(value,m_name);
|
|
}
|
|
|
|
bool wxPGProperty::SetValueFromString( const wxString&, int )
|
|
{
|
|
wxFAIL_MSG( wxT("must be overridden") );
|
|
return false;
|
|
}
|
|
|
|
|
|
bool wxPGProperty::SetValueFromInt( long, int )
|
|
{
|
|
wxFAIL_MSG ( wxT("must be overridden") );
|
|
return false;
|
|
}
|
|
|
|
|
|
wxSize wxPGProperty::GetImageSize() const
|
|
{
|
|
if ( m_dataExt && m_dataExt->m_valueBitmap )
|
|
return wxSize(m_dataExt->m_valueBitmap->GetWidth(),-1);
|
|
|
|
return wxSize(0,0);
|
|
}
|
|
|
|
|
|
void wxPGProperty::OnCustomPaint( wxDC& dc,
|
|
const wxRect& rect,
|
|
wxPGPaintData& )
|
|
{
|
|
wxCHECK_RET( m_dataExt, wxT("m_dataExt is mandatory") );
|
|
|
|
wxBitmap* bmp = m_dataExt->m_valueBitmap;
|
|
|
|
wxCHECK_RET( bmp && bmp->Ok(), wxT("invalid bitmap") );
|
|
|
|
wxCHECK_RET( rect.x >= 0, wxT("unexpected measure call") );
|
|
|
|
dc.DrawBitmap(*bmp,rect.x,rect.y);
|
|
}
|
|
|
|
const wxPGEditor* wxPGProperty::DoGetEditorClass() const
|
|
{
|
|
return wxPG_EDITOR(TextCtrl);
|
|
}
|
|
|
|
|
|
#ifdef __WXPYTHON__
|
|
wxString wxPGProperty::GetEditor() const
|
|
{
|
|
return wxEmptyString;
|
|
}
|
|
#endif
|
|
|
|
#ifdef __WXPYTHON__
|
|
wxString wxPGProperty::GetType() const
|
|
{
|
|
return wxString();
|
|
}
|
|
|
|
const wxPGValueType* wxPGProperty::GetValueType() const
|
|
{
|
|
wxString s = GetType();
|
|
|
|
const wxPGValueType* p = wxPropertyContainerMethods::GetValueType(s);
|
|
|
|
wxCHECK_MSG( p, wxPG_VALUETYPE(none),
|
|
wxT("GetType must return string that identifies a valid type") );
|
|
|
|
return p;
|
|
}
|
|
#endif
|
|
|
|
#if wxPG_VALUETYPE_IS_STRING
|
|
const wxPGValueType* wxPGProperty::GetValueTypePtr() const
|
|
{
|
|
return wxPropertyContainerMethods::GetValueTypeByName(GetValueType());
|
|
}
|
|
#endif
|
|
|
|
|
|
// Default extra property event handling - that is, none at all.
|
|
bool wxPGProperty::OnEvent( wxPropertyGrid*, wxWindow*, wxEvent& )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
void wxPGProperty::SetChoiceSelection( int newValue, const wxPGChoiceInfo& choiceInfo )
|
|
{
|
|
// Changes value of a property with choices, but only
|
|
// works if the value type is long or string.
|
|
const wxPGValueType* vt = GetValueTypePtr();
|
|
|
|
wxCHECK_RET( choiceInfo.m_choices, wxT("invalid choiceinfo") );
|
|
|
|
if ( vt == wxPG_VALUETYPE_PTR(long) )
|
|
{
|
|
DoSetValue( (long) newValue );
|
|
}
|
|
else if ( vt == wxPG_VALUETYPE_PTR(wxString) )
|
|
{
|
|
DoSetValue( choiceInfo.m_choices->GetLabel(newValue) );
|
|
}
|
|
}
|
|
|
|
|
|
int wxPGProperty::InsertChoice( const wxString& label, int index, int value )
|
|
{
|
|
wxPropertyGrid* pg = GetGrid();
|
|
|
|
wxPGChoiceInfo ci;
|
|
ci.m_choices = (wxPGChoices*) NULL;
|
|
int sel = GetChoiceInfo(&ci);
|
|
|
|
if ( ci.m_choices )
|
|
{
|
|
int newSel = sel;
|
|
|
|
if ( index < 0 )
|
|
index = ci.m_choices->GetCount();
|
|
|
|
if ( index <= sel )
|
|
newSel++;
|
|
|
|
ci.m_choices->Insert(label, index, value);
|
|
|
|
if ( sel != newSel )
|
|
SetChoiceSelection(newSel, ci);
|
|
|
|
if ( this == wxPGIdToPtr(pg->GetSelection()) )
|
|
GetEditorClass()->InsertItem(pg->GetPrimaryEditor(),label,index);
|
|
|
|
return index;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
void wxPGProperty::DeleteChoice( int index )
|
|
{
|
|
wxPropertyGrid* pg = GetGrid();
|
|
|
|
wxPGChoiceInfo ci;
|
|
ci.m_choices = (wxPGChoices*) NULL;
|
|
int sel = GetChoiceInfo(&ci);
|
|
|
|
if ( ci.m_choices )
|
|
{
|
|
int newSel = sel;
|
|
|
|
// Adjust current value
|
|
if ( sel == index )
|
|
{
|
|
SetFlag( wxPG_PROP_UNSPECIFIED );
|
|
newSel = 0;
|
|
}
|
|
else if ( index < sel )
|
|
{
|
|
newSel--;
|
|
}
|
|
|
|
ci.m_choices->RemoveAt(index);
|
|
|
|
if ( sel != newSel )
|
|
SetChoiceSelection(newSel, ci);
|
|
|
|
if ( this == wxPGIdToPtr(pg->GetSelection()) )
|
|
GetEditorClass()->DeleteItem(pg->GetPrimaryEditor(), index);
|
|
}
|
|
}
|
|
|
|
|
|
int wxPGProperty::GetChoiceInfo( wxPGChoiceInfo* )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
void wxPGProperty::SetAttribute( int, wxVariant& )
|
|
{
|
|
}
|
|
|
|
|
|
#if wxUSE_VALIDATORS
|
|
wxValidator* wxPGProperty::DoGetValidator() const
|
|
{
|
|
return (wxValidator*) NULL;
|
|
}
|
|
#endif
|
|
|
|
|
|
bool wxPGProperty::SetChoices( wxPGChoices& choices )
|
|
{
|
|
wxPGChoiceInfo ci;
|
|
ci.m_choices = (wxPGChoices*) NULL;
|
|
|
|
// Unref existing
|
|
GetChoiceInfo(&ci);
|
|
if ( ci.m_choices )
|
|
{
|
|
ci.m_choices->Assign(choices);
|
|
|
|
// This may be needed to trigger some initialization
|
|
// (but don't do it if property is somewhat uninitialized)
|
|
if ( m_parent )
|
|
DoSetValue(GetValueTypePtr()->GetDefaultValue());
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
const wxPGEditor* wxPGProperty::GetEditorClass() const
|
|
{
|
|
const wxPGEditor* editor;
|
|
|
|
if ( !m_dataExt || !m_dataExt->m_customEditor )
|
|
{
|
|
#ifdef __WXPYTHON__
|
|
wxString editorName = GetEditor();
|
|
if ( editorName.length() )
|
|
editor = wxPropertyContainerMethods::GetEditorByName(editorName);
|
|
else
|
|
#endif
|
|
editor = DoGetEditorClass();
|
|
}
|
|
else
|
|
{
|
|
editor = m_dataExt->m_customEditor;
|
|
}
|
|
|
|
return editor;
|
|
}
|
|
|
|
|
|
bool wxPGProperty::IsKindOf( wxPGPropertyClassInfo& info )
|
|
{
|
|
const wxPGPropertyClassInfo* ownInfo = GetClassInfo();
|
|
|
|
do
|
|
{
|
|
if ( ownInfo == &info )
|
|
return true;
|
|
|
|
ownInfo = ownInfo->m_baseInfo;
|
|
} while ( ownInfo );
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// Privatizes set of choices
|
|
void wxPGProperty::SetChoicesExclusive()
|
|
{
|
|
wxPGChoiceInfo ci;
|
|
ci.m_choices = (wxPGChoices*) NULL;
|
|
|
|
GetChoiceInfo(&ci);
|
|
if ( ci.m_choices )
|
|
ci.m_choices->SetExclusive();
|
|
}
|
|
|
|
|
|
bool wxPGProperty::PrepareValueForDialogEditing( wxPropertyGrid* propGrid )
|
|
{
|
|
wxWindow* primary = propGrid->GetEditorControl();
|
|
if ( primary && propGrid->IsEditorsValueModified() )
|
|
{
|
|
GetEditorClass()->CopyValueFromControl( this, primary );
|
|
return true;
|
|
}
|
|
else if ( m_flags & wxPG_PROP_UNSPECIFIED )
|
|
{
|
|
// Set default value in case it was unspecified
|
|
DoSetValue(GetValueTypePtr()->GetDefaultValue());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool wxPGProperty::RecreateEditor()
|
|
{
|
|
wxPropertyGrid* pg = GetGrid();
|
|
wxASSERT(pg);
|
|
|
|
wxPGProperty* selected = pg->GetSelection();
|
|
if ( this == selected )
|
|
{
|
|
pg->DoSelectProperty(this, wxPG_SEL_FORCE);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool wxPGProperty::EnsureDataExt()
|
|
{
|
|
if ( !m_dataExt )
|
|
{
|
|
m_dataExt = new wxPGPropertyDataExt();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void wxPGProperty::SetValueImage( wxBitmap& bmp )
|
|
{
|
|
EnsureDataExt();
|
|
|
|
delete m_dataExt->m_valueBitmap;
|
|
|
|
if ( &bmp && bmp.Ok() )
|
|
{
|
|
// Resize the image
|
|
wxSize maxSz = GetGrid()->GetImageSize();
|
|
wxSize imSz(bmp.GetWidth(),bmp.GetHeight());
|
|
|
|
if ( imSz.x != maxSz.x || imSz.y != maxSz.y )
|
|
{
|
|
// Create a memory DC
|
|
wxBitmap* bmpNew = new wxBitmap(maxSz.x,maxSz.y,bmp.GetDepth());
|
|
|
|
wxMemoryDC dc;
|
|
dc.SelectObject(*bmpNew);
|
|
|
|
// Scale
|
|
// FIXME: This is ugly - use image or wait for scaling patch.
|
|
double scaleX = (double)maxSz.x / (double)imSz.x;
|
|
double scaleY = (double)maxSz.y / (double)imSz.y;
|
|
|
|
dc.SetUserScale(scaleX,scaleY);
|
|
|
|
dc.DrawBitmap( bmp, 0, 0 );
|
|
|
|
m_dataExt->m_valueBitmap = bmpNew;
|
|
}
|
|
else
|
|
m_dataExt->m_valueBitmap = new wxBitmap(bmp);
|
|
|
|
m_flags |= wxPG_PROP_CUSTOMIMAGE;
|
|
}
|
|
else
|
|
{
|
|
m_dataExt->m_valueBitmap = (wxBitmap*) NULL;
|
|
m_flags &= ~(wxPG_PROP_CUSTOMIMAGE);
|
|
}
|
|
}
|
|
|
|
|
|
wxPGProperty* wxPGProperty::GetMainParent() const
|
|
{
|
|
const wxPGProperty* curChild = this;
|
|
const wxPGPropertyWithChildren* curParent = m_parent;
|
|
|
|
while ( curParent->m_parentingType < 0 )
|
|
{
|
|
curChild = curParent;
|
|
curParent = curParent->m_parent;
|
|
}
|
|
|
|
return (wxPGProperty*) curChild;
|
|
}
|
|
|
|
|
|
const wxPGProperty* wxPGProperty::GetLastVisibleSubItem() const
|
|
{
|
|
//
|
|
// Returns last visible sub-item, recursively.
|
|
|
|
if ( GetParentingType() == PT_NONE )
|
|
return this;
|
|
|
|
const wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*) this;
|
|
|
|
unsigned int count = pwc->GetCount();
|
|
|
|
if ( !pwc->IsExpanded() || !count )
|
|
return this;
|
|
|
|
return pwc->Last()->GetLastVisibleSubItem();
|
|
}
|
|
|
|
|
|
bool wxPGProperty::UsesAutoUnspecified() const
|
|
{
|
|
if ( GetGrid()->GetExtraStyle() & wxPG_EX_AUTO_UNSPECIFIED_VALUES )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGPropertyWithChildren
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
wxPGPropertyClassInfo wxBaseParentPropertyClassInfo = {wxT("wxBaseParentProperty"),
|
|
&wxBasePropertyClassInfo,
|
|
(wxPGPropertyConstructor) NULL};
|
|
|
|
|
|
wxPGPropertyWithChildren::wxPGPropertyWithChildren()
|
|
: wxPGProperty()
|
|
{
|
|
m_expanded = 1;
|
|
m_y = -2;
|
|
m_parentingType = -1;
|
|
}
|
|
|
|
wxPGPropertyWithChildren::wxPGPropertyWithChildren( const wxString &label, const wxString& name )
|
|
: wxPGProperty(label,name)
|
|
{
|
|
m_expanded = 1;
|
|
m_y = -2;
|
|
m_parentingType = -1;
|
|
m_parentState = (wxPropertyGridState*) NULL;
|
|
}
|
|
|
|
|
|
wxPGPropertyWithChildren::~wxPGPropertyWithChildren()
|
|
{
|
|
Empty(); // this deletes items
|
|
}
|
|
|
|
|
|
// This is used by Insert etc.
|
|
void wxPGPropertyWithChildren::AddChild2( wxPGProperty* prop, int index, bool correct_mode )
|
|
{
|
|
if ( index < 0 || (size_t)index >= m_children.GetCount() )
|
|
{
|
|
if ( correct_mode ) prop->m_arrIndex = m_children.GetCount();
|
|
m_children.Add( (void*)prop );
|
|
}
|
|
else
|
|
{
|
|
m_children.Insert( (void*)prop, index );
|
|
if ( correct_mode ) FixIndexesOfChildren( index );
|
|
}
|
|
|
|
prop->m_parent = this;
|
|
}
|
|
|
|
// This is used by properties that have fixed sub-properties
|
|
void wxPGPropertyWithChildren::AddChild( wxPGProperty* prop )
|
|
{
|
|
prop->m_arrIndex = m_children.GetCount();
|
|
m_children.Add( (void*)prop );
|
|
|
|
int custImgHeight = prop->GetImageSize().y;
|
|
if ( custImgHeight < 0 /*|| custImgHeight > 1*/ )
|
|
prop->m_flags |= wxPG_PROP_CUSTOMIMAGE;
|
|
|
|
prop->m_parent = this;
|
|
|
|
prop->m_y = -1; // Collapsed
|
|
}
|
|
|
|
|
|
void wxPGPropertyWithChildren::FixIndexesOfChildren( size_t starthere )
|
|
{
|
|
size_t i;
|
|
for ( i=starthere;i<GetCount();i++)
|
|
Item(i)->m_arrIndex = i;
|
|
}
|
|
|
|
|
|
// Returns (direct) child property with given name (or NULL if not found)
|
|
wxPGProperty* wxPGPropertyWithChildren::GetPropertyByName( const wxString& name ) const
|
|
{
|
|
size_t i;
|
|
|
|
for ( i=0; i<GetCount(); i++ )
|
|
{
|
|
wxPGProperty* p = Item(i);
|
|
if ( p->m_name == name )
|
|
return p;
|
|
}
|
|
|
|
// Does it have point, then?
|
|
int pos = name.Find(wxT('.'));
|
|
if ( pos <= 0 )
|
|
return (wxPGProperty*) NULL;
|
|
|
|
wxPGPropertyWithChildren* pwc =
|
|
(wxPGPropertyWithChildren*) GetPropertyByName(name.substr(0,pos));
|
|
|
|
if ( !pwc || !pwc->GetParentingType() )
|
|
return (wxPGProperty*) NULL;
|
|
|
|
return pwc->GetPropertyByName(name.substr(pos+1,name.length()-pos-1));
|
|
}
|
|
|
|
|
|
wxPGProperty* wxPGPropertyWithChildren::GetItemAtY( unsigned int y, unsigned int lh )
|
|
{
|
|
// Linear search.
|
|
unsigned int i = 0;
|
|
unsigned int iMax = GetCount();
|
|
unsigned long py = 0xFFFFFFFF;
|
|
wxPGProperty* p = (wxPGProperty*) NULL;
|
|
|
|
while ( i < iMax )
|
|
{
|
|
p = Item(i);
|
|
if ( p->m_y >= 0 )
|
|
{
|
|
py = (unsigned long)p->m_y;
|
|
if ( (py+lh) > y )
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
if ( py <= y && i < iMax )
|
|
{
|
|
// perfectly this item
|
|
wxASSERT_MSG( p, wxT("invalid property id") );
|
|
return p;
|
|
}
|
|
else
|
|
{
|
|
|
|
// If no visible children, we must retract our steps
|
|
// (should not really happen, so right now we check that it
|
|
// really doesn't).
|
|
if ( py == 0xFFFFFFFF )
|
|
{
|
|
wxLogDebug(wxT("wxPropertyGrid: \"%s\" (y=%i) did not have visible children (it should)."),m_label.c_str(),(int)m_y);
|
|
return (wxPGProperty*) NULL;
|
|
}
|
|
|
|
// We are about to return a child of previous' visible item.
|
|
|
|
#ifdef __WXDEBUG__
|
|
if ( i < 1 )
|
|
{
|
|
wxLogDebug( wxT("WARNING: \"%s\"->GetItemAtY: (i <= 0)"), m_label.c_str() );
|
|
wxLogDebug( wxT(" \\--> y = %i, py = %i"), (int)y, (int)py );
|
|
if ( p )
|
|
wxLogDebug( wxT(" \\--> p = \"%s\""), p->GetLabel().c_str() );
|
|
else
|
|
wxLogDebug( wxT(" \\--> p = None") );
|
|
return (wxPGProperty*) NULL;
|
|
}
|
|
#endif
|
|
|
|
// Get previous *visible* parent.
|
|
wxPGPropertyWithChildren* pwc;
|
|
do
|
|
{
|
|
wxASSERT( i > 0 );
|
|
i--;
|
|
pwc = (wxPGPropertyWithChildren*)Item(i);
|
|
} while ( pwc->m_y < 0 );
|
|
|
|
if ( pwc->GetParentingType() != 0 )
|
|
{
|
|
#ifdef __WXDEBUG__
|
|
if ( !pwc->m_expanded || pwc->m_y < 0 )
|
|
{
|
|
wxLogDebug(wxT("WARNING: wxPGPropertyWithChildren::GetItemAtY: Item %s should have been visible and expanded."),pwc->m_label.c_str());
|
|
wxLogDebug(wxT(" (%s[%i]: %s)"),pwc->m_parent->m_label.c_str(),pwc->m_arrIndex,pwc->m_label.c_str());
|
|
//wxLogDebug(wxT(" py=%i"),(int)py);
|
|
return (wxPGProperty*) NULL;
|
|
}
|
|
#endif
|
|
return pwc->GetItemAtY(y,lh);
|
|
}
|
|
}
|
|
return (wxPGProperty*) NULL;
|
|
}
|
|
|
|
|
|
void wxPGPropertyWithChildren::Empty()
|
|
{
|
|
size_t i;
|
|
if ( m_expanded != wxPG_EXP_OF_COPYARRAY )
|
|
{
|
|
for ( i=0; i<GetCount(); i++ )
|
|
{
|
|
wxPGProperty* p = (wxPGProperty*) Item(i);
|
|
delete p;
|
|
}
|
|
}
|
|
|
|
m_children.Empty();
|
|
}
|
|
|
|
|
|
void wxPGPropertyWithChildren::ChildChanged( wxPGProperty* WXUNUSED(p) )
|
|
{
|
|
}
|
|
|
|
|
|
wxString wxPGPropertyWithChildren::GetValueAsString( int argFlags ) const
|
|
{
|
|
wxCHECK_MSG( GetCount() > 0,
|
|
wxString(),
|
|
wxT("If user property does not have any children, it must override GetValueAsString.") );
|
|
|
|
wxString text;
|
|
|
|
int i;
|
|
int iMax = m_children.GetCount();
|
|
|
|
if ( iMax > PWC_CHILD_SUMMARY_LIMIT &&
|
|
!(argFlags & wxPG_FULL_VALUE) )
|
|
iMax = PWC_CHILD_SUMMARY_LIMIT;
|
|
|
|
int iMaxMinusOne = iMax-1;
|
|
|
|
wxPGProperty* curChild = (wxPGProperty*) m_children.Item(0);
|
|
|
|
for ( i = 0; i < iMax; i++ )
|
|
{
|
|
wxString s;
|
|
if ( !(curChild->m_flags & wxPG_PROP_UNSPECIFIED) )
|
|
s = curChild->GetValueAsString(argFlags);
|
|
|
|
if ( curChild->GetParentingType() == 0 )
|
|
text += s;
|
|
else
|
|
text += wxT("[") + s + wxT("]");
|
|
|
|
if ( i < iMaxMinusOne )
|
|
{
|
|
if ( text.length() > PWC_CHILD_SUMMARY_CHAR_LIMIT &&
|
|
!(argFlags & wxPG_EDITABLE_VALUE) &&
|
|
!(argFlags & wxPG_FULL_VALUE) )
|
|
break;
|
|
|
|
curChild = (wxPGProperty*) m_children.Item(i+1);
|
|
|
|
if ( curChild->GetParentingType() == 0 )
|
|
text += wxT("; ");
|
|
else
|
|
text += wxT(" ");
|
|
}
|
|
}
|
|
|
|
if ( (unsigned int)i < m_children.GetCount() )
|
|
text += wxT("; ...");
|
|
|
|
return text;
|
|
}
|
|
|
|
|
|
// Convert semicolon delimited tokens into child values.
|
|
bool wxPGPropertyWithChildren::SetValueFromString( const wxString& text, int argFlags )
|
|
{
|
|
if ( !GetCount() )
|
|
return false;
|
|
|
|
unsigned int curChild = 0;
|
|
|
|
unsigned int iMax = m_children.GetCount();
|
|
|
|
if ( iMax > PWC_CHILD_SUMMARY_LIMIT &&
|
|
!(argFlags & wxPG_FULL_VALUE) )
|
|
iMax = PWC_CHILD_SUMMARY_LIMIT;
|
|
|
|
bool changed = false;
|
|
|
|
wxString token;
|
|
size_t pos = 0;
|
|
|
|
// Its best only to add non-empty group items
|
|
bool addOnlyIfNotEmpty = false;
|
|
const wxChar delimeter = wxT(';');
|
|
wxChar a;
|
|
|
|
size_t lastPos = text.length();
|
|
size_t tokenStart = 0xFFFFFF;
|
|
|
|
do
|
|
{
|
|
a = text[pos];
|
|
|
|
if ( tokenStart != 0xFFFFFF )
|
|
{
|
|
// Token is running
|
|
if ( a == delimeter || a == 0 )
|
|
{
|
|
token = text.substr(tokenStart,pos-tokenStart);
|
|
token.Trim(true);
|
|
size_t len = token.length();
|
|
|
|
if ( !addOnlyIfNotEmpty || len > 0 )
|
|
{
|
|
wxPGProperty* child = Item(curChild);
|
|
|
|
if ( len > 0 )
|
|
{
|
|
bool wasUnspecified = child->IsValueUnspecified();
|
|
if ( child->SetValueFromString( token, wxPG_REPORT_ERROR ) )
|
|
{
|
|
// If modified, set mod flag and store value back to parent
|
|
child->SetFlag( wxPG_PROP_MODIFIED );
|
|
|
|
// Clear unspecified flag only if SetValueFromString didn't
|
|
// affect it.
|
|
if ( child->IsValueUnspecified() &&
|
|
(wasUnspecified || !UsesAutoUnspecified()) )
|
|
child->ClearFlag( wxPG_PROP_UNSPECIFIED );
|
|
|
|
ChildChanged( child );
|
|
changed = true;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
child->SetFlag( wxPG_PROP_UNSPECIFIED );
|
|
changed = true;
|
|
}
|
|
|
|
curChild++;
|
|
if ( curChild >= iMax )
|
|
break;
|
|
}
|
|
|
|
tokenStart = 0xFFFFFF;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Token is not running
|
|
if ( a != wxT(' ') )
|
|
{
|
|
|
|
addOnlyIfNotEmpty = false;
|
|
|
|
// Is this a group of tokens?
|
|
if ( a == wxT('[') )
|
|
{
|
|
int depth = 1;
|
|
|
|
pos++;
|
|
size_t startPos = pos;
|
|
|
|
// Group item - find end
|
|
do
|
|
{
|
|
a = text[pos];
|
|
pos++;
|
|
|
|
if ( a == wxT(']') )
|
|
depth--;
|
|
else if ( a == wxT('[') )
|
|
depth++;
|
|
|
|
} while ( depth > 0 && a );
|
|
|
|
token = text.substr(startPos,pos-startPos-1);
|
|
|
|
if ( !token.length() )
|
|
break;
|
|
|
|
wxPGProperty* child = Item(curChild);
|
|
|
|
//wxLogDebug(wxT("child(1) %i: %s"),curChild,token.c_str());
|
|
|
|
if ( child->SetValueFromString( token, wxPG_REPORT_ERROR ) )
|
|
{
|
|
// If modified, set mod flag and store value back to parent
|
|
child->SetFlag( wxPG_PROP_MODIFIED );
|
|
ChildChanged( child );
|
|
changed = true;
|
|
}
|
|
|
|
curChild++;
|
|
if ( curChild >= iMax )
|
|
break;
|
|
|
|
addOnlyIfNotEmpty = true;
|
|
|
|
tokenStart = 0xFFFFFF;
|
|
}
|
|
else
|
|
{
|
|
tokenStart = pos;
|
|
|
|
if ( a == delimeter )
|
|
{
|
|
pos--;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
pos++;
|
|
|
|
}
|
|
while ( pos <= lastPos );
|
|
|
|
// This ensures that the last item is set unspecified even
|
|
// if the blank had no terminating delimiter.
|
|
if ( curChild < iMax )
|
|
{
|
|
wxPGProperty* child = Item(curChild);
|
|
|
|
child->SetFlag( wxPG_PROP_UNSPECIFIED );
|
|
changed = true;
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
|
|
void wxPGPropertyWithChildren::RefreshChildren ()
|
|
{
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxParentProperty
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGProperty* wxParentProperty( const wxString& label, const wxString& name )
|
|
{
|
|
return new wxParentPropertyClass(label,name);
|
|
}
|
|
|
|
|
|
WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxParentProperty,none,TextCtrl)
|
|
WX_PG_IMPLEMENT_CLASSINFO(wxParentProperty,wxBaseParentPropertyClass)
|
|
|
|
|
|
wxParentPropertyClass::wxParentPropertyClass( const wxString& label, const wxString& name )
|
|
: wxPGPropertyWithChildren(label,name)
|
|
{
|
|
m_parentingType = PT_CUSTOMPROPERTY;
|
|
}
|
|
|
|
|
|
wxParentPropertyClass::~wxParentPropertyClass() { }
|
|
|
|
|
|
void wxParentPropertyClass::DoSetValue( wxPGVariant value )
|
|
{
|
|
const wxString& str = wxPGVariantToString(value);
|
|
m_string = str;
|
|
SetValueFromString(str,wxPG_REPORT_ERROR);
|
|
}
|
|
|
|
|
|
wxPGVariant wxParentPropertyClass::DoGetValue() const
|
|
{
|
|
return wxPGVariant();
|
|
}
|
|
|
|
|
|
void wxParentPropertyClass::ChildChanged( wxPGProperty* WXUNUSED(p) )
|
|
{
|
|
}
|
|
|
|
|
|
wxString wxParentPropertyClass::GetValueAsString( int argFlags ) const
|
|
{
|
|
if ( !GetCount() )
|
|
return wxEmptyString;
|
|
|
|
return wxPGPropertyWithChildren::GetValueAsString(argFlags);
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGRootPropertyClass
|
|
// -----------------------------------------------------------------------
|
|
|
|
WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxPGRootProperty,none,TextCtrl)
|
|
const wxPGPropertyClassInfo* wxPGRootPropertyClass::GetClassInfo() const
|
|
{
|
|
return (const wxPGPropertyClassInfo*) NULL;
|
|
}
|
|
|
|
|
|
wxPGRootPropertyClass::wxPGRootPropertyClass()
|
|
: wxPGPropertyWithChildren()
|
|
{
|
|
m_parentingType = PT_ROOT; // this was PT_CAPTION in <= 1.1.6, but changed
|
|
// so the depth calculations can become
|
|
// more consistent.
|
|
m_depth = 0;
|
|
}
|
|
|
|
|
|
wxPGRootPropertyClass::~wxPGRootPropertyClass()
|
|
{
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyCategoryClass
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGProperty* wxPropertyCategory( const wxString& label, const wxString& name )
|
|
{
|
|
return new wxPropertyCategoryClass(label,name);
|
|
}
|
|
|
|
|
|
WX_PG_IMPLEMENT_CLASSINFO(wxPropertyCategory,wxBaseParentPropertyClass)
|
|
|
|
|
|
WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxPropertyCategory,none,TextCtrl)
|
|
|
|
|
|
wxPropertyCategoryClass::wxPropertyCategoryClass()
|
|
: wxPGPropertyWithChildren()
|
|
{
|
|
// don't set colour - prepareadditem method should do this
|
|
m_parentingType = 1;
|
|
m_capFgColIndex = 1;
|
|
}
|
|
|
|
|
|
wxPropertyCategoryClass::wxPropertyCategoryClass( const wxString &label, const wxString& name )
|
|
: wxPGPropertyWithChildren(label,name)
|
|
{
|
|
// don't set colour - prepareadditem method should do this
|
|
m_parentingType = 1;
|
|
m_capFgColIndex = 1;
|
|
}
|
|
|
|
|
|
wxPropertyCategoryClass::~wxPropertyCategoryClass()
|
|
{
|
|
}
|
|
|
|
|
|
wxString wxPropertyCategoryClass::GetValueAsString( int ) const
|
|
{
|
|
return wxEmptyString;
|
|
}
|
|
|
|
|
|
void wxPropertyCategoryClass::CalculateTextExtent( wxWindow* wnd, wxFont& font )
|
|
{
|
|
int x = 0, y = 0;
|
|
wnd->GetTextExtent( m_label, &x, &y, 0, 0, &font );
|
|
m_textExtent = x;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGEditor
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGEditor::~wxPGEditor()
|
|
{
|
|
}
|
|
|
|
|
|
void wxPGEditor::DrawValue( wxDC& dc, wxPGProperty* property, const wxRect& rect ) const
|
|
{
|
|
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
|
|
dc.DrawText( property->GetDisplayedString(), rect.x+wxPG_XBEFORETEXT, rect.y );
|
|
}
|
|
|
|
|
|
void wxPGEditor::SetControlStringValue( wxWindow*, const wxString& ) const
|
|
{
|
|
}
|
|
|
|
|
|
void wxPGEditor::SetControlIntValue( wxWindow*, int ) const
|
|
{
|
|
}
|
|
|
|
|
|
int wxPGEditor::InsertItem( wxWindow*, const wxString&, int ) const
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
|
|
void wxPGEditor::DeleteItem( wxWindow*, int ) const
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
void wxPGEditor::OnFocus( wxPGProperty*, wxWindow* ) const
|
|
{
|
|
}
|
|
|
|
|
|
bool wxPGEditor::CanContainCustomImage() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGClipperWindow
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
#if wxPG_ENABLE_CLIPPER_WINDOW
|
|
|
|
//
|
|
// Clipper window is used to "remove" borders from controls
|
|
// which otherwise insist on having them despite of supplied
|
|
// wxNO_BORDER window style.
|
|
//
|
|
class wxPGClipperWindow : public wxWindow
|
|
{
|
|
DECLARE_CLASS(wxPGClipperWindow)
|
|
public:
|
|
|
|
wxPGClipperWindow()
|
|
: wxWindow()
|
|
{
|
|
wxPGClipperWindow::Init();
|
|
}
|
|
|
|
wxPGClipperWindow(wxWindow* parent,
|
|
wxWindowID id,
|
|
const wxPoint& pos = wxDefaultPosition,
|
|
const wxSize& size = wxDefaultSize)
|
|
{
|
|
Init();
|
|
Create(parent,id,pos,size);
|
|
}
|
|
|
|
void Create(wxWindow* parent,
|
|
wxWindowID id,
|
|
const wxPoint& pos = wxDefaultPosition,
|
|
const wxSize& size = wxDefaultSize);
|
|
|
|
virtual ~wxPGClipperWindow();
|
|
|
|
virtual bool ProcessEvent(wxEvent& event);
|
|
|
|
inline wxWindow* GetControl() const { return m_ctrl; }
|
|
|
|
// This is called before wxControl is constructed.
|
|
void GetControlRect( int xadj, int yadj, wxPoint& pt, wxSize& sz );
|
|
|
|
// This is caleed after wxControl has been constructed.
|
|
void SetControl( wxWindow* ctrl );
|
|
|
|
virtual void Refresh( bool eraseBackground = true,
|
|
const wxRect *rect = (const wxRect *) NULL );
|
|
virtual void SetFocus();
|
|
|
|
virtual bool SetFont(const wxFont& font);
|
|
|
|
inline int GetXClip() const { return m_xadj; }
|
|
|
|
inline int GetYClip() const { return m_yadj; }
|
|
|
|
protected:
|
|
wxWindow* m_ctrl;
|
|
|
|
int m_xadj; // Horizontal border clip.
|
|
|
|
int m_yadj; // Vertical border clip.
|
|
|
|
private:
|
|
void Init ()
|
|
{
|
|
m_ctrl = (wxWindow*) NULL;
|
|
}
|
|
};
|
|
|
|
|
|
IMPLEMENT_CLASS(wxPGClipperWindow,wxWindow)
|
|
|
|
|
|
// This is called before wxControl is constructed.
|
|
void wxPGClipperWindow::GetControlRect( int xadj, int yadj, wxPoint& pt, wxSize& sz )
|
|
{
|
|
m_xadj = xadj;
|
|
m_yadj = yadj;
|
|
pt.x = -xadj;
|
|
pt.y = -yadj;
|
|
wxSize own_size = GetSize();
|
|
sz.x = own_size.x+(xadj*2);
|
|
sz.y = own_size.y+(yadj*2);
|
|
}
|
|
|
|
|
|
// This is caleed after wxControl has been constructed.
|
|
void wxPGClipperWindow::SetControl( wxWindow* ctrl )
|
|
{
|
|
m_ctrl = ctrl;
|
|
|
|
// GTK requires this.
|
|
ctrl->SetSizeHints(3,3);
|
|
|
|
// Correct size of this window to match the child.
|
|
wxSize sz = GetSize();
|
|
wxSize chsz = ctrl->GetSize();
|
|
|
|
int hei_adj = chsz.y - (sz.y+(m_yadj*2));
|
|
if ( hei_adj )
|
|
SetSize(sz.x,chsz.y-(m_yadj*2));
|
|
|
|
}
|
|
|
|
|
|
void wxPGClipperWindow::Refresh( bool eraseBackground, const wxRect *rect )
|
|
{
|
|
wxWindow::Refresh(false,rect);
|
|
if ( m_ctrl )
|
|
// FIXME: Rect to sub-ctrl refresh too
|
|
m_ctrl->Refresh(eraseBackground);
|
|
}
|
|
|
|
|
|
// Pass focus to control
|
|
void wxPGClipperWindow::SetFocus()
|
|
{
|
|
if ( m_ctrl )
|
|
m_ctrl->SetFocus();
|
|
else
|
|
wxWindow::SetFocus();
|
|
}
|
|
|
|
|
|
bool wxPGClipperWindow::SetFont(const wxFont& font)
|
|
{
|
|
bool res = wxWindow::SetFont(font);
|
|
if ( m_ctrl )
|
|
return m_ctrl->SetFont(font);
|
|
return res;
|
|
}
|
|
|
|
|
|
void wxPGClipperWindow::Create(wxWindow* parent,
|
|
wxWindowID id,
|
|
const wxPoint& pos,
|
|
const wxSize& size )
|
|
{
|
|
wxWindow::Create(parent,id,pos,size);
|
|
}
|
|
|
|
|
|
wxPGClipperWindow::~wxPGClipperWindow()
|
|
{
|
|
}
|
|
|
|
|
|
bool wxPGClipperWindow::ProcessEvent(wxEvent& event)
|
|
{
|
|
if ( event.GetEventType() == wxEVT_SIZE )
|
|
{
|
|
if ( m_ctrl )
|
|
{
|
|
// Maintain correct size relationship.
|
|
wxSize sz = GetSize();
|
|
m_ctrl->SetSize(sz.x+(m_xadj*2),sz.y+(m_yadj*2));
|
|
event.Skip();
|
|
return false;
|
|
}
|
|
}
|
|
return wxWindow::ProcessEvent(event);
|
|
}
|
|
|
|
#endif // wxPG_ENABLE_CLIPPER_WINDOW
|
|
|
|
/*wxWindow* wxPropertyGrid::GetActualEditorControl( wxWindow* ctrl )
|
|
{
|
|
#if wxPG_ENABLE_CLIPPER_WINDOW
|
|
// Pass real control instead of clipper window
|
|
if ( ctrl->IsKindOf(CLASSINFO(wxPGClipperWindow)) )
|
|
{
|
|
return ((wxPGClipperWindow*)ctrl)->GetControl();
|
|
}
|
|
#else
|
|
return ctrl;
|
|
#endif
|
|
}*/
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGTextCtrlEditor
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Clipper window support macro (depending on whether it is used
|
|
// for this editor or not)
|
|
#if wxPG_NAT_TEXTCTRL_BORDER_X || wxPG_NAT_TEXTCTRL_BORDER_Y
|
|
#define wxPG_NAT_TEXTCTRL_BORDER_ANY 1
|
|
#define wxPGDeclareRealTextCtrl(WND) \
|
|
wxASSERT( WND ); \
|
|
wxTextCtrl* tc = (wxTextCtrl*)((wxPGClipperWindow*)WND)->GetControl()
|
|
#else
|
|
#define wxPG_NAT_TEXTCTRL_BORDER_ANY 0
|
|
#define wxPGDeclareRealTextCtrl(WND) \
|
|
wxASSERT( WND ); \
|
|
wxTextCtrl* tc = (wxTextCtrl*)WND
|
|
#endif
|
|
|
|
|
|
WX_PG_IMPLEMENT_EDITOR_CLASS(TextCtrl,wxPGTextCtrlEditor,wxPGEditor)
|
|
|
|
|
|
#ifndef __WXPYTHON__
|
|
wxWindow* wxPGTextCtrlEditor::CreateControls( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
const wxPoint& pos,
|
|
const wxSize& sz,
|
|
wxWindow** ) const
|
|
#else
|
|
wxPGWindowPair wxPGTextCtrlEditor::CreateControls( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
const wxPoint& pos,
|
|
const wxSize& sz ) const
|
|
#endif
|
|
{
|
|
wxString text;
|
|
|
|
// If has children and limited editing, then don't create.
|
|
if ((property->GetFlags() & wxPG_PROP_NOEDITOR) &&
|
|
property->GetParentingType() < 0 &&
|
|
!property->IsKindOf(WX_PG_CLASSINFO(wxCustomProperty)))
|
|
return (wxWindow*) NULL;
|
|
|
|
int flags = 0;
|
|
if ( (property->GetFlags() & wxPG_PROP_PASSWORD) &&
|
|
property->IsKindOf(WX_PG_CLASSINFO(wxStringProperty)) )
|
|
flags |= wxTE_PASSWORD;
|
|
|
|
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
|
|
text = property->GetValueAsString(property->HasFlag(wxPG_PROP_READONLY)?0:wxPG_EDITABLE_VALUE);
|
|
|
|
wxWindow* wnd = propGrid->GenerateEditorTextCtrl(pos,sz,text,(wxWindow*)NULL,flags,
|
|
property->GetMaxLength());
|
|
|
|
return wnd;
|
|
}
|
|
|
|
|
|
void wxPGTextCtrlEditor::DrawValue( wxDC& dc, wxPGProperty* property, const wxRect& rect ) const
|
|
{
|
|
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
|
|
{
|
|
wxString drawStr = property->GetDisplayedString();
|
|
|
|
// Code below should no longer be needed, as the obfuscation
|
|
// is now done in GetValueAsString.
|
|
/*if ( (property->GetFlags() & wxPG_PROP_PASSWORD) &&
|
|
property->IsKindOf(WX_PG_CLASSINFO(wxStringProperty)) )
|
|
{
|
|
size_t a = drawStr.length();
|
|
drawStr.Empty();
|
|
drawStr.Append(wxT('*'),a);
|
|
}*/
|
|
dc.DrawText( drawStr, rect.x+wxPG_XBEFORETEXT, rect.y );
|
|
}
|
|
}
|
|
|
|
|
|
void wxPGTextCtrlEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
|
|
{
|
|
wxPGDeclareRealTextCtrl(ctrl);
|
|
tc->SetValue(property->GetDisplayedString());
|
|
}
|
|
|
|
|
|
// Provided so that, for example, ComboBox editor can use the same code
|
|
// (multiple inheritance would get way too messy).
|
|
bool wxPGTextCtrlEditor::OnTextCtrlEvent( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
wxWindow* ctrl,
|
|
wxEvent& event )
|
|
{
|
|
if ( !ctrl )
|
|
return false;
|
|
|
|
if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER )
|
|
{
|
|
if ( propGrid->IsEditorsValueModified() )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
else if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED )
|
|
{
|
|
wxPGDeclareRealTextCtrl(ctrl);
|
|
|
|
// If value is unspecified and character count is zero,
|
|
// then do not set as modified.
|
|
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) ||
|
|
!tc ||
|
|
(tc->IsKindOf(CLASSINFO(wxTextCtrl)) &&
|
|
(tc->GetLastPosition() > 0)) )
|
|
{
|
|
|
|
// We must check this since an 'empty' text event
|
|
// may be triggered when creating the property.
|
|
if ( !(propGrid->GetInternalFlags() & wxPG_FL_IN_SELECT_PROPERTY) )
|
|
{
|
|
//
|
|
// Pass this event outside wxPropertyGrid so that,
|
|
// if necessary, program can tell when user is editing
|
|
// a textctrl.
|
|
// FIXME: Is it safe to change event id in the middle of event
|
|
// processing (seems to work, but...)?
|
|
event.Skip();
|
|
event.SetId(propGrid->GetId());
|
|
}
|
|
|
|
propGrid->EditorsValueWasModified();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool wxPGTextCtrlEditor::OnEvent( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
wxWindow* ctrl,
|
|
wxEvent& event ) const
|
|
{
|
|
return wxPGTextCtrlEditor::OnTextCtrlEvent(propGrid,property,ctrl,event);
|
|
}
|
|
|
|
|
|
bool wxPGTextCtrlEditor::CopyTextCtrlValueFromControl( wxPGProperty* property, wxWindow* ctrl )
|
|
{
|
|
#if wxPG_ENABLE_CLIPPER_WINDOW
|
|
// Pass real control instead of clipper window
|
|
if ( ctrl->IsKindOf(CLASSINFO(wxPGClipperWindow)) )
|
|
{
|
|
ctrl = ((wxPGClipperWindow*)ctrl)->GetControl();
|
|
}
|
|
#endif
|
|
wxTextCtrl* tc = (wxTextCtrl*)ctrl;
|
|
|
|
bool res = property->SetValueFromString(tc->GetValue(),0);
|
|
|
|
// Changing unspecified always causes event (returning
|
|
// true here should be enough to trigger it).
|
|
if ( !res && property->IsFlagSet(wxPG_PROP_UNSPECIFIED) )
|
|
res = true;
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
bool wxPGTextCtrlEditor::CopyValueFromControl( wxPGProperty* property, wxWindow* ctrl ) const
|
|
{
|
|
return wxPGTextCtrlEditor::CopyTextCtrlValueFromControl(property,ctrl);
|
|
}
|
|
|
|
|
|
void wxPGTextCtrlEditor::SetValueToUnspecified( wxWindow* ctrl ) const
|
|
{
|
|
wxPGDeclareRealTextCtrl(ctrl);
|
|
|
|
tc->Remove(0,tc->GetValue().length());
|
|
}
|
|
|
|
|
|
void wxPGTextCtrlEditor::SetControlStringValue( wxWindow* ctrl, const wxString& txt ) const
|
|
{
|
|
wxPGDeclareRealTextCtrl(ctrl);
|
|
|
|
tc->SetValue(txt);
|
|
}
|
|
|
|
|
|
void wxPGTextCtrlEditor::OnFocus( wxPGProperty*, wxWindow* wnd ) const
|
|
{
|
|
wxPGDeclareRealTextCtrl(wnd);
|
|
|
|
tc->SetSelection(-1,-1);
|
|
}
|
|
|
|
|
|
wxPGTextCtrlEditor::~wxPGTextCtrlEditor() { }
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGChoiceEditor
|
|
// -----------------------------------------------------------------------
|
|
|
|
extern const wxChar* wxPG_ClassName_wxBoolProperty; // in props.cpp
|
|
|
|
|
|
WX_PG_IMPLEMENT_EDITOR_CLASS(Choice,wxPGChoiceEditor,wxPGEditor)
|
|
|
|
|
|
// This is a special enhanced double-click processor class.
|
|
// In essence, it allows for double-clicks for which the
|
|
// first click "created" the control.
|
|
class wxPGDoubleClickProcessor : public wxEvtHandler
|
|
{
|
|
public:
|
|
|
|
wxPGDoubleClickProcessor( wxPGOwnerDrawnComboBox* combo )
|
|
: wxEvtHandler()
|
|
{
|
|
m_timeLastMouseUp = 0;
|
|
m_combo = combo;
|
|
m_downReceived = false;
|
|
}
|
|
|
|
protected:
|
|
|
|
void OnMouseEvent( wxMouseEvent& event )
|
|
{
|
|
wxLongLong t = ::wxGetLocalTimeMillis();
|
|
int evtType = event.GetEventType();
|
|
|
|
if ( m_combo->HasFlag(wxPGCC_DCLICK_CYCLES) &&
|
|
!m_combo->IsPopupShown() )
|
|
{
|
|
// Just check that it is in the text area
|
|
wxPoint pt = event.GetPosition();
|
|
if ( m_combo->GetTextRect().wxPGRectContains(pt) )
|
|
{
|
|
if ( evtType == wxEVT_LEFT_DOWN )
|
|
{
|
|
// Set value to avoid up-events without corresponding downs
|
|
m_downReceived = true;
|
|
}
|
|
else if ( evtType == wxEVT_LEFT_DCLICK )
|
|
{
|
|
// We'll make our own double-clicks
|
|
event.SetEventType(0);
|
|
return;
|
|
}
|
|
else if ( evtType == wxEVT_LEFT_UP )
|
|
{
|
|
if ( m_downReceived || m_timeLastMouseUp == 1 )
|
|
{
|
|
wxLongLong timeFromLastUp = (t-m_timeLastMouseUp);
|
|
|
|
if ( timeFromLastUp < DOUBLE_CLICK_CONVERSION_TRESHOLD )
|
|
{
|
|
event.SetEventType(wxEVT_LEFT_DCLICK);
|
|
m_timeLastMouseUp = 1;
|
|
}
|
|
else
|
|
{
|
|
m_timeLastMouseUp = t;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
void OnSetFocus( wxFocusEvent& event )
|
|
{
|
|
m_timeLastMouseUp = ::wxGetLocalTimeMillis();
|
|
event.Skip();
|
|
}
|
|
|
|
private:
|
|
wxLongLong m_timeLastMouseUp;
|
|
wxPGOwnerDrawnComboBox* m_combo;
|
|
bool m_downReceived;
|
|
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
BEGIN_EVENT_TABLE(wxPGDoubleClickProcessor, wxEvtHandler)
|
|
EVT_MOUSE_EVENTS(wxPGDoubleClickProcessor::OnMouseEvent)
|
|
EVT_SET_FOCUS(wxPGDoubleClickProcessor::OnSetFocus)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
|
|
class wxPGComboBox : public wxPGOwnerDrawnComboBox
|
|
{
|
|
public:
|
|
|
|
wxPGComboBox()
|
|
: wxPGOwnerDrawnComboBox()
|
|
{
|
|
m_dclickProcessor = (wxPGDoubleClickProcessor*) NULL;
|
|
}
|
|
|
|
~wxPGComboBox()
|
|
{
|
|
if ( m_dclickProcessor )
|
|
{
|
|
RemoveEventHandler(m_dclickProcessor);
|
|
delete m_dclickProcessor;
|
|
}
|
|
}
|
|
|
|
bool Create(wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxString& value = wxEmptyString,
|
|
const wxPoint& pos = wxDefaultPosition,
|
|
const wxSize& size = wxDefaultSize,
|
|
int n = 0,
|
|
const wxString choices[] = (const wxString *) NULL,
|
|
long style = 0,
|
|
const wxValidator& validator = wxDefaultValidator,
|
|
const wxString& name = wxComboBoxNameStr)
|
|
{
|
|
if ( !wxPGOwnerDrawnComboBox::Create( parent,
|
|
id,
|
|
value,
|
|
pos,
|
|
size,
|
|
n,
|
|
choices,
|
|
style,
|
|
validator,
|
|
name ) )
|
|
return false;
|
|
|
|
m_dclickProcessor = new wxPGDoubleClickProcessor(this);
|
|
|
|
PushEventHandler(m_dclickProcessor);
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool OnDrawListItem( wxDC& dc, const wxRect& rect, int item, int flags )
|
|
{
|
|
wxPropertyGrid* pg = wxDynamicCast(GetParent(),wxPropertyGrid);
|
|
wxASSERT(pg);
|
|
pg->OnComboItemPaint((wxPGCustomComboControl*)this,item,dc,(wxRect&)rect,flags);
|
|
return true;
|
|
}
|
|
virtual wxCoord OnMeasureListItem( int item )
|
|
{
|
|
wxPropertyGrid* pg = wxDynamicCast(GetParent(),wxPropertyGrid);
|
|
wxASSERT(pg);
|
|
wxRect rect;
|
|
rect.x = -1;
|
|
rect.width = 0;
|
|
pg->OnComboItemPaint((wxPGCustomComboControl*)this,item,*((wxDC*)NULL),rect,0);
|
|
return rect.height;
|
|
}
|
|
virtual wxCoord OnMeasureListItemWidth( int item )
|
|
{
|
|
wxPropertyGrid* pg = wxDynamicCast(GetParent(),wxPropertyGrid);
|
|
wxASSERT(pg);
|
|
wxRect rect;
|
|
rect.x = -1;
|
|
rect.width = -1;
|
|
pg->OnComboItemPaint((wxPGCustomComboControl*)this,item,*((wxDC*)NULL),rect,0);
|
|
return rect.width;
|
|
}
|
|
|
|
private:
|
|
wxPGDoubleClickProcessor* m_dclickProcessor;
|
|
};
|
|
|
|
|
|
void wxPropertyGrid::OnComboItemPaint( wxPGCustomComboControl* pCc,
|
|
int item,
|
|
wxDC& dc,
|
|
wxRect& rect,
|
|
int flags )
|
|
{
|
|
wxPGOwnerDrawnComboBox* pCb = (wxPGOwnerDrawnComboBox*)pCc;
|
|
|
|
// Sanity check
|
|
wxASSERT( IsKindOf(CLASSINFO(wxPropertyGrid)) );
|
|
|
|
wxPGProperty* p = m_selected;
|
|
|
|
//
|
|
// Decide what custom image size to use
|
|
wxSize cis = GetImageSize(p);
|
|
|
|
if ( rect.x < 0 &&
|
|
!(m_iFlags & wxPG_FL_SELECTED_IS_PAINT_FLEXIBLE) )
|
|
{
|
|
// Default measure behaviour (no flexible, custom paint image only)
|
|
if ( rect.width < 0 )
|
|
{
|
|
wxCoord x, y;
|
|
GetTextExtent(pCb->GetString(item), &x, &y, 0, 0, &m_font);
|
|
rect.width = cis.x + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2 + 9 + x;
|
|
}
|
|
|
|
rect.height = cis.y + 2;
|
|
return;
|
|
}
|
|
|
|
wxPGPaintData paintdata;
|
|
paintdata.m_parent = NULL;
|
|
paintdata.m_choiceItem = item;
|
|
|
|
// This is by the current (1.0.0b) spec - if painting control, item is -1
|
|
if ( (flags & wxPGCC_PAINTING_CONTROL) )
|
|
paintdata.m_choiceItem = -1;
|
|
|
|
if ( &dc )
|
|
dc.SetBrush(*wxWHITE_BRUSH);
|
|
|
|
if ( rect.x >= 0 )
|
|
{
|
|
//
|
|
// DrawItem call
|
|
|
|
wxPoint pt(rect.x + wxPG_CONTROL_MARGIN - wxPG_CHOICEXADJUST - 1,
|
|
rect.y + 1);
|
|
|
|
if ( cis.x > 0 &&
|
|
( !p->m_dataExt || !p->m_dataExt->m_valueBitmap || item == pCb->GetSelection() ) &&
|
|
( item >= 0 || (flags & wxPGCC_PAINTING_CONTROL) )
|
|
)
|
|
{
|
|
pt.x += wxCC_CUSTOM_IMAGE_MARGIN1;
|
|
wxRect r(pt.x,pt.y,cis.x,cis.y);
|
|
|
|
if ( flags & wxPGCC_PAINTING_CONTROL )
|
|
{
|
|
//r.width = cis.x;
|
|
r.height = wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight);
|
|
}
|
|
|
|
if ( m_iFlags & wxPG_FL_SELECTED_IS_FULL_PAINT )
|
|
r.width = rect.width;
|
|
|
|
paintdata.m_drawnWidth = r.width;
|
|
|
|
dc.SetPen(m_colPropFore);
|
|
if ( item >= 0 )
|
|
p->OnCustomPaint( dc, r, paintdata );
|
|
else
|
|
dc.DrawRectangle( r );
|
|
|
|
if ( (m_iFlags & wxPG_FL_SELECTED_IS_FULL_PAINT) )
|
|
{
|
|
if ( paintdata.m_drawnWidth > 0 )
|
|
return;
|
|
|
|
// Revert pt.x
|
|
pt.x -= (wxCC_CUSTOM_IMAGE_MARGIN1+1);
|
|
}
|
|
else
|
|
pt.x += paintdata.m_drawnWidth + wxCC_CUSTOM_IMAGE_MARGIN2 - 1;
|
|
}
|
|
else
|
|
// TODO: This aligns text so that it seems to be horizontally
|
|
// on the same line as property values. Not really
|
|
// sure if its needed, but seems to not cause any harm.
|
|
pt.x -= 1;
|
|
|
|
//
|
|
// Draw text
|
|
//
|
|
|
|
pt.y += (rect.height-m_fontHeight)/2 - 1;
|
|
|
|
wxString text;
|
|
if ( !(flags & wxPGCC_PAINTING_CONTROL) )
|
|
{
|
|
text = pCb->GetString(item);
|
|
}
|
|
else
|
|
{
|
|
if ( !p->IsValueUnspecified() )
|
|
text = p->GetValueAsString(0);
|
|
}
|
|
|
|
dc.DrawText( text, pt.x + wxPG_XBEFORETEXT, pt.y );
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// MeasureItem call
|
|
|
|
p->OnCustomPaint( dc, rect, paintdata );
|
|
rect.height = paintdata.m_drawnHeight + 2;
|
|
rect.width = cis.x + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2 + 9;
|
|
}
|
|
}
|
|
|
|
|
|
// CreateControls calls this with CB_READONLY in extraStyle
|
|
wxWindow* wxPGChoiceEditor::CreateControlsBase( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
const wxPoint& pos,
|
|
const wxSize& sz,
|
|
long extraStyle ) const
|
|
{
|
|
wxString defString;
|
|
wxPGChoiceInfo choiceInfo;
|
|
|
|
// Get choices.
|
|
choiceInfo.m_arrWxString = (wxString*) NULL;
|
|
choiceInfo.m_arrWxChars = (const wxChar**) NULL;
|
|
choiceInfo.m_itemCount = 0;
|
|
|
|
int index = property->GetChoiceInfo( &choiceInfo );
|
|
|
|
if ( property->GetFlags() & wxPG_PROP_UNSPECIFIED )
|
|
{
|
|
index = -1;
|
|
}
|
|
else
|
|
{
|
|
defString = property->GetDisplayedString();
|
|
}
|
|
|
|
// SLAlloc allows fast conversion using potentially pre-allocated wxStrings
|
|
// (and appending is out of question due to performance problems on some platforms).
|
|
|
|
// If itemcount is < 0, fill wxArrayString using GetEntry
|
|
if ( choiceInfo.m_itemCount < 0 )
|
|
{
|
|
wxBaseEnumPropertyClass* ep = (wxBaseEnumPropertyClass*) property;
|
|
size_t i = 0;
|
|
const wxString* entryLabel;
|
|
int entryValue;
|
|
|
|
wxArrayString& sl = propGrid->SLGet();
|
|
|
|
entryLabel = ep->GetEntry(i,&entryValue);
|
|
while ( entryLabel )
|
|
{
|
|
if ( sl.GetCount() > i )
|
|
sl[i] = *entryLabel;
|
|
else
|
|
sl.Add(*entryLabel);
|
|
i++;
|
|
entryLabel = ep->GetEntry(i,&entryValue);
|
|
}
|
|
choiceInfo.m_itemCount = ((int)i) - 1;
|
|
}
|
|
else if ( !choiceInfo.m_arrWxString )
|
|
{
|
|
wxASSERT( choiceInfo.m_arrWxChars || !choiceInfo.m_itemCount );
|
|
propGrid->SLAlloc( choiceInfo.m_itemCount, choiceInfo.m_arrWxChars );
|
|
if ( choiceInfo.m_itemCount )
|
|
choiceInfo.m_arrWxString = &propGrid->SLGet().Item(0);
|
|
}
|
|
|
|
//wxPGOwnerDrawnComboBox* cb;
|
|
wxPGComboBox* cb;
|
|
|
|
wxPoint po(pos);
|
|
wxSize si(sz);
|
|
po.y += wxPG_CHOICEYADJUST;
|
|
si.y -= (wxPG_CHOICEYADJUST*2);
|
|
|
|
/*#if wxPG_NAT_CHOICE_BORDER_ANY
|
|
po.x += (wxPG_CHOICEXADJUST+wxPG_NAT_CHOICE_BORDER_X);
|
|
si.x -= (wxPG_CHOICEXADJUST+wxPG_NAT_CHOICE_BORDER_X);
|
|
wxPGClipperWindow* wnd = new wxPGClipperWindow(propGrid,wxPG_SUBID1,po,si);
|
|
wxWindow* ctrlParent = wnd;
|
|
wnd->GetControlRect(wxPG_NAT_CHOICE_BORDER_X,wxPG_NAT_CHOICE_BORDER_Y,po,si);
|
|
#else*/
|
|
po.x += wxPG_CHOICEXADJUST;
|
|
si.x -= wxPG_CHOICEXADJUST;
|
|
wxWindow* ctrlParent = propGrid;
|
|
//#endif
|
|
|
|
// NB: Using wxWidgets wxOwnerDrawnComboBox needs adding wxTE_PROCESS_ENTER
|
|
// into the flags.
|
|
int odcbFlags = extraStyle | wxNO_BORDER | wxPGCC_PROCESS_ENTER | wxPGCC_ALT_KEYS;
|
|
|
|
if ( !(property->GetFlags() & wxPG_PROP_CUSTOMIMAGE) )
|
|
odcbFlags |= wxODCB_STD_CONTROL_PAINT;
|
|
|
|
if ( (property->GetFlags() & wxPG_PROP_USE_DCC) &&
|
|
(property->GetClassName()==wxPG_ClassName_wxBoolProperty) )
|
|
odcbFlags |= wxPGCC_DCLICK_CYCLES;
|
|
|
|
cb = new wxPGComboBox();
|
|
#ifdef __WXMSW__
|
|
cb->Hide();
|
|
#endif
|
|
cb->Create(ctrlParent,
|
|
wxPG_SUBID1,
|
|
wxString(),
|
|
po,
|
|
si,
|
|
choiceInfo.m_itemCount,choiceInfo.m_arrWxString,
|
|
//(wxComboPaintCallback) &wxPropertyGrid::OnComboItemPaint,
|
|
odcbFlags);
|
|
|
|
int extRight = propGrid->GetClientSize().x - (po.x+si.x);
|
|
|
|
cb->SetButtonPosition(si.y,0,wxRIGHT);
|
|
cb->SetPopupExtents( 1, extRight );
|
|
cb->SetTextIndent(wxPG_XBEFORETEXT-2);
|
|
|
|
if ( (property->GetFlags() & wxPG_PROP_CUSTOMIMAGE) &&
|
|
!(propGrid->GetInternalFlags() & wxPG_FL_SELECTED_IS_FULL_PAINT) )
|
|
{
|
|
wxSize imageSize = propGrid->GetImageSize(property);
|
|
cb->SetCustomPaintWidth( imageSize.x+6 );
|
|
}
|
|
|
|
if ( index >= 0 && index < (int)cb->GetCount() )
|
|
{
|
|
cb->SetSelection( index );
|
|
if ( defString.length() )
|
|
cb->SetValue( defString );
|
|
}
|
|
else if ( !(extraStyle & wxCB_READONLY) && defString.length() )
|
|
cb->SetValue( defString );
|
|
else
|
|
cb->SetSelection( -1 );
|
|
|
|
if ( property->HasFlag(wxPG_PROP_READONLY) )
|
|
cb->Disable();
|
|
|
|
#ifdef __WXMSW__
|
|
cb->Show();
|
|
#endif
|
|
|
|
return (wxWindow*) cb;
|
|
}
|
|
|
|
|
|
void wxPGChoiceEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
|
|
{
|
|
wxASSERT( ctrl );
|
|
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
|
|
wxASSERT( cb->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)));
|
|
int ind = property->GetChoiceInfo( (wxPGChoiceInfo*)NULL );
|
|
cb->SetSelection(ind);
|
|
}
|
|
|
|
#ifndef __WXPYTHON__
|
|
wxWindow* wxPGChoiceEditor::CreateControls( wxPropertyGrid* propGrid, wxPGProperty* property,
|
|
const wxPoint& pos, const wxSize& sz, wxWindow** ) const
|
|
#else
|
|
wxPGWindowPair wxPGChoiceEditor::CreateControls( wxPropertyGrid* propGrid, wxPGProperty* property,
|
|
const wxPoint& pos, const wxSize& sz ) const
|
|
#endif
|
|
{
|
|
return CreateControlsBase(propGrid,property,pos,sz,wxCB_READONLY);
|
|
}
|
|
|
|
|
|
int wxPGChoiceEditor::InsertItem( wxWindow* ctrl, const wxString& label, int index ) const
|
|
{
|
|
wxASSERT( ctrl );
|
|
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
|
|
wxASSERT( cb->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)));
|
|
|
|
if (index < 0)
|
|
index = cb->GetCount();
|
|
|
|
return cb->Insert(label,index);
|
|
}
|
|
|
|
|
|
void wxPGChoiceEditor::DeleteItem( wxWindow* ctrl, int index ) const
|
|
{
|
|
wxASSERT( ctrl );
|
|
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
|
|
wxASSERT( cb->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)));
|
|
|
|
cb->Delete(index);
|
|
}
|
|
|
|
|
|
bool wxPGChoiceEditor::OnEvent( wxPropertyGrid* WXUNUSED(propGrid), wxPGProperty* WXUNUSED(property),
|
|
wxWindow* WXUNUSED(ctrl), wxEvent& event ) const
|
|
{
|
|
if ( event.GetEventType() == wxEVT_COMMAND_COMBOBOX_SELECTED )
|
|
{
|
|
/*if ( CopyValueFromControl( property, ctrl ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
propGrid->EditorsValueWasNotModified();
|
|
|
|
//wxPropertyGridState::ClearPropertyAndChildrenFlags(property,wxPG_PROP_UNSPECIFIED);
|
|
CLEAR_PROPERTY_UNSPECIFIED_FLAG(property);*/
|
|
|
|
return true;
|
|
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool wxPGChoiceEditor::CopyValueFromControl( wxPGProperty* property, wxWindow* ctrl ) const
|
|
{
|
|
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
|
|
|
|
int index = cb->GetSelection();
|
|
|
|
if ( index != property->GetChoiceInfo( (wxPGChoiceInfo*) NULL ) ||
|
|
// Changing unspecified always causes event (returning
|
|
// true here should be enough to trigger it).
|
|
property->IsFlagSet(wxPG_PROP_UNSPECIFIED)
|
|
)
|
|
{
|
|
property->SetValueFromInt(index,0);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void wxPGChoiceEditor::SetControlStringValue( wxWindow* ctrl, const wxString& txt ) const
|
|
{
|
|
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
|
|
wxASSERT( cb );
|
|
cb->SetValue(txt);
|
|
}
|
|
|
|
|
|
void wxPGChoiceEditor::SetControlIntValue( wxWindow* ctrl, int value ) const
|
|
{
|
|
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
|
|
wxASSERT( cb );
|
|
cb->SetSelection(value);
|
|
}
|
|
|
|
|
|
void wxPGChoiceEditor::SetValueToUnspecified( wxWindow* ctrl ) const
|
|
{
|
|
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
|
|
cb->SetSelection(-1);
|
|
}
|
|
|
|
|
|
bool wxPGChoiceEditor::CanContainCustomImage() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
|
|
wxPGChoiceEditor::~wxPGChoiceEditor() { }
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGComboBoxEditor
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
WX_PG_IMPLEMENT_EDITOR_CLASS(ComboBox,wxPGComboBoxEditor,wxPGChoiceEditor)
|
|
|
|
|
|
void wxPGComboBoxEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
|
|
{
|
|
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
|
|
cb->SetValue(property->GetDisplayedString());
|
|
|
|
// TODO: If string matches any selection, then select that.
|
|
}
|
|
|
|
|
|
#ifndef __WXPYTHON__
|
|
wxWindow* wxPGComboBoxEditor::CreateControls( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
const wxPoint& pos,
|
|
const wxSize& sz,
|
|
wxWindow** ) const
|
|
#else
|
|
wxPGWindowPair wxPGComboBoxEditor::CreateControls( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
const wxPoint& pos,
|
|
const wxSize& sz ) const
|
|
#endif
|
|
{
|
|
return CreateControlsBase(propGrid,property,pos,sz,0);
|
|
}
|
|
|
|
|
|
bool wxPGComboBoxEditor::OnEvent( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
wxWindow* ctrl,
|
|
wxEvent& event ) const
|
|
{
|
|
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*) NULL;
|
|
wxWindow* textCtrl = (wxWindow*) NULL;
|
|
|
|
if ( ctrl )
|
|
{
|
|
cb = (wxPGOwnerDrawnComboBox*)ctrl;
|
|
textCtrl = cb->GetTextCtrl();
|
|
}
|
|
|
|
if ( wxPGTextCtrlEditor::OnTextCtrlEvent(propGrid,property,textCtrl,event) )
|
|
return true;
|
|
|
|
return wxPGChoiceEditor::OnEvent(propGrid,property,ctrl,event);
|
|
}
|
|
|
|
|
|
bool wxPGComboBoxEditor::CopyValueFromControl( wxPGProperty* property, wxWindow* ctrl ) const
|
|
{
|
|
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
|
|
|
|
bool res = property->SetValueFromString(cb->GetValue(),0);
|
|
|
|
// Changing unspecified always causes event (returning
|
|
// true here should be enough to trigger it).
|
|
if ( !res && property->IsFlagSet(wxPG_PROP_UNSPECIFIED) )
|
|
res = true;
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
void wxPGComboBoxEditor::OnFocus( wxPGProperty*, wxWindow* ctrl ) const
|
|
{
|
|
wxPGOwnerDrawnComboBox* cb = (wxPGOwnerDrawnComboBox*)ctrl;
|
|
cb->GetTextCtrl()->SetSelection(-1,-1);
|
|
}
|
|
|
|
|
|
wxPGComboBoxEditor::~wxPGComboBoxEditor() { }
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGChoiceAndButtonEditor
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
// This simpler implement_editor macro doesn't define class body.
|
|
WX_PG_IMPLEMENT_EDITOR_CLASS(ChoiceAndButton,wxPGChoiceAndButtonEditor,wxPGChoiceEditor)
|
|
|
|
|
|
#ifndef __WXPYTHON__
|
|
wxWindow* wxPGChoiceAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
const wxPoint& pos,
|
|
const wxSize& sz,
|
|
wxWindow** psecondary ) const
|
|
#else
|
|
wxPGWindowPair wxPGChoiceAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
const wxPoint& pos,
|
|
const wxSize& sz ) const
|
|
#endif
|
|
{
|
|
// Use one two units smaller to match size of the combo's dropbutton.
|
|
// (normally a bigger button is used because it looks better)
|
|
int bt_wid = sz.y;
|
|
bt_wid -= 2;
|
|
wxSize bt_sz(bt_wid,bt_wid);
|
|
|
|
// Position of button.
|
|
wxPoint bt_pos(pos.x+sz.x-bt_sz.x,pos.y);
|
|
#ifdef __WXMAC__
|
|
bt_pos.y -= 1;
|
|
#else
|
|
bt_pos.y += 1;
|
|
#endif
|
|
|
|
wxWindow* bt = propGrid->GenerateEditorButton( bt_pos, bt_sz );
|
|
|
|
// Size of choice.
|
|
wxSize ch_sz(sz.x-bt->GetSize().x,sz.y);
|
|
|
|
#ifdef __WXMAC__
|
|
ch_sz.x -= wxPG_TEXTCTRL_AND_BUTTON_SPACING;
|
|
#endif
|
|
|
|
wxWindow* ch = wxPG_EDITOR(Choice)->CreateControls(propGrid,property,
|
|
pos,ch_sz
|
|
#ifndef __WXPYTHON__
|
|
, (wxWindow**)NULL);
|
|
#else
|
|
).m_primary;
|
|
#endif
|
|
|
|
|
|
#ifdef __WXMSW__
|
|
bt->Show();
|
|
#endif
|
|
|
|
#ifndef __WXPYTHON__
|
|
*psecondary = bt;
|
|
return ch;
|
|
#else
|
|
return wxPGWindowPair(ch, bt);
|
|
#endif
|
|
}
|
|
|
|
|
|
wxPGChoiceAndButtonEditor::~wxPGChoiceAndButtonEditor() { }
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGTextCtrlAndButtonEditor
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
// This simpler implement_editor macro doesn't define class body.
|
|
WX_PG_IMPLEMENT_EDITOR_CLASS(TextCtrlAndButton,wxPGTextCtrlAndButtonEditor,wxPGTextCtrlEditor)
|
|
|
|
|
|
#ifndef __WXPYTHON__
|
|
wxWindow* wxPGTextCtrlAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
const wxPoint& pos,
|
|
const wxSize& sz,
|
|
wxWindow** psecondary ) const
|
|
{
|
|
wxWindow* wnd = propGrid->GenerateEditorTextCtrlAndButton( pos, sz, psecondary,
|
|
property->GetFlags() & wxPG_PROP_NOEDITOR, property);
|
|
|
|
return wnd;
|
|
}
|
|
#else
|
|
wxPGWindowPair wxPGTextCtrlAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
const wxPoint& pos,
|
|
const wxSize& sz ) const
|
|
{
|
|
wxWindow* wnd2;
|
|
wxWindow* wnd = propGrid->GenerateEditorTextCtrlAndButton( pos, sz, &wnd2,
|
|
property->GetFlags() & wxPG_PROP_NOEDITOR, property);
|
|
|
|
return wxPGWindowPair(wnd, wnd2);
|
|
}
|
|
#endif
|
|
|
|
|
|
wxPGTextCtrlAndButtonEditor::~wxPGTextCtrlAndButtonEditor() { }
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGCheckBoxEditor
|
|
// -----------------------------------------------------------------------
|
|
|
|
#if wxPG_INCLUDE_CHECKBOX
|
|
|
|
WX_PG_IMPLEMENT_EDITOR_CLASS(CheckBox,wxPGCheckBoxEditor,wxPGEditor)
|
|
|
|
|
|
// state argument: 0x01 = set if checked
|
|
// 0x02 = set if rectangle should be bold
|
|
static void DrawSimpleCheckBox( wxDC& dc, const wxRect& rect, int box_hei, int state, const wxColour& linecol )
|
|
{
|
|
|
|
// Box rectangle.
|
|
wxRect r(rect.x+wxPG_XBEFORETEXT,rect.y+((rect.height-box_hei)/2),box_hei,box_hei);
|
|
|
|
// Draw check mark first because it is likely to overdraw the
|
|
// surrounding rectangle.
|
|
if ( state & 1 )
|
|
{
|
|
wxRect r2(r.x+wxPG_CHECKMARK_XADJ,
|
|
r.y+wxPG_CHECKMARK_YADJ,
|
|
r.width+wxPG_CHECKMARK_WADJ,
|
|
r.height+wxPG_CHECKMARK_HADJ);
|
|
#if wxPG_CHECKMARK_DEFLATE
|
|
r2.Deflate(wxPG_CHECKMARK_DEFLATE);
|
|
#endif
|
|
dc.DrawCheckMark(r2);
|
|
|
|
// This would draw a simple cross check mark.
|
|
// dc.DrawLine(r.x,r.y,r.x+r.width-1,r.y+r.height-1);
|
|
// dc.DrawLine(r.x,r.y+r.height-1,r.x+r.width-1,r.y);
|
|
|
|
}
|
|
|
|
if ( !(state & 2) )
|
|
{
|
|
// Pen for thin rectangle.
|
|
dc.SetPen(linecol);
|
|
}
|
|
else
|
|
{
|
|
// Pen for bold rectangle.
|
|
wxPen linepen(linecol,2,wxSOLID);
|
|
linepen.SetJoin(wxJOIN_MITER); // This prevents round edges.
|
|
dc.SetPen(linepen);
|
|
r.x++;
|
|
r.y++;
|
|
r.width--;
|
|
r.height--;
|
|
}
|
|
|
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
|
|
|
dc.DrawRectangle(r);
|
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
|
}
|
|
|
|
//
|
|
// Real simple custom-drawn checkbox-without-label class.
|
|
//
|
|
class wxSimpleCheckBox : public wxControl
|
|
{
|
|
public:
|
|
|
|
void SetValue( int value );
|
|
|
|
wxSimpleCheckBox( wxWindow* parent,
|
|
wxWindowID id,
|
|
const wxPoint& pos = wxDefaultPosition,
|
|
const wxSize& size = wxDefaultSize )
|
|
: wxControl(parent,id,pos,size,wxNO_BORDER|wxWANTS_CHARS)
|
|
{
|
|
// Due to SetOwnFont stuff necessary for GTK+ 1.2, we need to have this
|
|
SetFont( parent->GetFont() );
|
|
|
|
m_state = 0;
|
|
m_boxHeight = ((wxPropertyGrid*)parent)->GetFontHeight();
|
|
SetBackgroundStyle( wxBG_STYLE_COLOUR );
|
|
}
|
|
|
|
virtual ~wxSimpleCheckBox();
|
|
|
|
virtual bool ProcessEvent(wxEvent& event);
|
|
|
|
int m_state;
|
|
int m_boxHeight;
|
|
|
|
static wxBitmap* ms_doubleBuffer;
|
|
|
|
};
|
|
|
|
wxSimpleCheckBox::~wxSimpleCheckBox()
|
|
{
|
|
delete ms_doubleBuffer;
|
|
ms_doubleBuffer = NULL;
|
|
}
|
|
|
|
|
|
wxBitmap* wxSimpleCheckBox::ms_doubleBuffer = (wxBitmap*) NULL;
|
|
|
|
// value = 2 means toggle (sorry, too lazy to do constants)
|
|
void wxSimpleCheckBox::SetValue( int value )
|
|
{
|
|
if ( value > 1 )
|
|
{
|
|
m_state++;
|
|
if ( m_state > 1 ) m_state = 0;
|
|
}
|
|
else
|
|
{
|
|
m_state = value;
|
|
}
|
|
Refresh();
|
|
|
|
wxCommandEvent evt(wxEVT_COMMAND_CHECKBOX_CLICKED,GetParent()->GetId());
|
|
((wxPropertyGrid*)GetParent())->OnCustomEditorEvent(evt);
|
|
}
|
|
|
|
|
|
bool wxSimpleCheckBox::ProcessEvent(wxEvent& event)
|
|
{
|
|
wxPropertyGrid* propGrid = (wxPropertyGrid*) GetParent();
|
|
|
|
if ( event.GetEventType() == wxEVT_NAVIGATION_KEY )
|
|
{
|
|
//wxLogDebug(wxT("wxEVT_NAVIGATION_KEY"));
|
|
//SetFocusFromKbd();
|
|
//event.Skip();
|
|
//return wxControl::ProcessEvent(event);
|
|
}
|
|
else
|
|
if ( ( (event.GetEventType() == wxEVT_LEFT_DOWN || event.GetEventType() == wxEVT_LEFT_DCLICK)
|
|
&& ((wxMouseEvent&)event).m_x > (wxPG_XBEFORETEXT-2)
|
|
&& ((wxMouseEvent&)event).m_x <= (wxPG_XBEFORETEXT-2+m_boxHeight) )
|
|
)
|
|
{
|
|
SetValue(2);
|
|
return true;
|
|
}
|
|
else if ( event.GetEventType() == wxEVT_PAINT )
|
|
{
|
|
wxSize clientSize = GetClientSize();
|
|
wxPaintDC dc(this);
|
|
|
|
/*
|
|
// Buffered paint DC doesn't seem to do much good
|
|
if ( !ms_doubleBuffer ||
|
|
clientSize.x > ms_doubleBuffer->GetWidth() ||
|
|
clientSize.y > ms_doubleBuffer->GetHeight() )
|
|
{
|
|
delete ms_doubleBuffer;
|
|
ms_doubleBuffer = new wxBitmap(clientSize.x+25,clientSize.y+25);
|
|
}
|
|
|
|
wxBufferedPaintDC dc(this,*ms_doubleBuffer);
|
|
*/
|
|
|
|
wxRect rect(0,0,clientSize.x,clientSize.y);
|
|
rect.x -= 1;
|
|
rect.width += 1;
|
|
|
|
m_boxHeight = propGrid->GetFontHeight();
|
|
|
|
wxColour bgcol = GetBackgroundColour();
|
|
dc.SetBrush( bgcol );
|
|
dc.SetPen( bgcol );
|
|
dc.DrawRectangle( rect );
|
|
|
|
wxColour txcol = GetForegroundColour();
|
|
|
|
int state = m_state;
|
|
if ( m_font.GetWeight() == wxBOLD )
|
|
state |= 2;
|
|
|
|
DrawSimpleCheckBox(dc,rect,m_boxHeight,state,txcol);
|
|
|
|
// If focused, indicate it somehow.
|
|
/*
|
|
if ( wxWindow::FindFocus() == this )
|
|
{
|
|
rect.x += 1;
|
|
rect.width -= 1;
|
|
|
|
wxPGDrawFocusRect(dc,rect);
|
|
}
|
|
*/
|
|
|
|
return true;
|
|
}
|
|
else if ( event.GetEventType() == wxEVT_SIZE ||
|
|
event.GetEventType() == wxEVT_SET_FOCUS ||
|
|
event.GetEventType() == wxEVT_KILL_FOCUS
|
|
)
|
|
{
|
|
Refresh();
|
|
}
|
|
else if ( event.GetEventType() == wxEVT_KEY_DOWN )
|
|
{
|
|
wxKeyEvent& keyEv = (wxKeyEvent&) event;
|
|
|
|
if ( keyEv.GetKeyCode() == WXK_TAB )
|
|
{
|
|
propGrid->SendNavigationKeyEvent( keyEv.ShiftDown()?0:1 );
|
|
return true;
|
|
}
|
|
else
|
|
if ( keyEv.GetKeyCode() == WXK_SPACE )
|
|
{
|
|
SetValue(2);
|
|
return true;
|
|
}
|
|
}
|
|
return wxControl::ProcessEvent(event);
|
|
}
|
|
|
|
|
|
#ifndef __WXPYTHON__
|
|
wxWindow* wxPGCheckBoxEditor::CreateControls( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
wxWindow** ) const
|
|
#else
|
|
wxPGWindowPair wxPGCheckBoxEditor::CreateControls( wxPropertyGrid* propGrid,
|
|
wxPGProperty* property,
|
|
const wxPoint& pos,
|
|
const wxSize& size ) const
|
|
#endif
|
|
{
|
|
wxPoint pt = pos;
|
|
pt.x -= wxPG_XBEFOREWIDGET;
|
|
wxSize sz = size;
|
|
sz.x += wxPG_XBEFOREWIDGET;
|
|
|
|
wxSimpleCheckBox* cb = new wxSimpleCheckBox(propGrid,wxPG_SUBID1,pt,sz);
|
|
|
|
cb->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
|
|
|
|
cb->Connect( wxPG_SUBID1, wxEVT_LEFT_DOWN,
|
|
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
|
|
&wxPropertyGrid::OnCustomEditorEvent, NULL, propGrid );
|
|
|
|
cb->Connect( wxPG_SUBID1, wxEVT_LEFT_DCLICK,
|
|
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction)
|
|
&wxPropertyGrid::OnCustomEditorEvent, NULL, propGrid );
|
|
|
|
if ( property->GetChoiceInfo((wxPGChoiceInfo*)NULL) &&
|
|
!(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
|
|
cb->m_state = 1;
|
|
|
|
// If mouse cursor was on the item, toggle the value now.
|
|
if ( propGrid->GetInternalFlags() & wxPG_FL_ACTIVATION_BY_CLICK )
|
|
{
|
|
wxPoint pt = propGrid->ScreenToClient(::wxGetMousePosition());
|
|
if ( pt.x <= (cb->GetPosition().x+wxPG_XBEFORETEXT-2+cb->m_boxHeight) )
|
|
{
|
|
cb->m_state++;
|
|
|
|
if ( cb->m_state > 1 )
|
|
cb->m_state = 0;
|
|
|
|
property->ClearFlag(wxPG_PROP_UNSPECIFIED);
|
|
property->SetValueFromInt(cb->m_state,0);
|
|
propGrid->PropertyWasModified(property);
|
|
}
|
|
}
|
|
|
|
return cb;
|
|
}
|
|
|
|
|
|
void wxPGCheckBoxEditor::DrawValue( wxDC& dc, wxPGProperty* property, const wxRect& rect ) const
|
|
{
|
|
int state = 0;
|
|
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
|
|
{
|
|
state = property->GetChoiceInfo((wxPGChoiceInfo*)NULL);
|
|
if ( dc.GetFont().GetWeight() == wxBOLD ) state |= 2;
|
|
}
|
|
DrawSimpleCheckBox(dc,rect,dc.GetCharHeight(),state,dc.GetTextForeground());
|
|
}
|
|
|
|
|
|
void wxPGCheckBoxEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
|
|
{
|
|
wxASSERT( ctrl );
|
|
((wxSimpleCheckBox*)ctrl)->m_state = property->GetChoiceInfo((wxPGChoiceInfo*)NULL);
|
|
ctrl->Refresh();
|
|
}
|
|
|
|
|
|
bool wxPGCheckBoxEditor::OnEvent( wxPropertyGrid* WXUNUSED(propGrid), wxPGProperty* WXUNUSED(property),
|
|
wxWindow* WXUNUSED(ctrl), wxEvent& event ) const
|
|
{
|
|
if ( event.GetEventType() == wxEVT_COMMAND_CHECKBOX_CLICKED )
|
|
{
|
|
/*if ( CopyValueFromControl( property, ctrl ) )
|
|
return true;
|
|
|
|
propGrid->EditorsValueWasNotModified();
|
|
|
|
CLEAR_PROPERTY_UNSPECIFIED_FLAG(property);*/
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool wxPGCheckBoxEditor::CopyValueFromControl( wxPGProperty* property, wxWindow* ctrl ) const
|
|
{
|
|
wxSimpleCheckBox* cb = (wxSimpleCheckBox*)ctrl;
|
|
|
|
int index = cb->m_state;
|
|
|
|
if ( index != property->GetChoiceInfo( (wxPGChoiceInfo*) NULL ) ||
|
|
// Changing unspecified always causes event (returning
|
|
// true here should be enough to trigger it).
|
|
property->IsFlagSet(wxPG_PROP_UNSPECIFIED)
|
|
)
|
|
{
|
|
property->SetValueFromInt(index,0);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
void wxPGCheckBoxEditor::SetControlIntValue( wxWindow* ctrl, int value ) const
|
|
{
|
|
if ( value != 0 ) value = 1;
|
|
((wxSimpleCheckBox*)ctrl)->m_state = value;
|
|
ctrl->Refresh();
|
|
}
|
|
|
|
|
|
void wxPGCheckBoxEditor::SetValueToUnspecified( wxWindow* ctrl ) const
|
|
{
|
|
((wxSimpleCheckBox*)ctrl)->m_state = 0;
|
|
ctrl->Refresh();
|
|
}
|
|
|
|
|
|
wxPGCheckBoxEditor::~wxPGCheckBoxEditor() { }
|
|
|
|
|
|
#endif // wxPG_INCLUDE_CHECKBOX
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGBrush
|
|
// -----------------------------------------------------------------------
|
|
|
|
//
|
|
// This class is a wxBrush derivative used in the background colour
|
|
// brush cache. It adds wxPG-type colour-in-long to the class.
|
|
// JMS: Yes I know wxBrush doesn't actually hold the value (refcounted
|
|
// object does), but this is simpler implementation and equally
|
|
// effective.
|
|
//
|
|
|
|
class wxPGBrush : public wxBrush
|
|
{
|
|
public:
|
|
wxPGBrush( const wxColour& colour );
|
|
wxPGBrush();
|
|
virtual ~wxPGBrush() { }
|
|
void SetColour2( const wxColour& colour );
|
|
inline long GetColourAsLong() const { return m_colAsLong; }
|
|
private:
|
|
long m_colAsLong;
|
|
};
|
|
|
|
|
|
void wxPGBrush::SetColour2( const wxColour& colour )
|
|
{
|
|
wxBrush::SetColour(colour);
|
|
m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
|
|
}
|
|
|
|
|
|
wxPGBrush::wxPGBrush() : wxBrush()
|
|
{
|
|
m_colAsLong = 0;
|
|
}
|
|
|
|
|
|
wxPGBrush::wxPGBrush( const wxColour& colour ) : wxBrush(colour)
|
|
{
|
|
m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGColour
|
|
// -----------------------------------------------------------------------
|
|
|
|
//
|
|
// Same as wxPGBrush, but for wxColour instead.
|
|
//
|
|
|
|
class wxPGColour : public wxColour
|
|
{
|
|
public:
|
|
wxPGColour( const wxColour& colour );
|
|
wxPGColour();
|
|
virtual ~wxPGColour() { }
|
|
void SetColour2( const wxColour& colour );
|
|
inline long GetColourAsLong() const { return m_colAsLong; }
|
|
private:
|
|
long m_colAsLong;
|
|
};
|
|
|
|
|
|
void wxPGColour::SetColour2( const wxColour& colour )
|
|
{
|
|
*this = colour;
|
|
m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
|
|
}
|
|
|
|
|
|
wxPGColour::wxPGColour() : wxColour()
|
|
{
|
|
m_colAsLong = 0;
|
|
}
|
|
|
|
|
|
wxPGColour::wxPGColour( const wxColour& colour ) : wxColour(colour)
|
|
{
|
|
m_colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGTLWHandler
|
|
// Intercepts Close-events sent to wxPropertyGrid's top-level parent,
|
|
// and tries to commit property value.
|
|
// -----------------------------------------------------------------------
|
|
|
|
class wxPGTLWHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
|
|
wxPGTLWHandler( wxPropertyGrid* pg )
|
|
: wxEvtHandler()
|
|
{
|
|
m_pg = pg;
|
|
}
|
|
|
|
protected:
|
|
|
|
void OnClose( wxCloseEvent& event )
|
|
{
|
|
// ClearSelection forces value validation/commit.
|
|
if ( event.CanVeto() && !m_pg->ClearSelection() )
|
|
{
|
|
event.Veto();
|
|
return;
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
private:
|
|
wxPropertyGrid* m_pg;
|
|
|
|
DECLARE_EVENT_TABLE()
|
|
};
|
|
|
|
BEGIN_EVENT_TABLE(wxPGTLWHandler, wxEvtHandler)
|
|
EVT_CLOSE(wxPGTLWHandler::OnClose)
|
|
END_EVENT_TABLE()
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid
|
|
// -----------------------------------------------------------------------
|
|
|
|
IMPLEMENT_CLASS(wxPropertyGrid, wxScrolledWindow)
|
|
|
|
BEGIN_EVENT_TABLE(wxPropertyGrid, wxScrolledWindow)
|
|
EVT_MOTION(wxPropertyGrid::OnMouseMove)
|
|
EVT_IDLE(wxPropertyGrid::OnIdle)
|
|
EVT_LEFT_DOWN(wxPropertyGrid::OnMouseClick)
|
|
EVT_LEFT_UP(wxPropertyGrid::OnMouseUp)
|
|
EVT_RIGHT_UP(wxPropertyGrid::OnMouseRightClick)
|
|
EVT_LEFT_DCLICK(wxPropertyGrid::OnMouseDoubleClick)
|
|
EVT_PAINT(wxPropertyGrid::OnPaint)
|
|
EVT_SIZE(wxPropertyGrid::OnResize)
|
|
EVT_KEY_DOWN(wxPropertyGrid::OnKey)
|
|
EVT_KEY_UP(wxPropertyGrid::OnKeyUp)
|
|
EVT_CHAR(wxPropertyGrid::OnKey)
|
|
EVT_ENTER_WINDOW(wxPropertyGrid::OnMouseEntry)
|
|
EVT_LEAVE_WINDOW(wxPropertyGrid::OnMouseEntry)
|
|
EVT_MOUSE_CAPTURE_CHANGED(wxPropertyGrid::OnCaptureChange)
|
|
EVT_SCROLLWIN(wxPropertyGrid::OnScrollEvent)
|
|
EVT_NAVIGATION_KEY(wxPropertyGrid::OnNavigationKey)
|
|
EVT_TEXT(wxPG_SUBID1,wxPropertyGrid::OnCustomEditorEvent)
|
|
EVT_COMBOBOX(wxPG_SUBID1,wxPropertyGrid::OnCustomEditorEvent)
|
|
EVT_BUTTON(wxPG_SUBID2,wxPropertyGrid::OnCustomEditorEvent)
|
|
EVT_CHILD_FOCUS(wxPropertyGrid::OnChildFocusEvent)
|
|
EVT_SET_FOCUS(wxPropertyGrid::OnFocusEvent)
|
|
EVT_KILL_FOCUS(wxPropertyGrid::OnFocusEvent)
|
|
EVT_TEXT_ENTER(wxPG_SUBID1,wxPropertyGrid::OnCustomEditorEvent)
|
|
EVT_SYS_COLOUR_CHANGED(wxPropertyGrid::OnSysColourChanged)
|
|
END_EVENT_TABLE()
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGrid::wxPropertyGrid()
|
|
: wxScrolledWindow()
|
|
{
|
|
Init1();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGrid::wxPropertyGrid( wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxChar* name )
|
|
: wxScrolledWindow()
|
|
{
|
|
Init1();
|
|
Create(parent,id,pos,size,style,name);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::Create( wxWindow *parent,
|
|
wxWindowID id,
|
|
const wxPoint& pos,
|
|
const wxSize& size,
|
|
long style,
|
|
const wxChar* name )
|
|
{
|
|
|
|
if ( !(style&wxBORDER_MASK) )
|
|
style |= wxSIMPLE_BORDER;
|
|
|
|
style |= wxVSCROLL;
|
|
|
|
#ifdef __WXMSW__
|
|
// This prevents crash under Win2K, but still
|
|
// enables keyboard navigation
|
|
if ( style & wxTAB_TRAVERSAL )
|
|
{
|
|
style &= ~(wxTAB_TRAVERSAL);
|
|
style |= wxWANTS_CHARS;
|
|
}
|
|
#else
|
|
if ( style & wxTAB_TRAVERSAL )
|
|
style |= wxWANTS_CHARS;
|
|
#endif
|
|
|
|
wxScrolledWindow::Create(parent,id,pos,size,style,name);
|
|
|
|
Init2();
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
static void wxPGRegisterStandardPropertyClasses();
|
|
|
|
//
|
|
// Initialize values to defaults
|
|
//
|
|
void wxPropertyGrid::Init1()
|
|
{
|
|
WX_PG_GLOBALS_LOCKER()
|
|
|
|
#if !wxPG_USE_WXMODULE
|
|
if ( !wxPGGlobalVars )
|
|
wxPGGlobalVars = new wxPGGlobalVarsClass();
|
|
#endif
|
|
|
|
// Register type classes, if necessary.
|
|
if ( wxPGGlobalVars->m_dictValueType.empty() )
|
|
RegisterDefaultValues();
|
|
|
|
// Register editor classes, if necessary.
|
|
if ( wxPGGlobalVars->m_mapEditorClasses.empty() )
|
|
RegisterDefaultEditors();
|
|
|
|
// Register property classes, if necessary
|
|
if ( wxPGGlobalVars->m_dictPropertyClassInfo.empty() )
|
|
wxPGRegisterStandardPropertyClasses();
|
|
|
|
m_iFlags = 0;
|
|
m_pState = (wxPropertyGridState*) NULL;
|
|
m_wndPrimary = m_wndSecondary = (wxWindow*) NULL;
|
|
m_selected = (wxPGProperty*) NULL;
|
|
m_propHover = (wxPGProperty*) NULL;
|
|
m_eventObject = this;
|
|
m_curFocused = (wxWindow*) NULL;
|
|
m_tlwHandler = NULL;
|
|
m_processingEvent = 0;
|
|
m_dragStatus = 0;
|
|
m_mouseSide = 16;
|
|
m_editorFocused = 0;
|
|
m_coloursCustomized = 0;
|
|
m_frozen = 0;
|
|
|
|
#if wxPG_DOUBLE_BUFFER
|
|
m_doubleBuffer = (wxBitmap*) NULL;
|
|
#endif
|
|
|
|
m_windowsToDelete = NULL;
|
|
|
|
#ifndef wxPG_ICON_WIDTH
|
|
m_expandbmp = NULL;
|
|
m_collbmp = NULL;
|
|
m_iconWidth = 11;
|
|
m_iconHeight = 11;
|
|
#else
|
|
m_iconWidth = wxPG_ICON_WIDTH;
|
|
#endif
|
|
|
|
m_prevVY = -1;
|
|
|
|
m_calcVisHeight = 0;
|
|
|
|
m_gutterWidth = wxPG_GUTTER_MIN;
|
|
m_subgroup_extramargin = 10;
|
|
|
|
m_lineHeight = 0;
|
|
|
|
m_width = m_height = m_fWidth = 0;
|
|
|
|
m_bottomy = 0;
|
|
|
|
m_splitterx = wxPG_DEFAULT_SPLITTERX;
|
|
m_fSplitterX = (float) wxPG_DEFAULT_SPLITTERX;
|
|
|
|
#if !wxPG_HEAVY_GFX
|
|
m_splitterpen.SetColour( *wxBLACK );
|
|
m_splitterpen.SetWidth( 4 );
|
|
|
|
m_splitterprevdrawnx = -1;
|
|
#endif
|
|
|
|
SetButtonShortcut(0);
|
|
|
|
m_keyComboConsumed = 0;
|
|
|
|
m_ignoredEvents = 0;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
//
|
|
// Initialize after parent etc. set
|
|
//
|
|
void wxPropertyGrid::Init2()
|
|
{
|
|
wxASSERT( !(m_iFlags & wxPG_FL_INITIALIZED ) );
|
|
|
|
#ifdef __WXMAC__
|
|
// Smaller controls on Mac
|
|
SetWindowVariant(wxWINDOW_VARIANT_SMALL);
|
|
#endif
|
|
|
|
// Now create state, if one didn't exist already
|
|
// (wxPropertyGridManager might have created it for us).
|
|
if ( !m_pState )
|
|
{
|
|
m_pState = CreateState();
|
|
m_pState->m_pPropGrid = this;
|
|
m_iFlags |= wxPG_FL_CREATEDSTATE;
|
|
}
|
|
|
|
if ( !(m_windowStyle & wxPG_SPLITTER_AUTO_CENTER) )
|
|
m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
|
|
|
|
if ( m_windowStyle & wxPG_HIDE_CATEGORIES )
|
|
{
|
|
m_pState->InitNonCatMode();
|
|
|
|
FROM_STATE(m_properties) = FROM_STATE(m_abcArray);
|
|
}
|
|
|
|
GetClientSize(&m_width,&m_height);
|
|
|
|
#if !wxPG_HEAVY_GFX
|
|
m_splitterpen.SetColour( *wxBLACK );
|
|
m_splitterpen.SetWidth( 4 );
|
|
#endif
|
|
|
|
#ifndef wxPG_ICON_WIDTH
|
|
// create two bitmap nodes for drawing
|
|
m_expandbmp = new wxBitmap(expand_xpm);
|
|
m_collbmp = new wxBitmap(collapse_xpm);
|
|
|
|
// calculate average font height for bitmap centering
|
|
|
|
m_iconWidth = m_expandbmp->GetWidth();
|
|
m_iconHeight = m_expandbmp->GetHeight();
|
|
#endif
|
|
|
|
m_curcursor = wxCURSOR_ARROW;
|
|
m_cursorSizeWE = new wxCursor( wxCURSOR_SIZEWE );
|
|
|
|
// adjust bitmap icon y position so they are centered
|
|
m_vspacing = wxPG_DEFAULT_VSPACING;
|
|
|
|
if ( !m_font.Ok() )
|
|
{
|
|
wxFont useFont = wxScrolledWindow::GetFont();
|
|
wxScrolledWindow::SetOwnFont( useFont );
|
|
}
|
|
else
|
|
// This should be otherwise called by SetOwnFont
|
|
CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING );
|
|
|
|
// Add base brush item
|
|
m_arrBgBrushes.Add((void*)new wxPGBrush());
|
|
|
|
// Add base colour items
|
|
m_arrFgCols.Add((void*)new wxPGColour());
|
|
m_arrFgCols.Add((void*)new wxPGColour());
|
|
|
|
RegainColours();
|
|
|
|
// This helps with flicker
|
|
SetBackgroundStyle( wxBG_STYLE_CUSTOM );
|
|
|
|
// Hook the TLW
|
|
wxPGTLWHandler* handler = new wxPGTLWHandler(this);
|
|
m_tlp = ::wxGetTopLevelParent(this);
|
|
m_tlwHandler = handler;
|
|
m_tlp->PushEventHandler(handler);
|
|
|
|
// set virtual size to this window size
|
|
wxSize wndsize = GetSize();
|
|
SetVirtualSize(wndsize.GetWidth(), wndsize.GetWidth());
|
|
|
|
m_timeCreated = ::wxGetLocalTimeMillis();
|
|
|
|
m_iFlags |= wxPG_FL_INITIALIZED;
|
|
|
|
// Need to call OnResize handler or size given in constructor/Create
|
|
// will never work.
|
|
wxSizeEvent sizeEvent(wndsize,0);
|
|
OnResize(sizeEvent);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGrid::~wxPropertyGrid()
|
|
{
|
|
size_t i;
|
|
|
|
DoSelectProperty(NULL);
|
|
|
|
// This should do prevent things from going too badly wrong
|
|
m_iFlags &= ~(wxPG_FL_INITIALIZED);
|
|
|
|
END_MOUSE_CAPTURE
|
|
|
|
wxPGTLWHandler* handler = (wxPGTLWHandler*) m_tlwHandler;
|
|
m_tlp->RemoveEventHandler(handler);
|
|
delete handler;
|
|
|
|
#ifdef __WXDEBUG__
|
|
if ( IsEditorsValueModified() )
|
|
::wxMessageBox(wxT("Most recent change in property editor was lost!!!\n\n(if you don't want this to happen, close your frames and dialogs using Close(false).)"),
|
|
wxT("wxPropertyGrid Debug Warning") );
|
|
#endif
|
|
|
|
#if wxPG_DOUBLE_BUFFER
|
|
if ( m_doubleBuffer )
|
|
delete m_doubleBuffer;
|
|
#endif
|
|
|
|
delete m_windowsToDelete;
|
|
|
|
m_selected = (wxPGProperty*) NULL;
|
|
|
|
if ( m_iFlags & wxPG_FL_CREATEDSTATE )
|
|
delete m_pState;
|
|
|
|
delete m_cursorSizeWE;
|
|
|
|
#ifndef wxPG_ICON_WIDTH
|
|
delete m_expandbmp;
|
|
delete m_collbmp;
|
|
#endif
|
|
|
|
// Delete cached text colours.
|
|
for ( i=0; i<m_arrFgCols.GetCount(); i++ )
|
|
{
|
|
delete (wxPGColour*)m_arrFgCols.Item(i);
|
|
}
|
|
|
|
// Delete cached brushes.
|
|
for ( i=0; i<m_arrBgBrushes.GetCount(); i++ )
|
|
{
|
|
delete (wxPGBrush*)m_arrBgBrushes.Item(i);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::Destroy()
|
|
{
|
|
END_MOUSE_CAPTURE
|
|
return wxScrolledWindow::Destroy();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGridState* wxPropertyGrid::CreateState() const
|
|
{
|
|
return new wxPropertyGridState();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid overridden wxWindow methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetWindowStyleFlag( long style )
|
|
{
|
|
long old_style = m_windowStyle;
|
|
|
|
if ( m_iFlags & wxPG_FL_INITIALIZED )
|
|
{
|
|
wxASSERT( m_pState );
|
|
|
|
if ( !(style & wxPG_HIDE_CATEGORIES) && (old_style & wxPG_HIDE_CATEGORIES) )
|
|
{
|
|
// Enable categories
|
|
EnableCategories( true );
|
|
}
|
|
else if ( (style & wxPG_HIDE_CATEGORIES) && !(old_style & wxPG_HIDE_CATEGORIES) )
|
|
{
|
|
// Disable categories
|
|
EnableCategories( false );
|
|
}
|
|
if ( !(old_style & wxPG_AUTO_SORT) && (style & wxPG_AUTO_SORT) )
|
|
{
|
|
//
|
|
// Autosort enabled
|
|
//
|
|
if ( !m_frozen )
|
|
PrepareAfterItemsAdded();
|
|
else
|
|
FROM_STATE(m_itemsAdded) = 1;
|
|
}
|
|
#if wxPG_SUPPORT_TOOLTIPS
|
|
if ( !(old_style & wxPG_TOOLTIPS) && (style & wxPG_TOOLTIPS) )
|
|
{
|
|
//
|
|
// Tooltips enabled
|
|
//
|
|
/*wxToolTip* tooltip = new wxToolTip ( wxEmptyString );
|
|
SetToolTip ( tooltip );
|
|
tooltip->SetDelay ( wxPG_TOOLTIP_DELAY );*/
|
|
}
|
|
else if ( (old_style & wxPG_TOOLTIPS) && !(style & wxPG_TOOLTIPS) )
|
|
{
|
|
//
|
|
// Tooltips disabled
|
|
//
|
|
wxScrolledWindow::SetToolTip ( (wxToolTip*) NULL );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
wxScrolledWindow::SetWindowStyleFlag ( style );
|
|
|
|
if ( m_iFlags & wxPG_FL_INITIALIZED )
|
|
{
|
|
if ( (old_style & wxPG_HIDE_MARGIN) != (style & wxPG_HIDE_MARGIN) )
|
|
{
|
|
CalculateFontAndBitmapStuff( m_vspacing );
|
|
RedrawAllVisible();
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::Freeze()
|
|
{
|
|
m_frozen++;
|
|
wxScrolledWindow::Freeze();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::Thaw()
|
|
{
|
|
m_frozen--;
|
|
wxScrolledWindow::Thaw();
|
|
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
|
|
Refresh();
|
|
#endif
|
|
|
|
// Force property re-selection
|
|
if ( m_selected )
|
|
DoSelectProperty(m_selected, wxPG_SEL_FORCE);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetExtraStyle( long exStyle )
|
|
{
|
|
if ( exStyle & wxPG_EX_NATIVE_DOUBLE_BUFFERING )
|
|
{
|
|
#if defined(__WXMSW__)
|
|
|
|
/*
|
|
// Don't use WS_EX_COMPOSITED just now.
|
|
HWND hWnd;
|
|
|
|
if ( m_iFlags & wxPG_FL_IN_MANAGER )
|
|
hWnd = (HWND)GetParent()->GetHWND();
|
|
else
|
|
hWnd = (HWND)GetHWND();
|
|
|
|
::SetWindowLong( hWnd, GWL_EXSTYLE,
|
|
::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED );
|
|
*/
|
|
|
|
//#elif defined(__WXGTK20__)
|
|
#endif
|
|
// Only apply wxPG_EX_NATIVE_DOUBLE_BUFFERING if the window
|
|
// truly was double-buffered.
|
|
if ( !wxPGIsWindowBuffered(this) )
|
|
{
|
|
exStyle &= ~(wxPG_EX_NATIVE_DOUBLE_BUFFERING);
|
|
}
|
|
else
|
|
{
|
|
#if wxPG_DOUBLE_BUFFER
|
|
delete m_doubleBuffer;
|
|
m_doubleBuffer = NULL;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
wxScrolledWindow::SetExtraStyle( exStyle );
|
|
|
|
if ( exStyle & wxPG_EX_INIT_NOCAT )
|
|
m_pState->InitNonCatMode ();
|
|
|
|
if ( exStyle & wxPG_EX_HELP_AS_TOOLTIPS )
|
|
m_windowStyle |= wxPG_TOOLTIPS;
|
|
|
|
if ( exStyle & wxPG_EX_AUTO_UNSPECIFIED_VALUES )
|
|
{
|
|
wxPGGlobalVars->m_numBoolChoices = 3;
|
|
}
|
|
else
|
|
{
|
|
wxPGGlobalVars->m_numBoolChoices = 2;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// returns the best acceptable minimal size
|
|
wxSize wxPropertyGrid::DoGetBestSize() const
|
|
{
|
|
int hei = 15;
|
|
if ( m_lineHeight > hei )
|
|
hei = m_lineHeight;
|
|
wxSize sz = wxSize( 60, hei+40 );
|
|
|
|
CacheBestSize(sz);
|
|
return sz;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid Font and Colour Methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing )
|
|
{
|
|
int x = 0, y = 0;
|
|
|
|
m_captionFont = GetFont();
|
|
|
|
GetTextExtent(wxT("jG"), &x, &y, 0, 0, &m_captionFont);
|
|
m_subgroup_extramargin = x + (x/2);
|
|
m_fontHeight = y;
|
|
|
|
#if wxPG_USE_RENDERER_NATIVE
|
|
m_iconWidth = wxPG_ICON_WIDTH;
|
|
#elif wxPG_ICON_WIDTH
|
|
// scale icon
|
|
m_iconWidth = (m_fontHeight * wxPG_ICON_WIDTH) / 13;
|
|
if ( m_iconWidth < 5 ) m_iconWidth = 5;
|
|
else if ( !(m_iconWidth & 0x01) ) m_iconWidth++; // must be odd
|
|
|
|
#endif
|
|
|
|
m_gutterWidth = m_iconWidth / wxPG_GUTTER_DIV;
|
|
if ( m_gutterWidth < wxPG_GUTTER_MIN )
|
|
m_gutterWidth = wxPG_GUTTER_MIN;
|
|
|
|
int vdiv = 6;
|
|
if ( vspacing <= 1 ) vdiv = 12;
|
|
else if ( vspacing >= 3 ) vdiv = 3;
|
|
|
|
m_spacingy = m_fontHeight / vdiv;
|
|
if ( m_spacingy < wxPG_YSPACING_MIN )
|
|
m_spacingy = wxPG_YSPACING_MIN;
|
|
|
|
m_marginWidth = 0;
|
|
if ( !(m_windowStyle & wxPG_HIDE_MARGIN) )
|
|
m_marginWidth = m_gutterWidth*2 + m_iconWidth;
|
|
|
|
m_captionFont.SetWeight(wxBOLD);
|
|
GetTextExtent(wxT("jG"), &x, &y, 0, 0, &m_captionFont);
|
|
|
|
m_lineHeight = m_fontHeight+(2*m_spacingy)+1;
|
|
|
|
// button spacing
|
|
m_buttonSpacingY = (m_lineHeight - m_iconHeight) / 2;
|
|
if ( m_buttonSpacingY < 0 ) m_buttonSpacingY = 0;
|
|
|
|
InvalidateBestSize();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnSysColourChanged( wxSysColourChangedEvent &WXUNUSED(event) )
|
|
{
|
|
RegainColours();
|
|
Refresh();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
static wxColour wxPGAdjustColour(const wxColour& src, int ra,
|
|
int ga = 1000, int ba = 1000,
|
|
bool forceDifferent = false)
|
|
{
|
|
if ( ga >= 1000 )
|
|
ga = ra;
|
|
if ( ba >= 1000 )
|
|
ba = ra;
|
|
|
|
// Recursion guard (allow 2 max)
|
|
static int isinside = 0;
|
|
isinside++;
|
|
wxCHECK_MSG( isinside < 3,
|
|
*wxBLACK,
|
|
wxT("wxPGAdjustColour should not be recursively called more than once") );
|
|
|
|
wxColour dst;
|
|
|
|
int r = src.Red();
|
|
int g = src.Green();
|
|
int b = src.Blue();
|
|
int r2 = r + ra;
|
|
if ( r2>255 ) r2 = 255;
|
|
else if ( r2<0) r2 = 0;
|
|
int g2 = g + ga;
|
|
if ( g2>255 ) g2 = 255;
|
|
else if ( g2<0) g2 = 0;
|
|
int b2 = b + ba;
|
|
if ( b2>255 ) b2 = 255;
|
|
else if ( b2<0) b2 = 0;
|
|
|
|
// Make sure they are somewhat different
|
|
if ( forceDifferent && (abs((r+g+b)-(r2+g2+b2)) < abs(ra/2)) )
|
|
dst = wxPGAdjustColour(src,-(ra*2));
|
|
else
|
|
dst = wxColour(r2,g2,b2);
|
|
|
|
// Recursion guard (allow 2 max)
|
|
isinside--;
|
|
|
|
return dst;
|
|
}
|
|
|
|
|
|
static int wxPGGetColAvg( const wxColour& col )
|
|
{
|
|
return (col.Red() + col.Green() + col.Blue()) / 3;
|
|
}
|
|
|
|
|
|
void wxPropertyGrid::RegainColours()
|
|
{
|
|
wxColour def_bgcol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
|
|
|
|
if ( !(m_coloursCustomized & 0x0002) )
|
|
{
|
|
wxColour col = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
|
|
|
|
// Make sure colour is dark enough
|
|
#ifdef __WXGTK__
|
|
int colDec = wxPGGetColAvg(col) - 230;
|
|
#else
|
|
int colDec = wxPGGetColAvg(col) - 200;
|
|
#endif
|
|
if ( colDec > 0 )
|
|
m_colCapBack = wxPGAdjustColour(col,-colDec);
|
|
else
|
|
m_colCapBack = col;
|
|
}
|
|
|
|
if ( !(m_coloursCustomized & 0x0001) )
|
|
m_colMargin = m_colCapBack;
|
|
|
|
if ( !(m_coloursCustomized & 0x0004) )
|
|
{
|
|
#ifdef __WXGTK__
|
|
int colDec = -90;
|
|
#else
|
|
int colDec = -72;
|
|
#endif
|
|
wxColour capForeCol = wxPGAdjustColour(m_colCapBack,colDec,5000,5000,true);
|
|
m_colCapFore = capForeCol;
|
|
|
|
// Set the cached colour as well.
|
|
((wxPGColour*)m_arrFgCols.Item(1))->SetColour2(capForeCol);
|
|
}
|
|
|
|
if ( !(m_coloursCustomized & 0x0008) )
|
|
{
|
|
wxColour bgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
|
|
m_colPropBack = bgCol;
|
|
|
|
// Set the cached brush as well.
|
|
((wxPGBrush*)m_arrBgBrushes.Item(0))->SetColour2(bgCol);
|
|
}
|
|
|
|
if ( !(m_coloursCustomized & 0x0010) )
|
|
{
|
|
wxColour fgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
|
|
m_colPropFore = fgCol;
|
|
|
|
// Set the cached colour as well.
|
|
((wxPGColour*)m_arrFgCols.Item(0))->SetColour2(fgCol);
|
|
}
|
|
|
|
if ( !(m_coloursCustomized & 0x0020) )
|
|
m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
|
|
|
|
if ( !(m_coloursCustomized & 0x0040) )
|
|
m_colSelFore = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT );
|
|
|
|
if ( !(m_coloursCustomized & 0x0080) )
|
|
m_colLine = m_colCapBack;
|
|
|
|
if ( !(m_coloursCustomized & 0x0100) )
|
|
m_colDisPropFore = m_colCapFore;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::ResetColours()
|
|
{
|
|
m_coloursCustomized = 0;
|
|
|
|
RegainColours();
|
|
|
|
Refresh();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::SetFont( const wxFont& font )
|
|
{
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug( wxT("wxPropertyGrid::SetFont") );
|
|
#endif
|
|
|
|
// Must disable active editor.
|
|
if ( m_selected )
|
|
{
|
|
bool selRes = ClearSelection();
|
|
wxPG_CHECK_MSG_DBG( selRes,
|
|
false,
|
|
wxT("failed to deselect a property (editor probably had invalid value)") );
|
|
}
|
|
|
|
// TODO: Following code is disabled with wxMac because
|
|
// it is reported to fail. I (JMS) cannot debug it
|
|
// personally right now.
|
|
#if !defined(__WXMAC__)
|
|
bool res = wxScrolledWindow::SetFont( font );
|
|
if ( res )
|
|
{
|
|
|
|
CalculateFontAndBitmapStuff( m_vspacing );
|
|
|
|
if ( m_pState )
|
|
{
|
|
// Recalculate caption text extents.
|
|
// TODO: This should also be done to other pages of manager
|
|
// (so add wxPropertyGridManager::SetFont), but since font
|
|
// is usually set before categories are added, this is
|
|
// quite low priority.
|
|
size_t i;
|
|
|
|
for ( i=0;i<FROM_STATE(m_regularArray).GetCount();i++ )
|
|
{
|
|
wxPGProperty* p = FROM_STATE(m_regularArray).Item(i);
|
|
|
|
if ( p->GetParentingType() > 0 )
|
|
((wxPropertyCategoryClass*)p)->CalculateTextExtent(this,m_captionFont);
|
|
}
|
|
|
|
CalculateYs(NULL,-1);
|
|
}
|
|
|
|
Refresh();
|
|
}
|
|
|
|
return res;
|
|
#else
|
|
// ** wxMAC Only **
|
|
// TODO: Remove after SetFont crash fixed.
|
|
if ( m_iFlags & wxPG_FL_INITIALIZED )
|
|
{
|
|
wxLogDebug(wxT("WARNING: propGrid.cpp: wxPropertyGrid::SetFont has been disabled on wxMac since there has been crash reported in it. If you are willing to debug the cause, replace line '#if !defined(__WXMAC__)' with line '#if 1' in wxPropertyGrid::SetFont."));
|
|
}
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetLineColour( const wxColour& col )
|
|
{
|
|
m_colLine = col;
|
|
m_coloursCustomized |= 0x80;
|
|
Refresh();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetMarginColour( const wxColour& col )
|
|
{
|
|
m_colMargin = col;
|
|
m_coloursCustomized |= 0x01;
|
|
Refresh();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetCellBackgroundColour( const wxColour& col )
|
|
{
|
|
m_colPropBack = col;
|
|
m_coloursCustomized |= 0x08;
|
|
|
|
// Set the cached brush as well.
|
|
((wxPGBrush*)m_arrBgBrushes.Item(0))->SetColour2(col);
|
|
|
|
Refresh();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetCellTextColour( const wxColour& col )
|
|
{
|
|
m_colPropFore = col;
|
|
m_coloursCustomized |= 0x10;
|
|
|
|
// Set the cached colour as well.
|
|
((wxPGColour*)m_arrFgCols.Item(0))->SetColour2(col);
|
|
|
|
Refresh();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetCellDisabledTextColour( const wxColour& col )
|
|
{
|
|
m_colDisPropFore = col;
|
|
m_coloursCustomized |= 0x100;
|
|
Refresh();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetSelectionBackground( const wxColour& col )
|
|
{
|
|
m_colSelBack = col;
|
|
m_coloursCustomized |= 0x20;
|
|
Refresh();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetSelectionForeground( const wxColour& col )
|
|
{
|
|
m_colSelFore = col;
|
|
m_coloursCustomized |= 0x40;
|
|
Refresh();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetCaptionBackgroundColour( const wxColour& col )
|
|
{
|
|
m_colCapBack = col;
|
|
m_coloursCustomized |= 0x02;
|
|
Refresh();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetCaptionForegroundColour( const wxColour& col )
|
|
{
|
|
m_colCapFore = col;
|
|
m_coloursCustomized |= 0x04;
|
|
|
|
// Set the cached colour as well.
|
|
((wxPGColour*)m_arrFgCols.Item(1))->SetColour2(col);
|
|
|
|
Refresh();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetBackgroundColourIndex( wxPGProperty* p, int index, int flags )
|
|
{
|
|
unsigned char ind = index;
|
|
|
|
if ( (p->m_bgColIndex == 0) || (flags & (wxPG_RECURSE_STARTS|wxPG_FORCE)) )
|
|
p->m_bgColIndex = ind;
|
|
|
|
if ( p->GetParentingType() != 0 && (flags & wxPG_RECURSE) )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
unsigned int i;
|
|
for ( i=0; i<pwc->GetCount(); i++ )
|
|
SetBackgroundColourIndex(pwc->Item(i), index, flags & ~(wxPG_RECURSE_STARTS));
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetPropertyBackgroundColour( wxPGId id, const wxColour& colour )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
size_t i;
|
|
int colInd = -1;
|
|
|
|
long colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
|
|
|
|
// As it is most likely that the previous colour is used, start comparison
|
|
// from the end.
|
|
for ( i=(m_arrBgBrushes.GetCount()-1); i>0; i-- )
|
|
{
|
|
if ( ((wxPGBrush*)m_arrBgBrushes.Item(i))->GetColourAsLong() == colAsLong )
|
|
{
|
|
colInd = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( colInd < 0 )
|
|
{
|
|
colInd = m_arrBgBrushes.GetCount();
|
|
wxCHECK_RET( colInd < 256, wxT("wxPropertyGrid: Warning - Only 255 different property background colours allowed.") );
|
|
m_arrBgBrushes.Add( (void*)new wxPGBrush(colour) );
|
|
}
|
|
|
|
// Set indexes
|
|
SetBackgroundColourIndex(p, colInd, wxPG_RECURSE|wxPG_RECURSE_STARTS);
|
|
|
|
// If this was on a visible grid, then draw it.
|
|
DrawItemAndChildren(p);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxColour wxPropertyGrid::GetPropertyBackgroundColour( wxPGId id ) const
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxColour())
|
|
|
|
return ((wxPGBrush*)m_arrBgBrushes.Item(p->m_bgColIndex))->GetColour();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetTextColourIndex( wxPGProperty* p, int index, int flags )
|
|
{
|
|
unsigned char ind = index;
|
|
|
|
if ( (p->m_fgColIndex == 0) || (flags & (wxPG_RECURSE_STARTS|wxPG_FORCE)) )
|
|
p->m_fgColIndex = ind;
|
|
|
|
if ( p->GetParentingType() != 0 && (flags & wxPG_RECURSE) )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
unsigned int i;
|
|
for ( i=0; i<pwc->GetCount(); i++ )
|
|
SetTextColourIndex( pwc->Item(i), index, flags&~(wxPG_RECURSE_STARTS) );
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
int wxPropertyGrid::CacheColour( const wxColour& colour )
|
|
{
|
|
unsigned int i;
|
|
int colInd = -1;
|
|
|
|
long colAsLong = wxPG_COLOUR(colour.Red(),colour.Green(),colour.Blue());
|
|
|
|
// As it is most likely that the previous colour is used, start comparison
|
|
// from the end.
|
|
for ( i=(m_arrFgCols.GetCount()-1); i>0; i-- )
|
|
{
|
|
if ( ((wxPGColour*)m_arrFgCols.Item(i))->GetColourAsLong() == colAsLong )
|
|
{
|
|
colInd = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( colInd < 0 )
|
|
{
|
|
colInd = m_arrFgCols.GetCount();
|
|
wxCHECK_MSG( colInd < 256, 0, wxT("wxPropertyGrid: Warning - Only 255 different property foreground colours allowed.") );
|
|
m_arrFgCols.Add( (void*)new wxPGColour(colour) );
|
|
}
|
|
|
|
return colInd;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetPropertyTextColour( wxPGId id, const wxColour& colour )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
// Set indexes
|
|
SetTextColourIndex(p, CacheColour(colour), wxPG_RECURSE|wxPG_RECURSE_STARTS );
|
|
|
|
// If this was on a visible grid, then draw it.
|
|
DrawItemAndChildren(p);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetCaptionTextColour( wxPGId id, const wxColour& colour )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
wxCHECK_RET( p->GetParentingType() == PT_CAPTION,
|
|
wxT("Only call SetCaptionTextColour for caption properties") );
|
|
|
|
// Set indexes
|
|
wxPropertyCategoryClass* cat = (wxPropertyCategoryClass*) p;
|
|
cat->SetTextColIndex(CacheColour(colour));
|
|
|
|
// If this was on a visible grid, then draw it.
|
|
DrawItemAndChildren(p);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxColour wxPropertyGrid::GetPropertyTextColour( wxPGId id ) const
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxColour())
|
|
|
|
return wxColour(*((wxPGColour*)m_arrFgCols.Item(p->m_fgColIndex)));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetPropertyColourToDefault( wxPGId id )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
SetBackgroundColourIndex( p, 0, wxPG_RECURSE|wxPG_FORCE );
|
|
SetTextColourIndex( p, 0, wxPG_RECURSE|wxPG_FORCE );
|
|
|
|
if ( p->GetParentingType() == PT_CAPTION )
|
|
{
|
|
wxPropertyCategoryClass* cat = (wxPropertyCategoryClass*) p;
|
|
cat->SetTextColIndex(1);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid property adding and removal
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGrid::Append( wxPGProperty* property )
|
|
{
|
|
return FROM_STATE(Append(property));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGrid::_Insert( wxPGProperty* priorthis, wxPGProperty* property )
|
|
{
|
|
wxASSERT( priorthis );
|
|
return FROM_STATE(DoInsert(priorthis->GetParent(), priorthis->GetArrIndex(), property));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyContainerMethods::Delete( wxPGId id )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
wxPropertyGridState* state = p->GetParentState();
|
|
wxPropertyGrid* grid = state->GetGrid();
|
|
|
|
if ( grid->GetState() == state )
|
|
{
|
|
bool selRes = grid->DoSelectProperty(wxPGIdGen(NULL), wxPG_SEL_DELETING);
|
|
wxPG_CHECK_RET_DBG( selRes,
|
|
wxT("failed to deselect a property (editor probably had invalid value)") );
|
|
}
|
|
|
|
state->DoDelete( p );
|
|
|
|
if ( grid->GetState() == state && !grid->IsFrozen() )
|
|
{
|
|
// This should be enough to resolve even the worst
|
|
// graphics glitch imaginable.
|
|
grid->Update();
|
|
grid->Refresh();
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyContainerMethods::ReplaceProperty( wxPGId id, wxPGProperty* property )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
|
|
|
|
wxPGProperty* replaced = wxPGIdToPtr(id);
|
|
wxCHECK_MSG( replaced && property,
|
|
wxNullProperty,
|
|
wxT("NULL property") );
|
|
wxCHECK_MSG( replaced->GetParentingType() == 0 || replaced->GetParentingType() == -1,
|
|
wxNullProperty,
|
|
wxT("cannot replace this type of property") );
|
|
wxCHECK_MSG( !m_pState->IsInNonCatMode(),
|
|
wxNullProperty,
|
|
wxT("cannot replace properties in alphabetic mode") );
|
|
|
|
// Get address to the slot
|
|
wxPGPropertyWithChildren* parent = replaced->GetParent();
|
|
int ind = replaced->GetIndexInParent();
|
|
|
|
wxPropertyGridState* state = replaced->GetParentState();
|
|
Delete(replaced); // Must use generic Delete
|
|
state->DoInsert(parent,ind,property);
|
|
|
|
return wxPGIdGen(property);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::PrepareAfterItemsAdded()
|
|
{
|
|
|
|
if ( !FROM_STATE(m_itemsAdded) ) return;
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug(wxT("PrepareAfterItemsAdded( in thread 0x%lX )"),
|
|
(unsigned long)wxThread::GetCurrentId());
|
|
#endif
|
|
|
|
FROM_STATE(m_itemsAdded) = 0;
|
|
|
|
if ( m_windowStyle & wxPG_AUTO_SORT )
|
|
{
|
|
Sort ();
|
|
}
|
|
else
|
|
{
|
|
if ( m_bottomy < 1 )
|
|
CalculateYs( NULL, -1 );
|
|
else
|
|
{
|
|
RecalculateVirtualSize();
|
|
// Update visibles array (maybe not necessary here, but just in case)
|
|
CalculateVisibles ( -1, true );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid property value setting and getting
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPGGetFailed( const wxPGProperty* p, const wxChar* typestr )
|
|
{
|
|
wxPGTypeOperationFailed(p,typestr,wxT("Get"));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPGTypeOperationFailed( const wxPGProperty* p, const wxChar* typestr,
|
|
const wxChar* op )
|
|
{
|
|
wxASSERT( p != NULL );
|
|
wxLogError( _("Type operation \"%s\" failed: Property labeled \"%s\" is of type \"%s\", NOT \"%s\"."),
|
|
op,p->GetLabel().c_str(),wxPG_TO_WXCHAR_PTR(p->GetValueTypePtr()->GetCustomTypeName()),typestr );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetPropertyValue( wxPGId id, const wxPGValueType* typeclass, const wxPGVariant& value )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
if ( p && m_pState->SetPropertyValue(p,typeclass,value) )
|
|
DrawItemAndValueRelated( p );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetPropertyValue( wxPGId id, const wxChar* typestring, const wxPGVariant& value )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
if ( p && m_pState->SetPropertyValue(p,typestring,value) )
|
|
DrawItemAndValueRelated( p );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetPropertyValueString( wxPGId id, const wxString& value )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
if ( m_pState->SetPropertyValueString(p,value) )
|
|
DrawItemAndValueRelated( p );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetPropertyValueWxObjectPtr( wxPGId id, wxObject* value )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
if ( m_pState->SetPropertyValueWxObjectPtr(p,value) )
|
|
DrawItemAndValueRelated( p );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
#ifndef __WXPYTHON__
|
|
void wxPropertyGrid::SetPropertyValue( wxPGId id, wxVariant& value )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
if ( m_pState->SetPropertyValue(p,value) )
|
|
DrawItemAndValueRelated( p );
|
|
}
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::ClearPropertyValue( wxPGId id )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
|
|
|
|
if ( m_pState->ClearPropertyValue(p) )
|
|
{
|
|
DrawItemAndChildren( p );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetPropertyUnspecified( wxPGId id )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
m_pState->SetPropertyUnspecified(p);
|
|
DrawItemAndChildren(p);
|
|
|
|
wxPGPropertyWithChildren* parent = p->GetParent();
|
|
while ( parent->GetParentingType() <= PT_FIXEDCHILDREN )
|
|
{
|
|
DrawItem(parent);
|
|
parent = parent->GetParent();
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid miscellaneous GetPropertyXXX methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyCategoryClass* wxPropertyGrid::_GetPropertyCategory( wxPGProperty* p )
|
|
{
|
|
wxPGProperty* parent = (wxPGPropertyWithChildren*)p;
|
|
wxPGProperty* grandparent = (wxPGProperty*)parent->GetParent();
|
|
do
|
|
{
|
|
parent = grandparent;
|
|
grandparent = (wxPGProperty*)parent->GetParent();
|
|
if ( parent->GetParentingType() >= PT_CAPTION && grandparent )
|
|
return (wxPropertyCategoryClass*)parent;
|
|
} while ( grandparent );
|
|
return (wxPropertyCategoryClass*) NULL;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid property operations
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::EnableProperty( wxPGId id, bool enable )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
|
|
|
|
if ( enable )
|
|
{
|
|
if ( !(p->m_flags & wxPG_PROP_DISABLED) )
|
|
return false;
|
|
|
|
// If active, Set active Editor.
|
|
if ( p == m_selected )
|
|
DoSelectProperty( p, wxPG_SEL_FORCE );
|
|
}
|
|
else
|
|
{
|
|
if ( p->m_flags & wxPG_PROP_DISABLED )
|
|
return false;
|
|
|
|
// If active, Disable as active Editor.
|
|
if ( p == m_selected )
|
|
DoSelectProperty( p, wxPG_SEL_FORCE );
|
|
}
|
|
|
|
m_pState->EnableProperty(p,enable);
|
|
|
|
DrawItemAndChildren( p );
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::LimitPropertyEditing( wxPGId id, bool limit )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
m_pState->LimitPropertyEditing(p,limit);
|
|
if ( p == m_selected )
|
|
DoSelectProperty( p, wxPG_SEL_FORCE );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::_SetPropertyLabel( wxPGProperty* p, const wxString& newproplabel )
|
|
{
|
|
wxCHECK_RET( p, wxT("invalid property id") );
|
|
|
|
p->m_label = newproplabel;
|
|
if ( m_windowStyle & wxPG_AUTO_SORT )
|
|
{
|
|
Sort(p->GetParent());
|
|
Refresh();
|
|
}
|
|
else
|
|
DrawItem ( p );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::DoSetPropertyName( wxPGProperty* p, const wxString& newname )
|
|
{
|
|
wxCHECK_RET( p, wxT("invalid property id") );
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug( wxT("wxPropertyGrid::SetPropertyName( %s -> %s )"),
|
|
p->GetName().c_str(), newname.c_str() );
|
|
#endif
|
|
|
|
if ( p->GetName().Len() ) FROM_STATE(m_dictName).erase ( wxPGNameConv(p->GetName()) );
|
|
if ( newname.Len() ) FROM_STATE(m_dictName)[newname] = (void*) p;
|
|
|
|
p->DoSetName(newname);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::EnsureVisible( wxPGId id )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
|
|
|
|
Update();
|
|
|
|
bool changed = false;
|
|
|
|
// Is it inside collapsed section?
|
|
if ( p->m_y < 0 )
|
|
{
|
|
// expand parents
|
|
wxPGProperty* parent = p->GetParent();
|
|
wxPGProperty* grandparent = parent->GetParent();
|
|
|
|
if ( grandparent && grandparent != FROM_STATE(m_properties) )
|
|
Expand ( grandparent );
|
|
|
|
Expand ( parent );
|
|
changed = true;
|
|
}
|
|
|
|
// Need to scroll?
|
|
int vx, vy;
|
|
GetViewStart(&vx,&vy);
|
|
vy*=wxPG_PIXELS_PER_UNIT;
|
|
|
|
if ( p->m_y < vy )
|
|
{
|
|
Scroll (vx, p->m_y/wxPG_PIXELS_PER_UNIT );
|
|
m_iFlags |= wxPG_FL_SCROLLED;
|
|
changed = true;
|
|
}
|
|
else if ( (p->m_y+m_lineHeight) > (vy+m_height) )
|
|
{
|
|
Scroll (vx, (p->m_y-m_height+(m_lineHeight*2))/wxPG_PIXELS_PER_UNIT );
|
|
m_iFlags |= wxPG_FL_SCROLLED;
|
|
changed = true;
|
|
}
|
|
|
|
if ( changed )
|
|
DrawItems ( p, p );
|
|
|
|
return changed;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid helper methods called by properties
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Fixes position of wxTextCtrl-like control (wxSpinCtrl usually
|
|
// fits into that category as well).
|
|
void wxPropertyGrid::FixPosForTextCtrl( wxWindow* ctrl )
|
|
{
|
|
// Center the control vertically
|
|
wxRect finalPos = ctrl->GetRect();
|
|
int y_adj = (m_lineHeight - finalPos.height)/2 + wxPG_TEXTCTRLYADJUST;
|
|
|
|
// Prevent over-sized control
|
|
int sz_dec = (y_adj + finalPos.height) - m_lineHeight;
|
|
if ( sz_dec < 0 ) sz_dec = 0;
|
|
|
|
finalPos.y += y_adj;
|
|
finalPos.height -= (y_adj+sz_dec);
|
|
|
|
// STUPID HACK: wxTextCtrl has different indentation with different
|
|
// fonts, so this is to solve most common case (ie. using MS Shell Dlg 2
|
|
// or Tahoma - which are usually the same).
|
|
/*#ifdef __WXMSW__
|
|
wxString faceName = m_font.GetFaceName();
|
|
int textCtrlXAdjust = wxPG_TEXTCTRLXADJUST;
|
|
if ( (faceName == wxT("MS Shell Dlg 2") ||
|
|
faceName == wxT("Tahoma")) &&
|
|
m_font.GetWeight() != wxFONTWEIGHT_BOLD )
|
|
textCtrlXAdjust = 0;
|
|
#else*/
|
|
const int textCtrlXAdjust = wxPG_TEXTCTRLXADJUST;
|
|
//#endif
|
|
|
|
finalPos.x += textCtrlXAdjust;
|
|
finalPos.width -= textCtrlXAdjust;
|
|
|
|
ctrl->SetSize(finalPos);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Control font changer helper.
|
|
void wxPropertyGrid::SetCurControlBoldFont()
|
|
{
|
|
wxASSERT( m_wndPrimary );
|
|
m_wndPrimary->SetFont( m_captionFont );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxWindow* wxPropertyGrid::GenerateEditorTextCtrl( const wxPoint& pos,
|
|
const wxSize& sz,
|
|
const wxString& value,
|
|
wxWindow* secondary,
|
|
int extraStyle,
|
|
int maxLen )
|
|
{
|
|
int tcFlags = wxTE_PROCESS_ENTER | extraStyle;
|
|
|
|
if ( m_selected->HasFlag(wxPG_PROP_READONLY) )
|
|
tcFlags |= wxTE_READONLY;
|
|
|
|
wxPoint p(pos.x,pos.y);
|
|
wxSize s(sz.x,sz.y);
|
|
|
|
// Need to reduce width of text control on Mac
|
|
#if defined(__WXMAC__)
|
|
s.x -= 8;
|
|
#endif
|
|
|
|
// Take button into acccount
|
|
if ( secondary )
|
|
{
|
|
s.x -= (secondary->GetSize().x + wxPG_TEXTCTRL_AND_BUTTON_SPACING);
|
|
m_iFlags &= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE);
|
|
}
|
|
|
|
// If the height is significantly higher, then use border, and fill the rect exactly.
|
|
bool hasSpecialSize = false;
|
|
|
|
if ( (sz.y - m_lineHeight) > 5 )
|
|
hasSpecialSize = true;
|
|
|
|
#if wxPG_NAT_TEXTCTRL_BORDER_ANY
|
|
|
|
// Create clipper window
|
|
wxPGClipperWindow* wnd = new wxPGClipperWindow();
|
|
#if defined(__WXMSW__)
|
|
wnd->Hide();
|
|
#endif
|
|
wnd->Create(this,wxPG_SUBID1,p,s);
|
|
|
|
// This generates rect of the control inside the clipper window
|
|
if ( !hasSpecialSize )
|
|
wnd->GetControlRect(wxPG_NAT_TEXTCTRL_BORDER_X, wxPG_NAT_TEXTCTRL_BORDER_Y, p, s);
|
|
else
|
|
wnd->GetControlRect(0, 0, p, s);
|
|
|
|
wxWindow* ctrlParent = wnd;
|
|
|
|
#else
|
|
|
|
wxWindow* ctrlParent = this;
|
|
|
|
if ( !hasSpecialSize )
|
|
tcFlags |= wxNO_BORDER;
|
|
|
|
#endif
|
|
|
|
wxTextCtrl* tc = new wxTextCtrl();
|
|
|
|
#if defined(__WXMSW__) && !wxPG_NAT_TEXTCTRL_BORDER_ANY
|
|
tc->Hide();
|
|
#endif
|
|
tc->Create(ctrlParent,wxPG_SUBID1,value, p, s,tcFlags);
|
|
|
|
#if wxPG_NAT_TEXTCTRL_BORDER_ANY
|
|
wxWindow* ed = wnd;
|
|
wnd->SetControl(tc);
|
|
#else
|
|
wxWindow* ed = tc;
|
|
#endif
|
|
|
|
// Center the control vertically
|
|
if ( !hasSpecialSize )
|
|
FixPosForTextCtrl(ed);
|
|
|
|
#ifdef __WXMSW__
|
|
ed->Show();
|
|
if ( secondary )
|
|
secondary->Show();
|
|
#endif
|
|
|
|
// Set maximum length
|
|
if ( maxLen > 0 )
|
|
tc->SetMaxLength( maxLen );
|
|
|
|
return (wxWindow*) ed;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxWindow* wxPropertyGrid::GenerateEditorButton( const wxPoint& pos, const wxSize& sz )
|
|
{
|
|
#ifdef __WXMAC__
|
|
// Decorations are chunky on Mac, and we can't make the button square, so
|
|
// do things a bit differently on this platform.
|
|
|
|
wxPoint p(pos.x+sz.x,
|
|
pos.y+wxPG_BUTTON_SIZEDEC-wxPG_NAT_BUTTON_BORDER_Y);
|
|
wxSize s(25, -1);
|
|
|
|
wxButton* but = new wxButton();
|
|
but->Create(this,wxPG_SUBID2,wxT("..."),p,s,wxWANTS_CHARS);
|
|
|
|
// Now that we know the size, move to the correct position
|
|
p.x = pos.x + sz.x - but->GetSize().x - 2;
|
|
but->Move(p);
|
|
|
|
#else
|
|
wxSize s(sz.y-(wxPG_BUTTON_SIZEDEC*2)+(wxPG_NAT_BUTTON_BORDER_Y*2),
|
|
sz.y-(wxPG_BUTTON_SIZEDEC*2)+(wxPG_NAT_BUTTON_BORDER_Y*2));
|
|
|
|
// Reduce button width to lineheight
|
|
if ( s.x > m_lineHeight )
|
|
s.x = m_lineHeight;
|
|
|
|
wxPoint p(pos.x+sz.x-s.x,
|
|
pos.y+wxPG_BUTTON_SIZEDEC-wxPG_NAT_BUTTON_BORDER_Y);
|
|
|
|
wxButton* but = new wxButton();
|
|
#ifdef __WXMSW__
|
|
but->Hide();
|
|
#endif
|
|
but->Create(this,wxPG_SUBID2,wxT("..."),p,s,wxWANTS_CHARS);
|
|
|
|
but->SetFont( m_captionFont );
|
|
#endif
|
|
|
|
if ( m_selected->HasFlag(wxPG_PROP_READONLY) )
|
|
but->Disable();
|
|
|
|
return but;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxWindow* wxPropertyGrid::GenerateEditorTextCtrlAndButton( const wxPoint& pos,
|
|
const wxSize& sz,
|
|
wxWindow** psecondary,
|
|
int limitedEditing,
|
|
wxPGProperty* property )
|
|
{
|
|
wxButton* but = (wxButton*)GenerateEditorButton(pos,sz);
|
|
*psecondary = (wxWindow*)but;
|
|
|
|
if ( limitedEditing )
|
|
{
|
|
#ifdef __WXMSW__
|
|
// There is button Show in GenerateEditorTextCtrl as well
|
|
but->Show();
|
|
#endif
|
|
return (wxWindow*) NULL;
|
|
}
|
|
|
|
wxString text;
|
|
|
|
if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
|
|
text = property->GetValueAsString(property->HasFlag(wxPG_PROP_READONLY)?0:wxPG_EDITABLE_VALUE);
|
|
|
|
return GenerateEditorTextCtrl(pos,sz,text,but,property->m_maxLen);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPoint wxPropertyGrid::GetGoodEditorDialogPosition( wxPGProperty* p,
|
|
const wxSize& sz )
|
|
{
|
|
#if wxPG_SMALL_SCREEN
|
|
// On small-screen devices, always show dialogs with default position and size.
|
|
return wxDefaultPosition;
|
|
#else
|
|
int x = m_splitterx;
|
|
int y = p->m_y;
|
|
|
|
wxCHECK_MSG( y >= 0, wxPoint(-1,-1), wxT("invalid y?") );
|
|
wxCHECK_MSG( y < (int)m_bottomy, wxPoint(-1,-1), wxT("invalid y?") );
|
|
|
|
ImprovedClientToScreen( &x, &y );
|
|
|
|
int sw = wxSystemSettings::GetMetric( ::wxSYS_SCREEN_X );
|
|
int sh = wxSystemSettings::GetMetric( ::wxSYS_SCREEN_Y );
|
|
|
|
int new_x;
|
|
int new_y;
|
|
|
|
if ( x > (sw/2) )
|
|
// left
|
|
new_x = x + (m_width-m_splitterx) - sz.x;
|
|
else
|
|
// right
|
|
new_x = x;
|
|
|
|
if ( y > (sh/2) )
|
|
// above
|
|
new_y = y - sz.y;
|
|
else
|
|
// below
|
|
new_y = y + m_lineHeight;
|
|
|
|
return wxPoint(new_x,new_y);
|
|
#endif
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SLAlloc( unsigned int itemcount, const wxChar** items )
|
|
{
|
|
wxArrayString& sl = m_sl;
|
|
unsigned int i;
|
|
unsigned int sl_oldcount = sl.GetCount();
|
|
if ( sl_oldcount > itemcount ) sl_oldcount = itemcount;
|
|
#if wxUSE_INTL
|
|
if ( !wxPGGlobalVars->m_autoGetTranslation )
|
|
{
|
|
#endif
|
|
for ( i=0; i<sl_oldcount; i++ )
|
|
sl.Item(i) = items[i];
|
|
for ( i=sl_oldcount; i<itemcount; i++ )
|
|
sl.Add ( items[i] );
|
|
#if wxUSE_INTL
|
|
}
|
|
else
|
|
{
|
|
for ( i=0; i<sl_oldcount; i++ )
|
|
sl.Item(i) = ::wxGetTranslation ( items[i] );
|
|
for ( i=sl_oldcount; i<itemcount; i++ )
|
|
sl.Add ( ::wxGetTranslation ( items[i] ) );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& src_str )
|
|
{
|
|
if ( src_str.length() == 0 )
|
|
{
|
|
dst_str = src_str;
|
|
return src_str;
|
|
}
|
|
|
|
bool prev_is_slash = false;
|
|
|
|
wxString::const_iterator i = src_str.begin();
|
|
|
|
dst_str.clear();
|
|
|
|
for ( ; i != src_str.end(); i++ )
|
|
{
|
|
wxUniChar a = wxPGGetIterChar(src_str, i);
|
|
|
|
if ( a != wxT('\\') )
|
|
{
|
|
if ( !prev_is_slash )
|
|
{
|
|
dst_str << a;
|
|
}
|
|
else
|
|
{
|
|
if ( a == wxT('n') )
|
|
{
|
|
#ifdef __WXMSW__
|
|
dst_str << wxT('\n');
|
|
//dst_str << wxT('\10');
|
|
#else
|
|
dst_str << wxT('\n');
|
|
//dst_str << 10;
|
|
#endif
|
|
}
|
|
else if ( a == wxT('t') )
|
|
dst_str << wxT('\t');
|
|
else
|
|
dst_str << a;
|
|
}
|
|
prev_is_slash = false;
|
|
}
|
|
else
|
|
{
|
|
if ( prev_is_slash )
|
|
{
|
|
dst_str << wxT('\\');
|
|
prev_is_slash = false;
|
|
}
|
|
else
|
|
{
|
|
prev_is_slash = true;
|
|
}
|
|
}
|
|
}
|
|
return dst_str;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxString& wxPropertyGrid::CreateEscapeSequences( wxString& dst_str, wxString& src_str )
|
|
{
|
|
if ( src_str.length() == 0 )
|
|
{
|
|
dst_str = src_str;
|
|
return src_str;
|
|
}
|
|
|
|
wxString::const_iterator i = src_str.begin();
|
|
wxChar prev_a = wxT('\0');
|
|
|
|
dst_str.clear();
|
|
|
|
for ( ; i != src_str.end(); i++ )
|
|
{
|
|
wxChar a = wxPGGetIterChar(src_str, i);
|
|
|
|
if ( a >= wxT(' ')
|
|
#if !wxUSE_UNICODE
|
|
|| a < 0
|
|
#endif
|
|
)
|
|
{
|
|
// This surely is not something that requires an escape sequence.
|
|
dst_str << a;
|
|
}
|
|
else
|
|
{
|
|
// This might need...
|
|
if ( a == wxT('\r') )
|
|
{
|
|
// DOS style line end.
|
|
// Already taken care below
|
|
//dst_str = wxT("\\n");
|
|
//src++;
|
|
}
|
|
else if ( a == wxT('\n') )
|
|
// UNIX style line end.
|
|
dst_str << wxT("\\n");
|
|
else if ( a == wxT('\t') )
|
|
// Tab.
|
|
dst_str << wxT('\t');
|
|
else
|
|
{
|
|
//wxLogDebug(wxT("WARNING: Could not create escape sequence for character #%i"),(int)a);
|
|
dst_str << a;
|
|
}
|
|
}
|
|
|
|
prev_a = a;
|
|
}
|
|
return dst_str;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Item iteration macros
|
|
// -----------------------------------------------------------------------
|
|
|
|
#define II_INVALID_I 0x00FFFFFF
|
|
|
|
#define ITEM_ITERATION_VARIABLES \
|
|
wxPGPropertyWithChildren* parent; \
|
|
unsigned int i; \
|
|
unsigned int iMax;
|
|
|
|
#define ITEM_ITERATION_DCAE_VARIABLES \
|
|
wxPGPropertyWithChildren* parent; \
|
|
unsigned int i; \
|
|
unsigned int iMax;
|
|
|
|
#define ITEM_ITERATION_INIT_FROM_THE_TOP \
|
|
parent = FROM_STATE(m_properties); \
|
|
i = 0;
|
|
|
|
#define ITEM_ITERATION_INIT(startparent,startindex) \
|
|
parent = startparent; \
|
|
i = (unsigned int)startindex; \
|
|
if ( parent == (wxPGPropertyWithChildren*) NULL ) \
|
|
{ \
|
|
parent = FROM_STATE(m_properties); \
|
|
i = 0; \
|
|
}
|
|
|
|
#define ITEM_ITERATION_LOOP_BEGIN \
|
|
unsigned char parent_expanded; \
|
|
do \
|
|
{ \
|
|
parent_expanded = (unsigned char)parent->m_expanded; \
|
|
if ( parent->m_parent && !parent->m_parent->m_expanded ) \
|
|
parent_expanded = 0; \
|
|
iMax = parent->GetCount(); \
|
|
while ( i < iMax ) \
|
|
{ \
|
|
wxPGProperty* p = parent->Item(i); \
|
|
int parenting = p->GetParentingType();
|
|
|
|
#define ITEM_ITERATION_LOOP_END \
|
|
if ( parenting ) \
|
|
{ \
|
|
i = 0; \
|
|
parent = (wxPGPropertyWithChildren*)p; \
|
|
if ( parent_expanded ) \
|
|
parent_expanded = (unsigned char)parent->m_expanded; \
|
|
else \
|
|
parent_expanded = 0; \
|
|
iMax = parent->GetCount(); \
|
|
} \
|
|
else \
|
|
i++; \
|
|
} \
|
|
i = parent->m_arrIndex + 1; \
|
|
parent = parent->m_parent; \
|
|
} \
|
|
while ( parent != NULL );
|
|
|
|
// DCAE = Don't care about parent_expanded (this is the least space hungry method).
|
|
#define ITEM_ITERATION_DCAE_LOOP_BEGIN \
|
|
do \
|
|
{ \
|
|
iMax = parent->GetCount(); \
|
|
while ( i < iMax ) \
|
|
{ \
|
|
wxPGProperty* p = parent->Item(i); \
|
|
int parenting = p->GetParentingType();
|
|
|
|
#define ITEM_ITERATION_DCAE_LOOP_END \
|
|
if ( parenting ) \
|
|
{ \
|
|
i = 0; \
|
|
parent = (wxPGPropertyWithChildren*)p; \
|
|
iMax = parent->GetCount(); \
|
|
} \
|
|
else \
|
|
i++; \
|
|
} \
|
|
i = parent->m_arrIndex + 1; \
|
|
parent = parent->m_parent; \
|
|
} \
|
|
while ( parent != NULL );
|
|
|
|
// DCAE_ISP = Don't care about parent_expanded, Ignore sub-properties.
|
|
// Note that this treats fixed sub-properties same as sub-properties
|
|
// of wxParentProperty. Mode conversion requires this behaviour.
|
|
#define ITEM_ITERATION_DCAE_ISP_LOOP_BEGIN \
|
|
do \
|
|
{ \
|
|
iMax = parent->GetCount(); \
|
|
while ( i < iMax ) \
|
|
{ \
|
|
wxPGProperty* p = parent->Item(i); \
|
|
int parenting = p->GetParentingType();
|
|
|
|
#define ITEM_ITERATION_DCAE_ISP_LOOP_END \
|
|
if ( parenting > 0 ) \
|
|
{ \
|
|
i = 0; \
|
|
parent = (wxPGPropertyWithChildren*)p; \
|
|
iMax = parent->GetCount(); \
|
|
} \
|
|
else \
|
|
i++; \
|
|
} \
|
|
i = parent->m_arrIndex + 1; \
|
|
parent = parent->m_parent; \
|
|
} \
|
|
while ( parent != (wxPGPropertyWithChildren*) NULL );
|
|
|
|
|
|
// VO = Visible only (including those outside the scrolled section).
|
|
#define ITEM_ITERATION_VO_LOOP_BEGIN \
|
|
if ( (parent == FROM_STATE(m_properties) || parent->m_y >= 0) && parent->m_expanded ) \
|
|
{ \
|
|
do \
|
|
{ \
|
|
iMax = parent->GetCount(); \
|
|
while ( i < iMax ) \
|
|
{ \
|
|
wxPGProperty* p = parent->Item(i); \
|
|
if ( p->m_y >= 0 ) \
|
|
{ \
|
|
int parenting = p->GetParentingType();
|
|
|
|
#define ITEM_ITERATION_VO_LOOP_END \
|
|
if ( parenting && ((wxPGPropertyWithChildren*)p)->m_expanded ) \
|
|
{ \
|
|
parent = (wxPGPropertyWithChildren*)p; \
|
|
i = 0; \
|
|
break; \
|
|
} \
|
|
} \
|
|
i++; \
|
|
} \
|
|
if ( i >= iMax ) \
|
|
{ \
|
|
i = parent->m_arrIndex + 1; \
|
|
parent = parent->m_parent; \
|
|
} \
|
|
} \
|
|
while ( parent != (wxPGPropertyWithChildren*) NULL ); \
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid visibility related methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::CalculateYs( wxPGPropertyWithChildren* startparent,
|
|
int startindex )
|
|
{
|
|
// Selection must be temporarily cleared during y-recalc
|
|
wxPGProperty* prevSelected = m_selected;
|
|
if ( prevSelected )
|
|
{
|
|
bool selRes = ClearSelection();
|
|
wxPG_CHECK_RET_DBG( selRes,
|
|
wxT("failed to deselect a property (editor probably had invalid value)") );
|
|
}
|
|
|
|
ITEM_ITERATION_VARIABLES
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug(wxT("CalculateYs(startsfrom: %s[%i] ) "),
|
|
startparent?startparent->m_label.c_str():wxT("NULL"),
|
|
startindex);
|
|
#endif
|
|
|
|
ITEM_ITERATION_INIT(startparent,startindex)
|
|
|
|
wxASSERT( !m_frozen );
|
|
|
|
int cury = 0;
|
|
int lh = m_lineHeight;
|
|
|
|
if ( startparent != NULL )
|
|
cury = parent->Item(i)->m_y;
|
|
|
|
wxASSERT_MSG( cury >= 0, wxT("CalculateYs first item was not visible!!!") );
|
|
|
|
long hide_state = m_iFlags & wxPG_FL_HIDE_STATE;
|
|
bool inside_hidden_part = false;
|
|
//parent_expanded = (unsigned char)parent->m_expanded;
|
|
wxPGPropertyWithChildren* nearest_expanded = (wxPGPropertyWithChildren*) NULL;
|
|
|
|
// Find first visible and expanded parent.
|
|
while ( !parent->IsExpanded() ||
|
|
( (parent->m_flags & wxPG_PROP_HIDEABLE) && hide_state )
|
|
)
|
|
{
|
|
parent = parent->GetParent();
|
|
i = 0;
|
|
}
|
|
|
|
wxASSERT( parent );
|
|
|
|
//parent = nearest_expanded;
|
|
|
|
do
|
|
{
|
|
iMax = parent->GetCount();
|
|
|
|
if ( !inside_hidden_part )
|
|
{
|
|
while ( i < iMax )
|
|
{
|
|
wxPGProperty* p = parent->Item(i);
|
|
|
|
int parenting = p->GetParentingType();
|
|
if ( !(p->m_flags & wxPG_PROP_HIDEABLE) || (!hide_state) )
|
|
{
|
|
// item is visible (all parents are expanded, non-hideable or not in hide state)
|
|
p->m_y = (int)cury;
|
|
cury += lh;
|
|
|
|
}
|
|
else
|
|
{
|
|
p->m_y = -1;
|
|
}
|
|
|
|
if ( parenting )
|
|
{
|
|
wxPGPropertyWithChildren* p2 = (wxPGPropertyWithChildren*)p;
|
|
|
|
if ( !p2->m_expanded ||
|
|
( (p2->m_flags & wxPG_PROP_HIDEABLE) && hide_state )
|
|
)
|
|
{
|
|
inside_hidden_part = true;
|
|
nearest_expanded = parent;
|
|
}
|
|
|
|
parent = p2;
|
|
i = 0;
|
|
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while ( i < iMax )
|
|
{
|
|
wxPGProperty* p = parent->Item(i);
|
|
int parenting = p->GetParentingType();
|
|
p->m_y = -1;
|
|
if ( parenting )
|
|
{
|
|
parent = (wxPGPropertyWithChildren*)p;
|
|
i = 0;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
if ( i >= iMax )
|
|
{
|
|
i = parent->m_arrIndex + 1;
|
|
parent = parent->m_parent;
|
|
if ( inside_hidden_part && parent == nearest_expanded )
|
|
{
|
|
inside_hidden_part = false;
|
|
}
|
|
}
|
|
}
|
|
while ( parent != (wxPGPropertyWithChildren*) NULL );
|
|
|
|
m_bottomy = cury;
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug(wxT(" \\-> m_bottomy = %i"),(int)m_bottomy);
|
|
#endif
|
|
|
|
// Forces a new DoGetBestSize() call.
|
|
wxScrolledWindow::InvalidateBestSize();
|
|
|
|
// Visibles need to be recalculated *always* after y recalculation
|
|
// (but make sure it stays here, above RecalculateVirtualSize).
|
|
CalculateVisibles( -1, true );
|
|
|
|
RecalculateVirtualSize();
|
|
|
|
// Reselect
|
|
if ( prevSelected )
|
|
DoSelectProperty( prevSelected, wxPG_SEL_NONVISIBLE );
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Call when scroll position changes. Do not pre-fill m_prevVY.
|
|
void wxPropertyGrid::CalculateVisibles( int vy, bool full_recalc )
|
|
{
|
|
if ( vy < 0 )
|
|
{
|
|
int vx;
|
|
GetViewStart(&vx,&vy);
|
|
vy *= wxPG_PIXELS_PER_UNIT;
|
|
if ( full_recalc )
|
|
m_prevVY = -1;
|
|
}
|
|
|
|
// Control not yet properly built.
|
|
if ( vy >= (int)m_bottomy )
|
|
return;
|
|
|
|
if ( m_height < 0 )
|
|
return;
|
|
|
|
// Hide popup
|
|
// FIXME: Delete after transient popup support fully added
|
|
if ( m_wndPrimary && m_wndPrimary->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
|
|
((wxPGOwnerDrawnComboBox*)m_wndPrimary)->HidePopup();
|
|
|
|
int vy2 = vy + m_height;
|
|
|
|
if ( vy2 > (int)m_bottomy )
|
|
vy2 = m_bottomy;
|
|
|
|
unsigned int arr_index = 0;
|
|
unsigned int vis_height = vy2-vy;
|
|
unsigned int new_item_count = vis_height/m_lineHeight;
|
|
if ( vis_height % m_lineHeight )
|
|
new_item_count++;
|
|
|
|
wxPGArrayProperty& arr = m_arrVisible;
|
|
|
|
arr.SetCount ( new_item_count );
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug( wxT("wxPropertyGrid::CalculateVisibles ( vy=%i, vy2=%i, m_height=%i, newitemcount=%i, lineheight=%i )"),
|
|
(int)vy, (int)vy2, (int)m_height, (int)new_item_count, (int)m_lineHeight );
|
|
#endif
|
|
|
|
//wxASSERT( vy != m_prevVY );
|
|
wxASSERT( vy >= 0 );
|
|
|
|
if ( !new_item_count )
|
|
{
|
|
arr.Empty();
|
|
return;
|
|
}
|
|
|
|
ITEM_ITERATION_VARIABLES
|
|
|
|
wxPGProperty* base = NULL;
|
|
|
|
// Will simpler operation be enough?
|
|
if ( m_prevVY >= 0 )
|
|
{
|
|
if ( m_calcVisHeight == m_height )
|
|
{
|
|
if ( m_iFlags & wxPG_FL_SCROLLED )
|
|
{
|
|
int diff = vy - m_prevVY;
|
|
if ( diff == m_lineHeight )
|
|
{
|
|
// Scrolled one down
|
|
base = DoGetItemAtY_Full( vy2 - 1 );
|
|
wxASSERT( base );
|
|
arr_index = new_item_count - 1;
|
|
for ( i=0; i<arr_index; i++ )
|
|
arr.Item(i) = arr.Item(i+1);
|
|
arr.Item(arr_index) = base;
|
|
base = (wxPGProperty*) NULL;
|
|
}
|
|
else if ( diff == -m_lineHeight )
|
|
{
|
|
// Scrolled one up
|
|
base = DoGetItemAtY_Full( vy );
|
|
wxASSERT( base );
|
|
vy2 = vy + m_lineHeight; // update visibility
|
|
for ( i=(new_item_count-1); i>arr_index; i-- )
|
|
arr.Item(i) = arr.Item(i-1);
|
|
arr.Item(arr_index) = base;
|
|
base = (wxPGProperty*) NULL;
|
|
}
|
|
else
|
|
base = DoGetItemAtY_Full( vy );
|
|
}
|
|
else
|
|
base = DoGetItemAtY_Full( vy );
|
|
}
|
|
else
|
|
if ( m_prevVY == vy && !(m_iFlags & wxPG_FL_SCROLLED) )
|
|
{
|
|
if ( m_height > m_calcVisHeight )
|
|
{
|
|
// Increased height - add missing items
|
|
arr_index = (m_calcVisHeight-1)/m_lineHeight;
|
|
if ( arr_index >= new_item_count )
|
|
{
|
|
// Now, were probably below last item here
|
|
//if ( (vy+m_calcVisHeight) >= (int)m_bottomy )
|
|
base = NULL;
|
|
/*else
|
|
arr_index = arr.GetCount()-1;*/
|
|
}
|
|
else
|
|
{
|
|
base = (wxPGProperty*) arr.Item( arr_index );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Decreased height - do nothing
|
|
//base = NULL;
|
|
}
|
|
}
|
|
else
|
|
base = DoGetItemAtY_Full( vy );
|
|
}
|
|
else
|
|
{
|
|
base = DoGetItemAtY_Full( vy );
|
|
}
|
|
|
|
if ( base )
|
|
{
|
|
ITEM_ITERATION_INIT(base->m_parent,base->m_arrIndex)
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug( wxT(" Starting at index %i"), (int)arr_index );
|
|
#endif
|
|
|
|
ITEM_ITERATION_VO_LOOP_BEGIN
|
|
|
|
//wxASSERT( p->m_y >= 0 );
|
|
|
|
// update visibility limit reached?
|
|
if ( p->m_y >= vy2 ) { parent = NULL; break; }
|
|
|
|
#ifdef __WXDEBUG__
|
|
if ( arr_index >= arr.GetCount() )
|
|
{
|
|
wxLogDebug(wxT(" wxPropertyGrid::CalculateVisibles Loop overflow (index=%i,vy+vis_height=%i,p->m_y=%i)"),
|
|
(int)arr_index,(int)(vy+vis_height),(int)p->m_y);
|
|
}
|
|
#endif
|
|
|
|
arr.Item(arr_index) = (void*)p;
|
|
arr_index++;
|
|
|
|
ITEM_ITERATION_VO_LOOP_END
|
|
}
|
|
|
|
// Adjust controls
|
|
/*if ( m_selected )
|
|
{
|
|
int adjust = prevVY - vy;
|
|
if ( adjust )
|
|
{
|
|
wxPoint cp(0,adjust);
|
|
|
|
if ( m_wndPrimary )
|
|
m_wndPrimary->Move ( m_wndPrimary->GetPosition() + cp );
|
|
|
|
if ( m_wndSecondary )
|
|
m_wndSecondary->Move ( m_wndSecondary->GetPosition() + cp );
|
|
}
|
|
}*/
|
|
|
|
m_iFlags &= ~(wxPG_FL_SCROLLED);
|
|
|
|
m_prevVY = vy;
|
|
|
|
m_calcVisHeight = m_height;
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// This version uses the visible item cache.
|
|
wxPGProperty* wxPropertyGrid::DoGetItemAtY( int y )
|
|
{
|
|
|
|
//wxASSERT( m_prevVY >= 0 );
|
|
|
|
// Outside(check 1)?
|
|
if ( y >= (int)m_bottomy || y < 0 )
|
|
{
|
|
/*
|
|
#if __PAINT_DEBUGGING__
|
|
wxLogDebug(wxT("WARNING: DoGetItemAtY(a): y = %i"),y);
|
|
#endif
|
|
*/
|
|
return (wxPGProperty*) NULL;
|
|
}
|
|
|
|
int vx, vy;
|
|
GetViewStart(&vx,&vy);
|
|
vy*=wxPG_PIXELS_PER_UNIT;
|
|
|
|
// Need to recalculate visibility cache
|
|
// Note: need to check for y < m_prevVY is a hack.
|
|
if ( m_prevVY != vy ||y < m_prevVY ) //m_iFlags & wxPG_FL_SCROLLED ||
|
|
CalculateVisibles( vy, true );
|
|
|
|
// Outside(check 2)?
|
|
if ( y >= (vy+m_height) )
|
|
{
|
|
/*
|
|
#if __PAINT_DEBUGGING__
|
|
wxLogDebug(wxT("WARNING: DoGetItemAtY(b): y = %i"),y);
|
|
#endif
|
|
*/
|
|
return (wxPGProperty*) NULL;
|
|
}
|
|
|
|
unsigned int index = (unsigned int)((y - vy) / m_lineHeight);
|
|
|
|
// Consistency checks
|
|
if ( !m_arrVisible.GetCount() )
|
|
return (wxPGProperty*) NULL;
|
|
|
|
if ( index >= m_arrVisible.GetCount() )
|
|
{
|
|
#ifdef __WXDEBUG__
|
|
wxLogDebug(wxT(" index = %i"),(int)index);
|
|
wxLogDebug(wxT(" (height/lineheight+1) = %i"),(int)((m_height/m_lineHeight)+1));
|
|
wxLogDebug(wxT(" m_arrVisible.GetCount() = %i"),(int)m_arrVisible.GetCount());
|
|
|
|
// This was wxCHECK_MSG, but I don't want it to show, since it can happen from
|
|
// time to time, and I probably won't fix in the current version of wxPropertyGrid.
|
|
wxLogDebug( wxT("Not enough entries in m_arrVisible (y was < m_bottomy).") );
|
|
#endif
|
|
|
|
return (wxPGProperty*) NULL;
|
|
}
|
|
|
|
if ( index >= m_arrVisible.GetCount() )
|
|
{
|
|
index = m_arrVisible.GetCount()-1;
|
|
}
|
|
|
|
return (wxPGProperty*)m_arrVisible.Item(index);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid graphics related methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
|
|
{
|
|
|
|
wxPG_PAINT_DC_INIT()
|
|
|
|
// Don't paint after destruction has begun
|
|
if ( !(m_iFlags & wxPG_FL_INITIALIZED) )
|
|
return;
|
|
|
|
#if __PAINT_DEBUGGING__
|
|
wxLogDebug( wxT("wxPropertyGrid::OnPaint()") );
|
|
#endif
|
|
|
|
// Find out where the window is scrolled to
|
|
int vx,vy; // Top left corner of client
|
|
GetViewStart(&vx,&vy);
|
|
vy *= wxPG_PIXELS_PER_UNIT;
|
|
|
|
// Update everything inside the box
|
|
wxRect r = GetUpdateRegion().GetBox();
|
|
|
|
r.y += vy;
|
|
|
|
// Repaint this rectangle
|
|
//if ( r.height > 0 )
|
|
DrawItems ( dc, r.y, r.y + r.height,
|
|
#if wxPG_ALLOW_CLIPPING
|
|
NULL //&r
|
|
#else
|
|
NULL
|
|
#endif
|
|
);
|
|
|
|
// We assume that the size set when grid is shown
|
|
// is what is desired.
|
|
m_iFlags |= wxPG_FL_GOOD_SIZE_SET;
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
//
|
|
// This is the one called by OnPaint event handler and others.
|
|
// topy and bottomy are already unscrolled
|
|
// Clears any area in coordinates that doesn't have items.
|
|
//
|
|
void wxPropertyGrid::DrawItems( wxDC& dc,
|
|
unsigned int topy,
|
|
unsigned int bottomy,
|
|
const wxRect* clipRect )
|
|
{
|
|
if ( m_frozen || m_height < 1 || bottomy < topy || !m_pState ) return;
|
|
|
|
#if __PAINT_DEBUGGING__
|
|
wxLogDebug(wxT("wxPropertyGrid::DrawItems ( %i -> %i, clipRect = 0x%X )"),topy,bottomy,
|
|
(unsigned int)clipRect);
|
|
#endif
|
|
|
|
// items added check
|
|
if ( FROM_STATE(m_itemsAdded) ) PrepareAfterItemsAdded();
|
|
|
|
unsigned int vx, vy; // Top left corner of client
|
|
GetViewStart((int*)&vx,(int*)&vy);
|
|
vy *= wxPG_PIXELS_PER_UNIT;
|
|
|
|
unsigned int client_bottom = (unsigned int)m_height + vy;
|
|
|
|
// Clip topy and bottomy
|
|
if ( bottomy > client_bottom )
|
|
bottomy = client_bottom;
|
|
if ( topy < vy )
|
|
topy = vy;
|
|
|
|
#if __PAINT_DEBUGGING__
|
|
wxLogDebug(wxT(" \\--> ( final area %i -> %i )"),topy,bottomy);
|
|
#endif
|
|
|
|
//
|
|
// Determine first and last item to draw
|
|
// (don't draw if already over the top)
|
|
//
|
|
|
|
if ( topy < client_bottom && topy < m_bottomy && FROM_STATE(m_properties)->GetCount() > 0 )
|
|
{
|
|
|
|
wxPGProperty* firstItem = DoGetItemAtY(topy);
|
|
|
|
if ( firstItem == (wxPGProperty*) NULL )
|
|
{
|
|
#ifdef __WXDEBUG__
|
|
wxString msg;
|
|
msg.Printf(wxT("WARNING: wxPropertyGrid::DrawItems(): firstItem == NULL!"));
|
|
wxMessageBox(msg);
|
|
wxLogDebug(msg);
|
|
wxLogDebug(wxT(" More info: y: %i -> %i visible_window: %i -> %i"),
|
|
(int)topy,(int)bottomy,(int)vy,(int)client_bottom);
|
|
// This is here for debugging purposes.
|
|
DoGetItemAtY(topy);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
wxPGProperty* lastItem = (wxPGProperty*) NULL;
|
|
|
|
// lastItem may be NULL on call to DoDrawItems
|
|
// in this case lastItem will truly become the last item
|
|
|
|
if ( bottomy > topy && bottomy < m_bottomy )
|
|
{
|
|
lastItem = DoGetItemAtY(bottomy-1);
|
|
#if __PAINT_DEBUGGING__
|
|
wxLogDebug( wxT(" \\--> WARNING: lastItem acquisition failed (should not)!"));
|
|
#endif
|
|
}
|
|
|
|
DoDrawItems( dc, firstItem, lastItem, clipRect );
|
|
}
|
|
|
|
// Clear area beyond m_bottomy?
|
|
if ( bottomy > m_bottomy )
|
|
{
|
|
wxColour& bgc = wxPG_SLACK_BACKROUND;
|
|
//wxColour& bgc = wxColour(255,0,255);
|
|
dc.SetPen ( wxPen(bgc) );
|
|
dc.SetBrush ( wxBrush(bgc) );
|
|
unsigned int clear_top = m_bottomy;
|
|
if ( topy > clear_top ) clear_top = topy;
|
|
dc.DrawRectangle ( 0, clear_top, m_width, m_height-(clear_top-vy) );
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
#define DECLARE_ITEM_ITERATION_UVC_VARIABLES \
|
|
unsigned int ind; \
|
|
wxPGProperty* p;
|
|
|
|
// UVC = Use Visibility Cache
|
|
// VISTART = index to first item from visibility cache to use.
|
|
// BOTTOMY = Logical y coordinate of last item to draw.
|
|
#define ITEM_ITERATION_UVC_LOOP_BEGIN(VISTART,BOTTOMY) \
|
|
ind = VISTART; \
|
|
do \
|
|
{ \
|
|
p = (wxPGProperty*)m_arrVisible.Item(ind); \
|
|
ind++; \
|
|
int parenting = p->GetParentingType();
|
|
|
|
#define ITEM_ITERATION_UVC_LOOP_END(BOTTOMY) \
|
|
} while ( p->m_y < BOTTOMY ); \
|
|
|
|
|
|
void wxPropertyGrid::DoDrawItems( wxDC& dcMain,
|
|
const wxPGProperty* firstItem,
|
|
const wxPGProperty* lastItem,
|
|
const wxRect* clipRect )
|
|
{
|
|
if ( m_frozen || m_height < 1 )
|
|
return;
|
|
|
|
//wxCHECK_RET( !FROM_STATE(m_itemsAdded), wxT("m_itemsAdded must be zero at this point") );
|
|
|
|
// items added check
|
|
if ( FROM_STATE(m_itemsAdded) ) PrepareAfterItemsAdded();
|
|
|
|
wxCHECK_RET( firstItem != NULL, wxT("invalid first item") );
|
|
wxASSERT( FROM_STATE(m_properties->GetCount()) );
|
|
|
|
// Make sure visibility cache is up-to-date
|
|
int vy;
|
|
int vx;
|
|
GetViewStart(&vx,&vy);
|
|
vy*=wxPG_PIXELS_PER_UNIT;
|
|
if ( vy != m_prevVY )
|
|
CalculateVisibles(vy,true);
|
|
|
|
if ( vy != m_prevVY )
|
|
return;
|
|
|
|
// Determine last item, if not given (but requires clipRect).
|
|
if ( lastItem == NULL )
|
|
{
|
|
if ( clipRect != NULL )
|
|
{
|
|
unsigned int bottomy = clipRect->y + clipRect->height;
|
|
|
|
if ( bottomy <= (unsigned int)firstItem->m_y )
|
|
lastItem = firstItem;
|
|
}
|
|
|
|
if ( lastItem == NULL )
|
|
{
|
|
lastItem = DoGetItemAtY(vy+m_height-1);
|
|
if ( lastItem == NULL )
|
|
lastItem = GetLastItem(true);
|
|
}
|
|
}
|
|
|
|
DoDrawItems2(dcMain, firstItem, lastItem, clipRect);
|
|
}
|
|
|
|
//
|
|
// Uses three pass approach, so it is optimized for drawing
|
|
// multiple items at once.
|
|
//
|
|
// IMPORTANT NOTES:
|
|
// - Clipping rectangle must be of physical coordinates.
|
|
//
|
|
//
|
|
void wxPropertyGrid::DoDrawItems2( wxDC& dcMain,
|
|
const wxPGProperty* firstItem,
|
|
const wxPGProperty* lastItem,
|
|
const wxRect* clipRect ) const
|
|
{
|
|
int lh = m_lineHeight;
|
|
|
|
int vy;
|
|
int vx;
|
|
GetViewStart(&vx,&vy);
|
|
vy*=wxPG_PIXELS_PER_UNIT;
|
|
|
|
int firstItemTopY = firstItem->m_y;
|
|
int lastItemBottomY = lastItem->m_y+lh-1;
|
|
|
|
int yRelMod = 0;
|
|
|
|
// Entire range outside scrolled, visible area?
|
|
if ( firstItemTopY >= (vy+m_height) || lastItemBottomY <= vy )
|
|
return;
|
|
|
|
wxCHECK_RET( firstItemTopY < lastItemBottomY, wxT("invalid y values") );
|
|
|
|
wxDC* dcPtr;
|
|
|
|
#if wxPG_DOUBLE_BUFFER
|
|
wxMemoryDC* bufferDC = NULL;
|
|
const wxRect* blitClipRect = NULL;
|
|
int renderHeight = lastItem->m_y - firstItemTopY + m_lineHeight;
|
|
|
|
if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
|
|
{
|
|
if ( !m_doubleBuffer )
|
|
return;
|
|
|
|
// Must fit to double-buffer
|
|
#ifdef __WXDEBUG__
|
|
if ( (lastItemBottomY - firstItemTopY) > m_doubleBuffer->GetHeight() )
|
|
{
|
|
wxString msg;
|
|
msg.Printf( wxT("wxPropertyGrid: DOUBLE-BUFFER TOO SMALL ( drawn %i vs db height %i vs client_height %i)!"),
|
|
(int)(lastItemBottomY - firstItemTopY),
|
|
(int)(m_doubleBuffer->GetHeight()),
|
|
(int)m_height );
|
|
wxLogError(msg);
|
|
wxLogDebug(msg);
|
|
}
|
|
#endif
|
|
|
|
bufferDC = new wxMemoryDC();
|
|
bufferDC->SelectObject( *m_doubleBuffer );
|
|
dcPtr = bufferDC;
|
|
|
|
blitClipRect = clipRect;
|
|
|
|
//if ( m_iFlags & wxPG_FL_CHANGED ||
|
|
// !(m_iFlags & wxPG_FL_HANDLING_PAINT_EVENT) )
|
|
//{
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
dcPtr = &dcMain;
|
|
}
|
|
|
|
wxDC& dc = *dcPtr;
|
|
|
|
#if __PAINT_DEBUGGING__
|
|
wxLogDebug(wxT(" -> DoDrawItems ( \"%s\" -> \"%s\", height=%i (ch=%i), clipRect = 0x%lX )"),
|
|
firstItem->GetLabel().c_str(),
|
|
lastItem->GetLabel().c_str(),
|
|
(int)(lastItemBottomY - firstItemTopY),
|
|
(int)m_height,
|
|
(unsigned long)clipRect );
|
|
#endif
|
|
|
|
wxPGPaintData paintdata;
|
|
wxRect r;
|
|
|
|
DECLARE_ITEM_ITERATION_UVC_VARIABLES
|
|
|
|
// Get first and last indexes to visibility cache
|
|
unsigned int viStart = (firstItemTopY - vy) / lh;
|
|
int vi_end_y = lastItem->m_y;
|
|
|
|
if ( viStart >= m_arrVisible.GetCount() )
|
|
{
|
|
wxLogDebug(wxT("WARNING: viStart >= m_arrVisible.GetCount() ( %i >= %i )"),
|
|
(int)viStart, (int)m_arrVisible.GetCount() );
|
|
return;
|
|
}
|
|
|
|
#ifdef __WXDEBUG__
|
|
unsigned int viEnd = (lastItem->m_y - vy) / lh;
|
|
if ( viEnd >= m_arrVisible.GetCount() )
|
|
{
|
|
wxLogDebug(wxT("WARNING: viEnd >= m_arrVisible.GetCount() ( %i >= %i )"),
|
|
(int)viEnd, (int)m_arrVisible.GetCount() );
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
int x = m_marginWidth;
|
|
int y;
|
|
|
|
long window_style = m_windowStyle;
|
|
int extraStyle = GetExtraStyle();
|
|
|
|
//
|
|
// With wxPG_DOUBLE_BUFFER, do double buffering
|
|
// - buffer's y = 0, so align cliprect and coordinates to that
|
|
//
|
|
#if wxPG_DOUBLE_BUFFER
|
|
if ( bufferDC )
|
|
{
|
|
wxRect cr2;
|
|
|
|
//yRelMod = firstItemTopY;
|
|
yRelMod = vy;
|
|
|
|
//
|
|
// clipRect conversion
|
|
if ( clipRect )
|
|
{
|
|
cr2 = *clipRect;
|
|
cr2.y -= yRelMod;
|
|
clipRect = &cr2;
|
|
}
|
|
//int renderHeight = lastItem->m_y - firstItemTopY + m_lineHeight;
|
|
//lastItemBottomY -= firstItemTopY;
|
|
//firstItemTopY = 0;
|
|
firstItemTopY -= vy;
|
|
lastItemBottomY -= vy;
|
|
}
|
|
#endif
|
|
|
|
const wxFont& normalfont = m_font;
|
|
|
|
bool reallyFocused = (m_iFlags & wxPG_FL_FOCUSED) ? true : false;
|
|
|
|
bool isEnabled = IsEnabled();
|
|
|
|
//
|
|
// Prepare some pens and brushes that are often changed to.
|
|
//
|
|
|
|
wxBrush marginBrush(m_colMargin);
|
|
wxPen marginPen(m_colMargin);
|
|
wxBrush capbgbrush(m_colCapBack,wxSOLID);
|
|
wxPen linepen(m_colLine,1,wxSOLID);
|
|
|
|
// pen that has same colour as text
|
|
wxPen outlinepen(m_colPropFore,1,wxSOLID);
|
|
|
|
if ( clipRect )
|
|
dc.SetClippingRegion( *clipRect );
|
|
|
|
//
|
|
// Clear margin with background colour
|
|
//
|
|
dc.SetBrush( marginBrush );
|
|
if ( !(window_style & wxPG_HIDE_MARGIN) )
|
|
{
|
|
dc.SetPen( *wxTRANSPARENT_PEN );
|
|
dc.DrawRectangle(-1,firstItemTopY-1,m_marginWidth+2,lastItemBottomY-firstItemTopY+3);
|
|
}
|
|
|
|
/*
|
|
// This colorizer helps to debug painting.
|
|
bool small_draw = false;
|
|
if ( renderHeight < (m_height-(lh*3)) )
|
|
{
|
|
if ( firstItem == lastItem )
|
|
{
|
|
bgbrush = wxBrush(wxColour(255,128,128));
|
|
linepen = wxPen(wxColour(128,0,255));
|
|
//boxbrush = wxBrush(wxColour(192,192,192));
|
|
}
|
|
else
|
|
{
|
|
bgbrush = wxBrush(wxColour(128,255,128));
|
|
linepen = wxPen(wxColour(0,0,255));
|
|
//boxbrush = wxBrush(wxColour(230,230,230));
|
|
}
|
|
small_draw = true;
|
|
}
|
|
*/
|
|
|
|
//dc.SetPen ( *wxTRANSPARENT_PEN );
|
|
//dc.SetFont(normalfont);
|
|
|
|
wxPGProperty* selected = m_selected;
|
|
|
|
/*#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
|
|
bool selected_painted = false;
|
|
#endif*/
|
|
|
|
// NOTE: Clipping and pen/brush switching are main reasons for multi-pass approach.
|
|
|
|
//
|
|
// zero'th pass: Wireframes.
|
|
// (this could be embedded in another loop)
|
|
|
|
dc.SetBrush( marginBrush );
|
|
|
|
unsigned long cur_first_ind = viStart;
|
|
unsigned long next_cur_first_ind = 0xFFFFFFFF;
|
|
wxPGPropertyWithChildren* cur_category = (wxPGPropertyWithChildren*) NULL;
|
|
int vcache_last_item_y = vy + m_height;
|
|
if ( vcache_last_item_y > (int)m_bottomy ) vcache_last_item_y = m_bottomy;
|
|
vcache_last_item_y -= lh;
|
|
|
|
ITEM_ITERATION_UVC_LOOP_BEGIN(viStart,vi_end_y)
|
|
|
|
wxPGPropertyWithChildren* parent = p->GetParent();
|
|
|
|
// Should not happen (but this is better than a crash)...
|
|
wxCHECK_RET( parent, wxT("NULL parent") );
|
|
|
|
// Does this wireframe end?
|
|
// Conditions:
|
|
// A) This is last item within its parent, and parent is category.
|
|
// B) Next is category.
|
|
// C) This is collapsed category.
|
|
// D) This is the last item drawn.
|
|
|
|
if ( p->m_y >= vi_end_y )
|
|
{
|
|
// This is the last item.
|
|
//wxLogDebug(wxT("--> last item"));
|
|
next_cur_first_ind = ind;
|
|
}
|
|
else if ( p->m_arrIndex >= (parent->GetCount()-1) && parent->GetParentingType() >= PT_CAPTION &&
|
|
( parenting <= 0 /*|| ((wxPGPropertyWithChildren*)p)->GetCount() < 1*/ )
|
|
)
|
|
{
|
|
// This is last item within its parent, and parent is category, but this isn't
|
|
// an non-empty category.
|
|
//wxLogDebug(wxT("--> category ends"));
|
|
cur_category = (wxPropertyCategoryClass*)parent;
|
|
next_cur_first_ind = ind;
|
|
}
|
|
else if ( ((wxPGProperty*)m_arrVisible.Item(ind))->GetParentingType() >= PT_CAPTION )
|
|
{
|
|
// Next item is a category.
|
|
//wxLogDebug(wxT("--> next item is category"));
|
|
next_cur_first_ind = ind;
|
|
}
|
|
else if ( parenting > 0 &&
|
|
(!((wxPGPropertyWithChildren*)p)->IsExpanded() ||
|
|
!((wxPGPropertyWithChildren*)p)->GetCount()) )
|
|
{
|
|
// This is collapsed category.
|
|
//wxLogDebug(wxT("--> collapsed category"));
|
|
cur_category = (wxPropertyCategoryClass*)p;
|
|
next_cur_first_ind = ind;
|
|
}
|
|
|
|
// When new category begins or old ends, draw wireframe for items in-between
|
|
if ( next_cur_first_ind < 0xFFFFFF )
|
|
{
|
|
|
|
wxPGProperty* cur_first = (wxPGProperty*)m_arrVisible.Item(cur_first_ind);
|
|
wxPGPropertyWithChildren* cur_last_item = (wxPGPropertyWithChildren*)p;
|
|
|
|
if ( !cur_category )
|
|
{
|
|
if ( cur_first->GetParentingType() >= PT_CAPTION )
|
|
{
|
|
cur_category = (wxPropertyCategoryClass*)cur_first;
|
|
}
|
|
else if ( !(m_windowStyle & wxPG_HIDE_CATEGORIES) )
|
|
{
|
|
cur_category = _GetPropertyCategory(cur_first);
|
|
/*if ( !cur_category )
|
|
cur_category = (wxPropertyCategoryClass*)FROM_STATE(m_properties);*/
|
|
}
|
|
}
|
|
|
|
int draw_top = cur_first->m_y - yRelMod;
|
|
int draw_bottom = cur_last_item->m_y + lh - yRelMod;
|
|
int frame_top = draw_top;
|
|
int frame_bottom = draw_bottom;
|
|
int margin_top = draw_top;
|
|
int margin_bottom = draw_bottom;
|
|
|
|
int ly = frame_top + lh - 1;
|
|
|
|
if ( cur_first->GetParentingType() >= PT_CAPTION )
|
|
{
|
|
wxPropertyCategoryClass* pc = ((wxPropertyCategoryClass*)cur_first);
|
|
frame_top += lh;
|
|
if ( !pc->IsExpanded() )
|
|
{
|
|
// Category collapsed.
|
|
frame_top = frame_bottom + 1;
|
|
}
|
|
}
|
|
|
|
int grey_x = x;
|
|
if ( cur_category /*!(window_style & wxPG_HIDE_CATEGORIES)*/ )
|
|
grey_x += ((unsigned int)((cur_category->GetDepth()-1)*m_subgroup_extramargin));
|
|
|
|
//wxLogDebug( wxT("wireframe: %s -> %s (grey_x:%i)"), cur_first->GetLabel().c_str(),
|
|
// cur_last_item->GetLabel().c_str(),((int)grey_x-x));
|
|
|
|
dc.SetPen( *wxTRANSPARENT_PEN );
|
|
|
|
// Clear extra margin area.
|
|
dc.DrawRectangle( x-1, margin_top, grey_x - x + 1, margin_bottom-margin_top );
|
|
|
|
dc.SetPen( linepen );
|
|
|
|
if ( frame_bottom > frame_top )
|
|
{
|
|
|
|
//if ( cat_top < firstItemTopY )
|
|
// cat_top = firstItemTopY;
|
|
|
|
|
|
// Margin Edge
|
|
dc.DrawLine ( grey_x, frame_top, grey_x, frame_bottom );
|
|
|
|
// Splitter
|
|
dc.DrawLine ( m_splitterx, frame_top, m_splitterx, frame_bottom );
|
|
|
|
// Horizontal Lines
|
|
while ( ly < (frame_bottom-1) )
|
|
{
|
|
dc.DrawLine ( grey_x, ly, m_width, ly );
|
|
ly += lh;
|
|
}
|
|
}
|
|
|
|
int use_depth = grey_x; // Default is to simply tidy up this wireframe.
|
|
|
|
// Properly draw top line of next wireframe, if adjacent.
|
|
|
|
// Get next item.
|
|
wxPGProperty* next_item;
|
|
//if ( ind < m_arrVisible.GetCount() )
|
|
if ( cur_last_item->m_y < vcache_last_item_y )
|
|
{
|
|
next_item = (wxPGProperty*)m_arrVisible.Item(ind);
|
|
}
|
|
else
|
|
{
|
|
// Was not in visibility cache, so use clumsier method.
|
|
next_item = GetNeighbourItem(cur_last_item,true,1);
|
|
if (!next_item)
|
|
next_item = cur_last_item; // This will serve our purpose.
|
|
}
|
|
//wxLogDebug(wxT("next_item: %s"),next_item->GetLabel().c_str());
|
|
|
|
// Just take the depth and is-it-category out of it.
|
|
int next_parenting = next_item->GetParentingType();
|
|
int last_parenting = cur_last_item->GetParentingType();
|
|
|
|
// A) If both are categories, draw line with background colour.
|
|
// B) If only next is category, use its category's depth.
|
|
// C) If only last is category, use grey_x as depth.
|
|
// D) If neither is a category, use smaller.
|
|
if ( next_parenting > 0 )
|
|
{
|
|
// Next is category.
|
|
|
|
if ( last_parenting > 0 )
|
|
{
|
|
// Last is a category too - draw complete line with background colour.
|
|
dc.SetPen ( marginPen );
|
|
use_depth = x;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Next is not a category.
|
|
wxPropertyCategoryClass* next_cat = _GetPropertyCategory(next_item);
|
|
int depth_next = x;
|
|
if ( next_cat && /*cur_category*/ !(window_style & wxPG_HIDE_CATEGORIES) )
|
|
{
|
|
//wxLogDebug(wxT("next_item_cat: %s"),next_cat->GetLabel().c_str());
|
|
depth_next += ((unsigned int)((next_cat->GetDepth()-1)*m_subgroup_extramargin));
|
|
}
|
|
|
|
if ( last_parenting <= 0 )
|
|
{
|
|
// Last is not a category - use lesser depth
|
|
if ( depth_next < grey_x )
|
|
use_depth = depth_next;
|
|
//wxLogDebug(wxT("- neither is category"));
|
|
}
|
|
else
|
|
{
|
|
// Last is a category
|
|
use_depth = depth_next;
|
|
//wxLogDebug(wxT("last only is category"));
|
|
}
|
|
}
|
|
|
|
//wxLogDebug(wxT("last_line_use_depth: %i"),(int)use_depth);
|
|
dc.DrawLine( use_depth, ly, m_width, ly );
|
|
|
|
cur_first_ind = next_cur_first_ind;
|
|
next_cur_first_ind = 0xFFFFFFFF;
|
|
//cur_first = (wxPGPropertyWithChildren*)p;
|
|
cur_category = (wxPGPropertyWithChildren*) NULL;
|
|
}
|
|
|
|
//cur_last_item = p;
|
|
|
|
ITEM_ITERATION_UVC_LOOP_END(vi_end_y)
|
|
|
|
//
|
|
// First pass: Category background and text, Images, Label+value background.
|
|
//
|
|
|
|
//wxLogDebug(wxT(" \\--> first pass..."));
|
|
y = firstItemTopY;
|
|
|
|
dc.SetFont( m_captionFont );
|
|
dc.SetPen( *wxTRANSPARENT_PEN );
|
|
|
|
ITEM_ITERATION_UVC_LOOP_BEGIN(viStart,vi_end_y)
|
|
|
|
y += m_spacingy;
|
|
|
|
int text_x = x + ((unsigned int)((p->GetDepth()-1)*m_subgroup_extramargin));
|
|
|
|
if ( parenting > 0 )
|
|
{
|
|
|
|
dc.SetBrush( capbgbrush ); // Category label background colour.
|
|
|
|
// Category - draw background, text and possibly selection rectangle.
|
|
wxPropertyCategoryClass* pc = (wxPropertyCategoryClass*)p;
|
|
|
|
// Note how next separator line is overdrawn if next item is category .
|
|
int useLh = lh;
|
|
if ( ind < (m_arrVisible.GetCount()) &&
|
|
( ((wxPGProperty*)m_arrVisible[ind])->GetParentingType() <= 0 ) )
|
|
useLh -= 1;
|
|
|
|
if ( isEnabled && p->IsEnabled() )
|
|
dc.SetTextForeground( *(wxPGColour*)m_arrFgCols[pc->GetTextColIndex()] );
|
|
else
|
|
dc.SetTextForeground( m_colDisPropFore );
|
|
|
|
dc.DrawRectangle( text_x, y-m_spacingy, m_width-text_x, useLh );
|
|
dc.DrawText( pc->GetLabel(), text_x+wxPG_XBEFORETEXT, y );
|
|
|
|
// active caption gets nice dotted rectangle
|
|
if ( p == selected )
|
|
{
|
|
wxRect focusRect(text_x+wxPG_XBEFORETEXT-wxPG_CAPRECTXMARGIN,
|
|
y-wxPG_CAPRECTYMARGIN,
|
|
pc->GetTextExtent()+(wxPG_CAPRECTXMARGIN*2),
|
|
m_fontHeight+(wxPG_CAPRECTYMARGIN*2));
|
|
wxPGDrawFocusRect(dc,focusRect);
|
|
|
|
dc.SetPen( *wxTRANSPARENT_PEN );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
// Basic background colour.
|
|
dc.SetBrush( *(wxPGBrush*)m_arrBgBrushes[p->m_bgColIndex] );
|
|
|
|
//wxLogDebug(wxT("%s: %i"),p->m_label.c_str(),(int)p->m_depthBgCol);
|
|
|
|
int greyDepth = 0;
|
|
if ( !(window_style & wxPG_HIDE_CATEGORIES) )
|
|
greyDepth = (((int)p->m_depthBgCol)-1) * m_subgroup_extramargin;
|
|
|
|
// In two parts to retain splitter
|
|
|
|
if ( p == m_selected )
|
|
{
|
|
// Selected get different label background.
|
|
if ( reallyFocused )
|
|
dc.SetBrush( m_colSelBack );
|
|
else
|
|
dc.SetBrush( m_colLine );
|
|
|
|
dc.DrawRectangle( x+greyDepth+1, y-m_spacingy, m_splitterx-greyDepth-x-1, lh-1 );
|
|
|
|
}
|
|
else
|
|
{
|
|
dc.DrawRectangle( x+greyDepth+1, y-m_spacingy, m_splitterx-greyDepth-x-1, lh-1 );
|
|
}
|
|
|
|
dc.DrawRectangle( m_splitterx+1, y-m_spacingy, m_width-m_splitterx, lh-1 );
|
|
|
|
}
|
|
|
|
y += m_fontHeight+m_spacingy+1;
|
|
|
|
ITEM_ITERATION_UVC_LOOP_END(vi_end_y)
|
|
|
|
dc.SetFont( normalfont );
|
|
|
|
//
|
|
// Second pass: Expander Buttons, Labels.
|
|
//
|
|
// Second pass happens entirely on the left side, so sometimes
|
|
// we can just skip it.
|
|
|
|
if ( clipRect == NULL || clipRect->x < m_splitterx )
|
|
{
|
|
//wxLogDebug(wxT(" \\--> second pass..."));
|
|
|
|
y = firstItemTopY;
|
|
|
|
r = wxRect(0,y,m_splitterx,lastItemBottomY);
|
|
dc.SetClippingRegion ( r );
|
|
|
|
dc.SetFont(normalfont);
|
|
|
|
ITEM_ITERATION_UVC_LOOP_BEGIN(viStart,vi_end_y)
|
|
|
|
if ( isEnabled && (p->IsEnabled() || !(extraStyle & wxPG_EX_GREY_LABEL_WHEN_DISABLED)) )
|
|
dc.SetTextForeground( *(wxPGColour*)m_arrFgCols[p->m_fgColIndex] );
|
|
else
|
|
dc.SetTextForeground( m_colDisPropFore );
|
|
|
|
//
|
|
// Expand/collapse button image.
|
|
if ( parenting != 0 &&
|
|
!(window_style & wxPG_HIDE_MARGIN) &&
|
|
((wxPGPropertyWithChildren*)p)->GetChildCount() )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
|
|
int depth = p->m_depth - 1;
|
|
|
|
#ifdef wxPG_ICON_WIDTH
|
|
int imageX = m_gutterWidth + ( depth * m_subgroup_extramargin );
|
|
#endif
|
|
|
|
y += m_buttonSpacingY;
|
|
|
|
#if (wxPG_USE_RENDERER_NATIVE)
|
|
// Prepare rectangle to be used
|
|
r.x = imageX; r.y = y;
|
|
r.width = m_iconWidth; r.height = m_iconHeight;
|
|
#elif wxPG_ICON_WIDTH
|
|
// Drawing expand/collapse button manually
|
|
dc.SetPen(m_colPropFore);
|
|
if ( parenting > 0 )
|
|
{
|
|
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
|
}
|
|
else
|
|
{
|
|
dc.SetBrush(m_colPropBack);
|
|
}
|
|
dc.DrawRectangle( imageX, y, m_iconWidth, m_iconWidth );
|
|
int _y = y+(m_iconWidth/2);
|
|
dc.DrawLine(imageX+2,_y,imageX+m_iconWidth-2,_y);
|
|
#else
|
|
wxBitmap* bmp;
|
|
#endif
|
|
|
|
if ( pwc->m_expanded )
|
|
{
|
|
// wxRenderer functions are non-mutating in nature, so it
|
|
// should be safe to cast "const wxPropertyGrid*" to "wxWindow*".
|
|
// Hopefully this does not cause problems.
|
|
#if (wxPG_USE_RENDERER_NATIVE)
|
|
wxRendererNative::Get().DrawTreeItemButton(
|
|
(wxWindow*)this,
|
|
dc,
|
|
r,
|
|
wxCONTROL_EXPANDED
|
|
);
|
|
#elif wxPG_ICON_WIDTH
|
|
//
|
|
#else
|
|
bmp = m_collbmp;
|
|
#endif
|
|
|
|
}
|
|
else
|
|
{
|
|
#if (wxPG_USE_RENDERER_NATIVE)
|
|
wxRendererNative::Get().DrawTreeItemButton(
|
|
(wxWindow*)this,
|
|
dc,
|
|
r,
|
|
0
|
|
);
|
|
#elif wxPG_ICON_WIDTH
|
|
int _x = imageX+(m_iconWidth/2);
|
|
dc.DrawLine(_x,y+2,_x,y+m_iconWidth-2);
|
|
#else
|
|
bmp = m_expandbmp;
|
|
#endif
|
|
}
|
|
|
|
#if (wxPG_USE_RENDERER_NATIVE)
|
|
//
|
|
#elif wxPG_ICON_WIDTH
|
|
//
|
|
#else
|
|
dc.DrawBitmap( *bmp, m_gutterWidth, y, true );
|
|
#endif
|
|
|
|
y -= m_buttonSpacingY;
|
|
}
|
|
|
|
y += m_spacingy;
|
|
|
|
if ( parenting <= 0 )
|
|
{
|
|
// Non-categories.
|
|
|
|
int text_x = x;
|
|
// Use basic depth if in non-categoric mode and parent is base array.
|
|
if ( !(window_style & wxPG_HIDE_CATEGORIES) || p->GetParent() != FROM_STATE(m_properties) )
|
|
{
|
|
text_x += ((unsigned int)((p->m_depth-1)*m_subgroup_extramargin));
|
|
}
|
|
/*
|
|
else
|
|
{
|
|
wxLogDebug( wxT("%s"), p->GetLabel().c_str() );
|
|
text_x = x;
|
|
}
|
|
*/
|
|
|
|
if ( p != selected )
|
|
{
|
|
dc.DrawText( p->m_label, text_x+wxPG_XBEFORETEXT, y );
|
|
}
|
|
else
|
|
{
|
|
// Selected gets different colour.
|
|
if ( reallyFocused )
|
|
dc.SetTextForeground( m_colSelFore );
|
|
|
|
dc.DrawText( p->m_label, text_x+wxPG_XBEFORETEXT, y );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*// switch background colour
|
|
bgbrush.SetColour ( ((wxPropertyCategoryClass*)p)->m_colCellBg );
|
|
dc.SetBrush ( bgbrush );*/
|
|
}
|
|
|
|
y += m_fontHeight+m_spacingy+1;
|
|
|
|
ITEM_ITERATION_UVC_LOOP_END(vi_end_y)
|
|
}
|
|
|
|
|
|
//
|
|
// Third pass: Values
|
|
//
|
|
dc.DestroyClippingRegion();
|
|
|
|
if ( clipRect )
|
|
{
|
|
// third pass happens entirely on the right side, so sometimes
|
|
// we can just skip it
|
|
if ( (clipRect->x + clipRect->width) < m_splitterx )
|
|
x = -1;
|
|
dc.SetClippingRegion ( *clipRect );
|
|
}
|
|
|
|
// This used with value drawer method.
|
|
wxRect valueRect(0,0,
|
|
m_width-(m_splitterx+wxPG_CONTROL_MARGIN),
|
|
m_fontHeight);
|
|
|
|
wxSize imageSize;
|
|
|
|
if ( x != -1 )
|
|
{
|
|
|
|
r.x = m_splitterx+1+wxPG_CONTROL_MARGIN;
|
|
r.width = m_width-m_splitterx-wxPG_CONTROL_MARGIN;
|
|
//r.x = m_splitterx+wxPG_DIST_SPLITTER_TO_IMAGE;
|
|
//r.width = m_width-m_splitterx-wxPG_DIST_SPLITTER_TO_IMAGE-1;
|
|
r.height = lh-1;
|
|
|
|
/*#if wxCC_CORRECT_CONTROL_POSITION
|
|
const int vy2 = vy;
|
|
#endif*/
|
|
|
|
//wxLogDebug(wxT(" \\--> third pass..."));
|
|
|
|
// Altough this line may seem unnecessary, it isn't
|
|
dc.SetFont(normalfont);
|
|
|
|
dc.SetPen( *wxTRANSPARENT_PEN );
|
|
|
|
// Prepare paintdata.
|
|
paintdata.m_parent = this;
|
|
paintdata.m_choiceItem = -1; // Not drawing list item at this time.
|
|
|
|
y = firstItemTopY;
|
|
|
|
ITEM_ITERATION_UVC_LOOP_BEGIN(viStart,vi_end_y)
|
|
|
|
if ( parenting <= 0 )
|
|
{
|
|
r.y = y;
|
|
y += m_spacingy;
|
|
|
|
// background
|
|
dc.SetBrush( *(wxPGBrush*)m_arrBgBrushes[p->m_bgColIndex] );
|
|
|
|
if ( isEnabled && p->IsEnabled() )
|
|
dc.SetTextForeground( *(wxPGColour*)m_arrFgCols[p->m_fgColIndex] );
|
|
else
|
|
dc.SetTextForeground( m_colDisPropFore );
|
|
|
|
// draw value string only if editor widget not open
|
|
// (exception: no primary editor widget or it is hidden)
|
|
if ( p != selected || !m_wndPrimary
|
|
// "if not primary shown" is required because
|
|
// primary is not usually shown during splitter
|
|
// movement.
|
|
|| m_dragStatus > 0
|
|
)
|
|
{
|
|
|
|
valueRect.x = m_splitterx+wxPG_CONTROL_MARGIN;
|
|
valueRect.y = y;
|
|
|
|
// Draw background
|
|
if ( p != selected )
|
|
{
|
|
dc.DrawRectangle( r );
|
|
}
|
|
else
|
|
{
|
|
if ( m_wndPrimary )
|
|
dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
|
|
else
|
|
dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
|
|
|
|
dc.DrawRectangle( r );
|
|
}
|
|
|
|
// Set bold font?
|
|
if ( p->m_flags & wxPG_PROP_MODIFIED && (window_style & wxPG_BOLD_MODIFIED) )
|
|
dc.SetFont( m_captionFont );
|
|
|
|
const wxPGEditor* editor = p->GetEditorClass();
|
|
bool fullPaint = false;
|
|
|
|
if ( p->m_flags & wxPG_PROP_CUSTOMIMAGE )
|
|
{
|
|
imageSize = p->GetImageSize();
|
|
|
|
wxRect imageRect(r.x + wxPG_CONTROL_MARGIN + wxCC_CUSTOM_IMAGE_MARGIN1,
|
|
r.y+wxPG_CUSTOM_IMAGE_SPACINGY,
|
|
wxPG_CUSTOM_IMAGE_WIDTH,
|
|
r.height-(wxPG_CUSTOM_IMAGE_SPACINGY*2));
|
|
|
|
if ( imageSize.x == wxPG_FULL_CUSTOM_PAINT_WIDTH )
|
|
{
|
|
fullPaint = true;
|
|
imageRect.width = m_width - imageRect.x;
|
|
}
|
|
|
|
dc.SetPen( outlinepen );
|
|
|
|
paintdata.m_drawnWidth = imageRect.width;
|
|
|
|
if ( !(p->m_flags & wxPG_PROP_UNSPECIFIED) )
|
|
{
|
|
p->OnCustomPaint( dc, imageRect, paintdata );
|
|
}
|
|
else
|
|
{
|
|
dc.SetBrush(*wxWHITE_BRUSH);
|
|
dc.DrawRectangle(imageRect);
|
|
}
|
|
dc.SetPen( *wxTRANSPARENT_PEN );
|
|
}
|
|
else
|
|
paintdata.m_drawnWidth = 0;
|
|
|
|
if ( paintdata.m_drawnWidth > 0 )
|
|
valueRect.x += paintdata.m_drawnWidth + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2;
|
|
else
|
|
fullPaint = false;
|
|
|
|
if ( !fullPaint )
|
|
editor->DrawValue(dc,p,valueRect);
|
|
|
|
// Return original font?
|
|
if ( p->m_flags & wxPG_PROP_MODIFIED && (window_style & wxPG_BOLD_MODIFIED) )
|
|
dc.SetFont(normalfont);
|
|
}
|
|
else
|
|
{
|
|
|
|
if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) ||
|
|
m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE )
|
|
{
|
|
//wxLogDebug(wxT("Primary doesn't fill entire"));
|
|
dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
|
|
dc.DrawRectangle( r );
|
|
}
|
|
if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE )
|
|
{
|
|
wxRect imagerect(r.x + wxPG_CONTROL_MARGIN + wxCC_CUSTOM_IMAGE_MARGIN1,
|
|
r.y+wxPG_CUSTOM_IMAGE_SPACINGY,wxPG_CUSTOM_IMAGE_WIDTH,
|
|
r.height-(wxPG_CUSTOM_IMAGE_SPACINGY*2));
|
|
|
|
dc.SetPen ( outlinepen );
|
|
if ( !(p->m_flags & wxPG_PROP_UNSPECIFIED) )
|
|
{
|
|
p->OnCustomPaint( dc, imagerect, paintdata );
|
|
}
|
|
else
|
|
{
|
|
dc.SetBrush(*wxWHITE_BRUSH);
|
|
dc.DrawRectangle(imagerect);
|
|
}
|
|
}
|
|
dc.SetPen( *wxTRANSPARENT_PEN );
|
|
}
|
|
|
|
y += m_fontHeight+m_spacingy + 1;
|
|
}
|
|
else
|
|
{
|
|
// caption item
|
|
y += lh;
|
|
}
|
|
|
|
//if ( y > lastItemBottomY ) { parent = NULL; break; }
|
|
|
|
ITEM_ITERATION_UVC_LOOP_END(vi_end_y)
|
|
|
|
}
|
|
|
|
dc.DestroyClippingRegion(); // Is this really necessary?
|
|
|
|
#if wxPG_DOUBLE_BUFFER
|
|
//}
|
|
//else wxLogDebug(wxT("Used Cache"));
|
|
|
|
if ( bufferDC )
|
|
{
|
|
if ( blitClipRect )
|
|
dcMain.SetClippingRegion( *blitClipRect );
|
|
//wxLogDebug(wxT(" \\--> (0,%i)"),(int)final_y);
|
|
dcMain.Blit ( 0, firstItem->m_y, m_width, renderHeight,
|
|
&dc, 0, firstItem->m_y-vy, wxCOPY );
|
|
//dcMain.Blit ( 0, 0, m_width, m_height,
|
|
// &dc, 0, 0, wxCOPY );
|
|
dcMain.DestroyClippingRegion(); // Is this really necessary?
|
|
delete bufferDC;
|
|
}
|
|
#endif
|
|
|
|
#if __PAINT_DEBUGGING__
|
|
wxLogDebug(wxT(" \\--> ends..."));
|
|
#endif
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxRect wxPropertyGrid::GetPropertyRect( const wxPGProperty* p1, const wxPGProperty* p2 ) const
|
|
{
|
|
wxRect r;
|
|
|
|
if ( m_width < 10 || m_height < 10 ||
|
|
!FROM_STATE(m_properties)->GetCount() ||
|
|
p1 == (wxPGProperty*) NULL )
|
|
return wxRect(0,0,0,0);
|
|
|
|
int vx,vy;
|
|
GetViewStart(&vx,&vy);
|
|
vy*=wxPG_PIXELS_PER_UNIT;
|
|
|
|
//
|
|
// Return rect which encloses the given property range
|
|
|
|
int visTop = p1->m_y;
|
|
int visBottom = m_bottomy;
|
|
if ( p2 )
|
|
visBottom = p2->m_y + m_lineHeight;
|
|
|
|
// If seleced property is inside the range, we'll extend the range to include
|
|
// control's size.
|
|
wxPGProperty* selected = m_selected;
|
|
if ( selected && selected->m_y >= visTop && selected->m_y < visBottom )
|
|
{
|
|
wxWindow* editor = GetEditorControl();
|
|
if ( editor )
|
|
{
|
|
int visBottom2 = selected->m_y + editor->GetSize().y;
|
|
if ( visBottom2 > visBottom )
|
|
visBottom = visBottom2;
|
|
}
|
|
}
|
|
|
|
return wxRect(0,visTop-vy,m_width,visBottom-visTop);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::DrawItems( const wxPGProperty* p1, const wxPGProperty* p2 )
|
|
{
|
|
if ( m_frozen )
|
|
return;
|
|
|
|
if ( FROM_STATE(m_itemsAdded) )
|
|
PrepareAfterItemsAdded();
|
|
|
|
wxRect r = GetPropertyRect(p1, p2);
|
|
if ( r.width > 0 )
|
|
RefreshRect(r);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// In addition to calling DoDrawItems directly, this is the
|
|
// only alternative for using wxClientDC - others just call
|
|
// RefreshRect.
|
|
void wxPropertyGrid::DrawItem( wxDC& dc, wxPGProperty* p )
|
|
{
|
|
wxCHECK_RET( p, wxT("invalid property id") );
|
|
|
|
// do not draw a single item if multiple pending
|
|
if ( FROM_STATE(m_itemsAdded) )
|
|
return;
|
|
|
|
if ( p->m_y < 0 )
|
|
return;
|
|
|
|
#if __PAINT_DEBUGGING__
|
|
wxLogDebug(wxT("wxPropertyGrid::DrawItem( %s )"), p->GetLabel().c_str() );
|
|
#endif
|
|
|
|
DoDrawItems( dc, p, p, NULL );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::RefreshProperty( wxPGProperty* p )
|
|
{
|
|
if ( p == m_selected )
|
|
DoSelectProperty(p, wxPG_SEL_FORCE);
|
|
|
|
DrawItemAndChildren(p);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::DrawItemAndValueRelated( wxPGProperty* p )
|
|
{
|
|
if ( m_frozen )
|
|
return;
|
|
|
|
// Draw item, children, and parent too, if it is not category
|
|
wxPGProperty* parent = p->GetParent();
|
|
|
|
while ( parent &&
|
|
parent->GetParentingType() < PT_NONE )
|
|
{
|
|
DrawItem(parent);
|
|
parent = parent->GetParent();
|
|
}
|
|
|
|
DrawItemAndChildren(p);
|
|
}
|
|
|
|
void wxPropertyGrid::DrawItemAndChildren( wxPGProperty* p )
|
|
{
|
|
wxCHECK_RET( p, wxT("invalid property id") );
|
|
|
|
// Do not draw if in non-visible page
|
|
if ( p->GetParentState() != m_pState )
|
|
return;
|
|
|
|
// do not draw a single item if multiple pending
|
|
if ( FROM_STATE(m_itemsAdded) || p->m_y < 0 || m_frozen )
|
|
return;
|
|
|
|
#if __PAINT_DEBUGGING__
|
|
wxLogDebug(wxT("wxPropertyGrid::DrawItemAndChildren( %s )"), p->GetLabel().c_str() );
|
|
#endif
|
|
|
|
// Update child control.
|
|
if ( m_selected && m_selected->GetParent() == p )
|
|
m_selected->UpdateControl(m_wndPrimary);
|
|
|
|
const wxPGProperty* lastDrawn = p->GetLastVisibleSubItem();
|
|
|
|
DrawItems(p, lastDrawn);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::Refresh( bool WXUNUSED(eraseBackground),
|
|
const wxRect *rect )
|
|
{
|
|
// Refresh implies forced redraw
|
|
//m_iFlags |= wxPG_FL_CHANGED;
|
|
|
|
wxWindow::Refresh(false,rect);
|
|
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
|
|
// I think this really helps only GTK+1.2
|
|
if ( m_wndPrimary ) m_wndPrimary->Refresh();
|
|
if ( m_wndSecondary ) m_wndSecondary->Refresh();
|
|
#endif
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
/*
|
|
void wxPropertyGrid::RedrawAllVisible ()
|
|
{
|
|
// TODO: Is this safe?
|
|
//Update();
|
|
|
|
if ( m_frozen || !IsShown() )
|
|
return;
|
|
|
|
wxPG_CLIENT_DC_INIT()
|
|
|
|
#if __PAINT_DEBUGGING__
|
|
wxLogDebug( wxT("wxPropertyGrid::RedrawAllVisible()") );
|
|
#endif
|
|
|
|
int vx,vy; // Top left corner of client
|
|
GetViewStart(&vx,&vy);
|
|
vy *= wxPG_PIXELS_PER_UNIT;
|
|
|
|
int y1 = vy;
|
|
int y2 = y1 + m_height;
|
|
|
|
// Repaint this rectangle
|
|
DrawItems ( dc, y1, y2, (wxRect*) NULL );
|
|
|
|
}
|
|
*/
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
#if wxPG_HEAVY_GFX
|
|
void wxPropertyGrid::DrawSplitterDragColumn( wxDC&, int ) { }
|
|
#else
|
|
void wxPropertyGrid::DrawSplitterDragColumn( wxDC& dc, int x )
|
|
{
|
|
int vx, vy;
|
|
GetViewStart(&vx,&vy);
|
|
vy *= wxPG_PIXELS_PER_UNIT;
|
|
|
|
dc.SetLogicalFunction(wxINVERT);
|
|
dc.DestroyClippingRegion();
|
|
|
|
dc.SetPen( m_splitterpen );
|
|
dc.DrawLine(x,vy,x,vy+m_height);
|
|
|
|
}
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid global operations
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::Clear()
|
|
{
|
|
if ( m_selected )
|
|
{
|
|
bool selRes = DoSelectProperty(wxPGIdGen(NULL), wxPG_SEL_DELETING); // This must be before state clear
|
|
wxPG_CHECK_RET_DBG( selRes,
|
|
wxT("failed to deselect a property (editor probably had invalid value)") );
|
|
}
|
|
|
|
FROM_STATE(Clear());
|
|
|
|
m_propHover = NULL;
|
|
m_bottomy = 0;
|
|
|
|
m_prevVY = 0;
|
|
m_arrVisible.Empty();
|
|
|
|
RecalculateVirtualSize();
|
|
|
|
// Need to clear some area at the end
|
|
if ( !m_frozen )
|
|
RefreshRect(wxRect(0, 0, m_width, m_height));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::EnableCategories( bool enable )
|
|
{
|
|
if ( !ClearSelection() )
|
|
return false;
|
|
|
|
if ( enable )
|
|
{
|
|
//
|
|
// Enable categories
|
|
//
|
|
|
|
m_windowStyle &= ~(wxPG_HIDE_CATEGORIES);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Disable categories
|
|
//
|
|
m_windowStyle |= wxPG_HIDE_CATEGORIES;
|
|
}
|
|
|
|
if ( !m_pState->EnableCategories(enable) )
|
|
return false;
|
|
|
|
if ( !m_frozen )
|
|
{
|
|
if ( m_windowStyle & wxPG_AUTO_SORT )
|
|
{
|
|
FROM_STATE(m_itemsAdded) = 1; // force
|
|
PrepareAfterItemsAdded();
|
|
}
|
|
else
|
|
{
|
|
CalculateYs(NULL,-1);
|
|
//CalculateVisibles( -1 );
|
|
}
|
|
}
|
|
else
|
|
FROM_STATE(m_itemsAdded) = 1;
|
|
|
|
Refresh();
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SwitchState( wxPropertyGridState* pNewState )
|
|
{
|
|
wxASSERT( pNewState );
|
|
|
|
wxPGProperty* oldSelection = m_selected;
|
|
|
|
// Deselect
|
|
if ( m_selected )
|
|
{
|
|
bool selRes = ClearSelection();
|
|
wxPG_CHECK_RET_DBG( selRes,
|
|
wxT("failed to deselect a property (editor probably had invalid value)") );
|
|
}
|
|
|
|
m_pState->m_selected = oldSelection;
|
|
|
|
bool orig_mode = m_pState->IsInNonCatMode();
|
|
bool new_state_mode = pNewState->IsInNonCatMode();
|
|
|
|
m_pState = pNewState;
|
|
|
|
m_bottomy = 0; // This is necessary or y's won't get updated.
|
|
m_propHover = (wxPGProperty*) NULL;
|
|
|
|
// If necessary, convert state to correct mode.
|
|
if ( orig_mode != new_state_mode )
|
|
{
|
|
// This should refresh as well.
|
|
EnableCategories ( orig_mode?false:true );
|
|
}
|
|
else if ( !m_frozen )
|
|
{
|
|
// Refresh, if not frozen.
|
|
if ( FROM_STATE(m_itemsAdded) )
|
|
PrepareAfterItemsAdded();
|
|
else
|
|
CalculateYs(NULL,-1);
|
|
|
|
// Reselect
|
|
if ( FROM_STATE(m_selected) )
|
|
DoSelectProperty( FROM_STATE(m_selected) );
|
|
|
|
//RedrawAllVisible();
|
|
Refresh();
|
|
}
|
|
else
|
|
m_pState->m_itemsAdded = 1;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::Sort( wxPGId id )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
m_pState->Sort( p );
|
|
|
|
// Because order changed, Y's need to be changed as well
|
|
if ( p->GetParentState() == m_pState )
|
|
CalculateYs ( p->m_parent, p->m_arrIndex );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::Sort()
|
|
{
|
|
bool selRes = ClearSelection(); // This must be before state clear
|
|
wxPG_CHECK_RET_DBG( selRes,
|
|
wxT("failed to deselect a property (editor probably had invalid value)") );
|
|
|
|
m_pState->Sort();
|
|
|
|
CalculateYs( NULL, -1 );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Call to SetSplitterPosition will always disable splitter auto-centering
|
|
// if parent window is shown.
|
|
void wxPropertyGrid::DoSetSplitterPosition( int newxpos, bool refresh )
|
|
{
|
|
if ( ( newxpos < wxPG_DRAG_MARGIN ) )
|
|
return;
|
|
// ( m_width > wxPG_DRAG_MARGIN && newxpos > (m_width-wxPG_DRAG_MARGIN) )
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug( wxT("wxPropertyGrid::DoSetSplitterPosition ( %i )"), newxpos );
|
|
#endif
|
|
|
|
#if wxPG_HEAVY_GFX
|
|
m_splitterx = newxpos;
|
|
m_fSplitterX = (float) newxpos;
|
|
|
|
if ( refresh )
|
|
{
|
|
if ( m_selected )
|
|
CorrectEditorWidgetSizeX( m_splitterx, m_width );
|
|
|
|
Refresh();
|
|
//RedrawAllVisible(); // no flicker
|
|
}
|
|
#else
|
|
if ( !m_dragStatus )
|
|
{
|
|
// Only do this if this was not a call from HandleMouseUp
|
|
m_startingSplitterX = m_splitterx;
|
|
m_splitterx = newxpos;
|
|
m_fSplitterX = (float) newxpos;
|
|
}
|
|
|
|
// Clear old
|
|
if ( m_splitterprevdrawnx != -1 )
|
|
{
|
|
wxPG_CLIENT_DC_INIT()
|
|
|
|
DrawSplitterDragColumn( dc, m_splitterprevdrawnx );
|
|
m_splitterprevdrawnx = -1;
|
|
}
|
|
|
|
// Redraw only if drag really moved
|
|
if ( m_splitterx != m_startingSplitterX && refresh )
|
|
{
|
|
if ( m_selected)
|
|
CorrectEditorWidgetSizeX( m_splitterx, m_width );
|
|
|
|
Update(); // This fixes a graphics-mess in wxMSW
|
|
|
|
Refresh();
|
|
//RedrawAllVisible(); // no flicker
|
|
}
|
|
#endif
|
|
|
|
// Don't allow initial splitter auto-positioning after this.
|
|
m_iFlags |= wxPG_FL_SPLITTER_PRE_SET;
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::CenterSplitter( bool enable_auto_centering )
|
|
{
|
|
SetSplitterPosition ( m_width/2, true );
|
|
if ( enable_auto_centering && ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER ) )
|
|
m_iFlags &= ~(wxPG_FL_DONT_CENTER_SPLITTER);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Moves splitter so that all labels are visible, but just.
|
|
void wxPropertyGrid::SetSplitterLeft( bool subProps )
|
|
{
|
|
wxClientDC dc(this);
|
|
dc.SetFont(m_font);
|
|
|
|
int maxW = m_pState->GetLeftSplitterPos(dc, m_pState->m_properties, subProps );
|
|
|
|
if ( maxW > 0 )
|
|
SetSplitterPosition( maxW );
|
|
|
|
m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid item iteration (GetNextProperty etc.) methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Returns nearest paint visible property (such that will be painted unless
|
|
// window is scrolled or resized). If given property is paint visible, then
|
|
// it itself will be returned
|
|
wxPGProperty* wxPropertyGrid::GetNearestPaintVisible( wxPGProperty* p )
|
|
{
|
|
int vx,vy1;// Top left corner of client
|
|
GetViewStart(&vx,&vy1);
|
|
vy1 *= wxPG_PIXELS_PER_UNIT;
|
|
|
|
int vy2 = vy1 + m_height;
|
|
|
|
if ( (p->m_y + m_lineHeight) < vy1 )
|
|
{
|
|
// Too high
|
|
return DoGetItemAtY( vy1 );
|
|
}
|
|
else if ( p->m_y > vy2 )
|
|
{
|
|
// Too low
|
|
return DoGetItemAtY( vy2 );
|
|
}
|
|
|
|
// Itself paint visible
|
|
return p;
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGProperty* wxPropertyGrid::GetNeighbourItem( wxPGProperty* item,
|
|
bool need_visible,
|
|
int dir ) const
|
|
{
|
|
wxPGPropertyWithChildren* parent = item->m_parent;
|
|
unsigned int indinparent = item->GetIndexInParent();
|
|
|
|
if ( dir > 0 )
|
|
{
|
|
if ( item->GetChildCount() == 0 ||
|
|
(!((wxPGPropertyWithChildren*)item)->m_expanded && need_visible) )
|
|
{
|
|
// current item did not have any expanded children
|
|
if ( indinparent < (parent->GetCount()-1) )
|
|
{
|
|
// take next in parent's array
|
|
item = parent->Item(indinparent+1);
|
|
}
|
|
else
|
|
{
|
|
// no more in parent's array; move up until found;
|
|
wxPGPropertyWithChildren* p2 = parent;
|
|
parent = parent->m_parent;
|
|
item = (wxPGProperty*) NULL;
|
|
while ( parent )
|
|
{
|
|
if ( p2->m_arrIndex < (parent->GetCount()-1) )
|
|
{
|
|
item = parent->Item(p2->m_arrIndex+1);
|
|
break;
|
|
}
|
|
p2 = parent;
|
|
parent = parent->m_parent;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// take first of current item's children
|
|
wxPGPropertyWithChildren* p2 = (wxPGPropertyWithChildren*)item;
|
|
item = p2->Item(0);
|
|
//indinparent = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
// items in array left?
|
|
if ( indinparent > 0 )
|
|
{
|
|
// take prev in parent's array
|
|
item = parent->Item(indinparent-1);
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)item;
|
|
|
|
// Recurse to it's last child
|
|
while ( item->GetParentingType() != 0 && pwc->GetCount() &&
|
|
( pwc->m_expanded || !need_visible )
|
|
)
|
|
{
|
|
item = pwc->Last();
|
|
pwc = (wxPGPropertyWithChildren*)item;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// If we were at first, go to parent
|
|
item = parent;
|
|
}
|
|
}
|
|
|
|
if ( item == FROM_STATE(m_properties) )
|
|
return (wxPGProperty*) NULL;
|
|
|
|
// If item was hidden and need_visible, get next.
|
|
if ( (m_iFlags & wxPG_FL_HIDE_STATE) && need_visible && item )
|
|
{
|
|
if ( item->m_flags & wxPG_PROP_HIDEABLE )
|
|
{
|
|
// Speed-up: If parent is hidden as well, then skip to last child or to itself
|
|
if ( parent->m_flags & wxPG_PROP_HIDEABLE )
|
|
{
|
|
item = parent; // if dir up
|
|
if ( dir > 0 )
|
|
item = parent->Last(); // if dir down
|
|
}
|
|
|
|
return GetNeighbourItem ( item, need_visible, dir );
|
|
}
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// This is used in DoDrawItems.
|
|
wxPGProperty* wxPropertyGrid::GetLastItem ( bool need_visible, bool allowSubprops )
|
|
{
|
|
if ( FROM_STATE(m_properties)->GetCount() < 1 )
|
|
return (wxPGProperty*) NULL;
|
|
|
|
wxPGProperty* p = FROM_STATE(m_properties)->Last();
|
|
|
|
int parenting = p->GetParentingType();
|
|
|
|
while ( parenting != 0 && ( allowSubprops || parenting >= PT_CAPTION ) )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
|
|
parenting = 0;
|
|
if ( pwc->GetCount() )
|
|
{
|
|
|
|
if ( (!need_visible || pwc->m_expanded) )
|
|
{
|
|
p = pwc->Last();
|
|
parenting = p->GetParentingType();
|
|
}
|
|
else
|
|
parenting = 0;
|
|
}
|
|
}
|
|
|
|
// If item was hidden and need_visible, get previous.
|
|
if ( (m_iFlags & wxPG_FL_HIDE_STATE) &&
|
|
need_visible &&
|
|
p && ( p->m_flags & wxPG_PROP_HIDEABLE )
|
|
)
|
|
return GetNeighbourItem( p, need_visible, -1 );
|
|
|
|
return p;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetButtonShortcut( int keycode, bool ctrlDown, bool altDown )
|
|
{
|
|
if ( keycode )
|
|
{
|
|
m_pushButKeyCode = keycode;
|
|
m_pushButKeyCodeNeedsCtrl = ctrlDown ? 1 : 0;
|
|
m_pushButKeyCodeNeedsAlt = altDown ? 1 : 0;
|
|
}
|
|
else
|
|
{
|
|
m_pushButKeyCode = WXK_DOWN;
|
|
m_pushButKeyCodeNeedsCtrl = 0;
|
|
m_pushButKeyCodeNeedsAlt = 1;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Methods related to change in value, value modification and sending events
|
|
// -----------------------------------------------------------------------
|
|
|
|
// commits any changes in editor of selected property
|
|
// return true if validation did not fail
|
|
// flags are same as with DoSelectProperty
|
|
bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags )
|
|
{
|
|
if ( m_wndPrimary && IsEditorsValueModified() &&
|
|
(m_iFlags & wxPG_FL_INITIALIZED) )
|
|
{
|
|
wxCHECK_MSG( m_selected, false, wxT("no selection") );
|
|
|
|
bool wasUnspecified = m_selected->IsValueUnspecified();
|
|
|
|
// JACS - necessary to avoid new focus being found spuriously within OnIdle
|
|
// due to another window getting focus
|
|
wxWindow* oldFocus = m_curFocused;
|
|
|
|
if ( !(flags & (wxPG_SEL_NOVALIDATE|wxPG_SEL_FORCE)) &&
|
|
!DoEditorValidate() )
|
|
{
|
|
if (oldFocus)
|
|
{
|
|
oldFocus->SetFocus();
|
|
m_curFocused = oldFocus;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// Save value (only if truly modified).
|
|
if ( !m_selected->GetEditorClass()->CopyValueFromControl( m_selected, m_wndPrimary ) )
|
|
EditorsValueWasNotModified();
|
|
|
|
if ( m_selected->IsValueUnspecified() && !wasUnspecified && UsesAutoUnspecified() )
|
|
flags |= wxPG_SEL_SETUNSPEC;
|
|
|
|
DoPropertyChanged( m_selected, flags );
|
|
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// flags are same as with DoSelectProperty
|
|
void wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags )
|
|
{
|
|
if ( m_processingEvent )
|
|
return;
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug(wxT("wxPropertyGrid::DoPropertyChanged( %s )"),p->GetLabel().c_str());
|
|
#endif
|
|
|
|
m_pState->m_anyModified = 1;
|
|
|
|
m_processingEvent = 1;
|
|
|
|
// No longer unspecified (but not if the value was set to unspecified by
|
|
// user modification)
|
|
if ( !(selFlags & wxPG_SEL_SETUNSPEC) )
|
|
CLEAR_PROPERTY_UNSPECIFIED_FLAG(p);
|
|
|
|
if ( m_iFlags & wxPG_FL_VALUE_MODIFIED )
|
|
{
|
|
m_iFlags &= ~(wxPG_FL_VALUE_MODIFIED);
|
|
|
|
// Set as Modified (not if dragging just began)
|
|
if ( !(p->m_flags & wxPG_PROP_MODIFIED) )
|
|
{
|
|
p->m_flags |= wxPG_PROP_MODIFIED;
|
|
if ( p == m_selected && (m_windowStyle & wxPG_BOLD_MODIFIED) )
|
|
{
|
|
if ( m_wndPrimary )
|
|
SetCurControlBoldFont();
|
|
}
|
|
}
|
|
|
|
wxPGProperty* curChild = p;
|
|
wxPGPropertyWithChildren* curParent = p->m_parent;
|
|
|
|
// Also update parent(s), if any
|
|
// (but not if its wxCustomProperty)
|
|
|
|
while ( curParent &&
|
|
curParent->GetParentingType() < 0 /*&&
|
|
wxStrcmp(curParent->GetClassName(),wxT("wxCustomProperty")) != 0*/ )
|
|
{
|
|
// Set as Modified
|
|
if ( !(curParent->m_flags & wxPG_PROP_MODIFIED) )
|
|
{
|
|
curParent->m_flags |= wxPG_PROP_MODIFIED;
|
|
if ( curParent == m_selected && (m_windowStyle & wxPG_BOLD_MODIFIED) )
|
|
{
|
|
if ( m_wndPrimary )
|
|
SetCurControlBoldFont();
|
|
}
|
|
}
|
|
|
|
curParent->ChildChanged( curChild );
|
|
|
|
DrawItem( curParent );
|
|
|
|
curChild = curParent;
|
|
curParent = curParent->GetParent();
|
|
}
|
|
|
|
// Draw the actual property
|
|
if ( ( p != m_selected ) || !m_wndPrimary ||
|
|
( p->GetParentingType() < 0 ) ||
|
|
( p->m_flags & wxPG_PROP_CUSTOMIMAGE ) )
|
|
{
|
|
DrawItemAndChildren( p );
|
|
}
|
|
|
|
if ( curChild != p && !(selFlags & wxPG_SEL_SETUNSPEC) )
|
|
//m_pState->ClearPropertyAndChildrenFlags(curChild,wxPG_PROP_UNSPECIFIED);
|
|
CLEAR_PROPERTY_UNSPECIFIED_FLAG(curChild);
|
|
|
|
wxPGProperty* changedProperty;
|
|
|
|
// Call wx event handler for property (or its topmost parent, but only
|
|
// when dealing with legitemate sub-properties - see above).
|
|
if ( curChild->GetParentingType() != PT_CUSTOMPROPERTY )
|
|
changedProperty = curChild;
|
|
else
|
|
changedProperty = p;
|
|
|
|
// Maybe need to update control
|
|
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
|
|
if ( m_wndPrimary ) m_wndPrimary->Refresh();
|
|
if ( m_wndSecondary ) m_wndSecondary->Refresh();
|
|
#endif
|
|
|
|
SendEvent( wxEVT_PG_CHANGED, changedProperty, selFlags );
|
|
}
|
|
|
|
m_processingEvent = 0;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Runs wxValidator for the selected property
|
|
bool wxPropertyGrid::DoEditorValidate()
|
|
{
|
|
#if wxUSE_VALIDATORS
|
|
if ( m_iFlags & wxPG_FL_VALIDATION_FAILED )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
wxWindow* wnd = GetEditorControl();
|
|
|
|
wxValidator* validator = m_selected->GetValidator();
|
|
if ( validator && wnd )
|
|
{
|
|
// Use TextCtrl of ODComboBox instead
|
|
if ( wnd->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
|
|
{
|
|
wnd = ((wxPGOwnerDrawnComboBox*)wnd)->GetTextCtrl();
|
|
|
|
if ( !wnd )
|
|
return true;
|
|
}
|
|
|
|
validator->SetWindow(wnd);
|
|
|
|
// Instead setting the flag after the failure, we set
|
|
// it before checking and then clear afterwards if things
|
|
// went fine. This trick is necessary since focus events
|
|
// may be triggered while in Validate.
|
|
m_iFlags |= wxPG_FL_VALIDATION_FAILED;
|
|
if ( !validator->Validate(this) )
|
|
{
|
|
// If you dpm't want to display message multiple times per change,
|
|
// comment the following line.
|
|
m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
|
|
return false;
|
|
}
|
|
m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
#if wxUSE_VALIDATORS
|
|
bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* propGrid,
|
|
wxValidator* validator,
|
|
const wxString& value )
|
|
{
|
|
if ( !validator )
|
|
return true;
|
|
|
|
wxTextCtrl* tc = m_textCtrl;
|
|
|
|
if ( !tc )
|
|
{
|
|
{
|
|
tc = new wxTextCtrl( propGrid, wxPG_SUBID_TEMP1, wxEmptyString,
|
|
wxPoint(30000,30000));
|
|
tc->Hide();
|
|
}
|
|
|
|
m_textCtrl = tc;
|
|
}
|
|
|
|
//wxString oldValue = tc->GetValue();
|
|
tc->SetValue(value);
|
|
|
|
validator->SetWindow(tc);
|
|
bool res = validator->Validate(propGrid);
|
|
|
|
//tc->SetValue(oldValue);
|
|
return res;
|
|
}
|
|
#else
|
|
bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* WXUNUSED(propGrid),
|
|
wxValidator* WXUNUSED(validator),
|
|
const wxString& WXUNUSED(value) )
|
|
{
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// NB: It may really not be wxCommandEvent - must check if necessary
|
|
// (usually not).
|
|
void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event )
|
|
{
|
|
wxPGProperty* selected = m_selected;
|
|
|
|
//
|
|
// Somehow, event is handled after property has been deselected.
|
|
// Possibly, but very rare.
|
|
if ( !selected )
|
|
return;
|
|
|
|
bool wasUnspecified = selected->IsValueUnspecified();
|
|
bool usesAutoUnspecified = UsesAutoUnspecified();
|
|
wxWindow* wnd = m_wndPrimary;
|
|
bool res1, res2;
|
|
|
|
m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
|
|
|
|
// First call editor class' event handler.
|
|
const wxPGEditor* editor = selected->GetEditorClass();
|
|
res1 = editor->OnEvent( this, selected, wnd, event );
|
|
|
|
if ( res1 )
|
|
{
|
|
// If changes, validate them
|
|
if ( DoEditorValidate() )
|
|
{
|
|
if ( editor->CopyValueFromControl( selected, wnd ) )
|
|
{
|
|
}
|
|
else
|
|
{
|
|
// False alarm
|
|
res1 = false;
|
|
|
|
EditorsValueWasNotModified();
|
|
|
|
// However, even moot editing will clear the unspecified status
|
|
if ( wasUnspecified || !usesAutoUnspecified )
|
|
CLEAR_PROPERTY_UNSPECIFIED_FLAG(selected);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
res1 = false;
|
|
EditorsValueWasNotModified();
|
|
if ( wasUnspecified || !usesAutoUnspecified )
|
|
CLEAR_PROPERTY_UNSPECIFIED_FLAG(selected);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Then the property's custom handler (must be always called).
|
|
res2 = selected->OnEvent( this, wnd, event );
|
|
|
|
if ( res1 || res2 )
|
|
{
|
|
// Setting this is not required if res was true, so we do it now.
|
|
m_iFlags |= wxPG_FL_VALUE_MODIFIED;
|
|
|
|
int selFlags = ( !wasUnspecified && selected->IsValueUnspecified() && usesAutoUnspecified ) ? wxPG_SEL_SETUNSPEC : 0;
|
|
|
|
DoPropertyChanged(selected, selFlags);
|
|
}
|
|
else
|
|
// Let unhandled button click events go to the parent
|
|
if ( event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
|
|
{
|
|
wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,GetId());
|
|
GetEventHandler()->AddPendingEvent(evt);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// When a property's value was modified internally (using SetValueFromString
|
|
// or SetValueFromInt, for example), then this should be called afterwards.
|
|
// NB: Avoid using this method, if possible.
|
|
void wxPropertyGrid::PropertyWasModified( wxPGProperty* p, int selFlags )
|
|
{
|
|
wxCHECK_RET( p, wxT("invalid property id") );
|
|
EditorsValueWasModified();
|
|
DoPropertyChanged(p, selFlags);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid editor control helper methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxWindow* wxPropertyGrid::GetEditorControl() const
|
|
{
|
|
wxWindow* ctrl = m_wndPrimary;
|
|
|
|
if ( !ctrl )
|
|
return ctrl;
|
|
|
|
// If it's clipper window, return its child instead
|
|
#if wxPG_ENABLE_CLIPPER_WINDOW
|
|
if ( ctrl->IsKindOf(CLASSINFO(wxPGClipperWindow)) )
|
|
{
|
|
return ((wxPGClipperWindow*)ctrl)->GetControl();
|
|
}
|
|
#endif
|
|
|
|
return ctrl;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// inline because it is used exactly once in the code
|
|
inline wxRect wxPropertyGrid::GetEditorWidgetRect( wxPGProperty* p )
|
|
{
|
|
//wxASSERT( p->m_y >= 0 ); // item is not visible
|
|
|
|
int itemy = p->m_y;
|
|
int vx,vy;// Top left corner of client
|
|
GetViewStart(&vx,&vy);
|
|
vy *= wxPG_PIXELS_PER_UNIT;
|
|
int cust_img_space = 0;
|
|
|
|
//m_iFlags &= ~(wxPG_FL_CUR_USES_CUSTOM_IMAGE);
|
|
|
|
// TODO: If custom image detection changes from current, change this.
|
|
if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE /*p->m_flags & wxPG_PROP_CUSTOMIMAGE*/ )
|
|
{
|
|
//m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
|
|
int imwid = p->GetImageSize().x;
|
|
if ( imwid < 1 ) imwid = wxPG_CUSTOM_IMAGE_WIDTH;
|
|
cust_img_space = imwid + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2;
|
|
}
|
|
|
|
return wxRect
|
|
(
|
|
m_splitterx+cust_img_space+wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1,
|
|
itemy-vy,
|
|
m_width-m_splitterx-wxPG_XBEFOREWIDGET-wxPG_CONTROL_MARGIN-cust_img_space-1,
|
|
m_lineHeight-1
|
|
);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// return size of custom paint image
|
|
wxSize wxPropertyGrid::GetImageSize( wxPGId id ) const
|
|
{
|
|
if ( wxPGIdIsOk(id) )
|
|
{
|
|
wxSize cis = wxPGIdToPtr(id)->GetImageSize();
|
|
|
|
if ( cis.x < 0 )
|
|
{
|
|
if ( cis.x <= -1 )
|
|
cis.x = wxPG_CUSTOM_IMAGE_WIDTH;
|
|
}
|
|
if ( cis.y <= 0 )
|
|
{
|
|
if ( cis.y >= -1 )
|
|
cis.y = wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight);
|
|
else
|
|
cis.y = -cis.y;
|
|
}
|
|
return cis;
|
|
}
|
|
// If called with NULL property, then return default image
|
|
// size for properties that use image.
|
|
return wxSize(wxPG_CUSTOM_IMAGE_WIDTH,wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::CorrectEditorWidgetSizeX( int newSplitterx, int newWidth )
|
|
{
|
|
wxASSERT( m_selected );
|
|
|
|
int secWid = 0;
|
|
|
|
if ( m_wndSecondary )
|
|
{
|
|
// if width change occurred, move secondary wnd by that amount
|
|
wxRect r = m_wndSecondary->GetRect();
|
|
secWid = r.width;
|
|
r.x = newWidth - secWid;
|
|
//r.y += yAdj;
|
|
|
|
m_wndSecondary->SetSize ( r );
|
|
|
|
// if primary is textctrl, then we have to add some extra space
|
|
#ifdef __WXMAC__
|
|
if ( m_wndPrimary )
|
|
#else
|
|
if ( m_wndPrimary && m_wndPrimary->IsKindOf(CLASSINFO(wxTextCtrl)) )
|
|
#endif
|
|
secWid += wxPG_TEXTCTRL_AND_BUTTON_SPACING;
|
|
}
|
|
|
|
if ( m_wndPrimary )
|
|
{
|
|
wxRect r = m_wndPrimary->GetRect();
|
|
|
|
r.x = newSplitterx+m_ctrlXAdjust;
|
|
//r.y += yAdj;
|
|
r.width = newWidth - r.x - secWid;
|
|
|
|
m_wndPrimary->SetSize(r);
|
|
}
|
|
|
|
/*
|
|
int sec_wid = 0;
|
|
|
|
int vx, vy;
|
|
GetViewStart(&vx,&vy);
|
|
vy*=wxPG_PIXELS_PER_UNIT;
|
|
int propY = m_selected->m_y - vy;
|
|
|
|
if ( m_wndSecondary )
|
|
{
|
|
// if width change occurred, move secondary wnd by that amount
|
|
wxRect r = m_wndSecondary->GetRect();
|
|
int adjust = r.y % wxPG_PIXELS_PER_UNIT;
|
|
if ( adjust > (wxPG_PIXELS_PER_UNIT/2) )
|
|
adjust = adjust - wxPG_PIXELS_PER_UNIT;
|
|
int y = propY + adjust;
|
|
sec_wid = r.width;
|
|
|
|
m_wndSecondary->Move ( new_width-r.width,y );
|
|
|
|
// if primary is textctrl, then we have to add some extra space
|
|
if ( m_wndPrimary && m_wndPrimary->IsKindOf(CLASSINFO(wxTextCtrl)) )
|
|
sec_wid += wxPG_TEXTCTRL_AND_BUTTON_SPACING;
|
|
}
|
|
|
|
if ( m_wndPrimary )
|
|
{
|
|
wxRect r = m_wndPrimary->GetRect();
|
|
int adjust = r.y % wxPG_PIXELS_PER_UNIT;
|
|
if ( adjust > (wxPG_PIXELS_PER_UNIT/2) )
|
|
adjust = adjust - wxPG_PIXELS_PER_UNIT;
|
|
wxLogDebug(wxT("adjust: %i"),adjust);
|
|
int y = propY + adjust;
|
|
|
|
m_wndPrimary->SetSize(
|
|
new_splitterx+m_ctrlXAdjust,
|
|
y,
|
|
new_width-(new_splitterx+m_ctrlXAdjust)-sec_wid,
|
|
r.height
|
|
);
|
|
}
|
|
*/
|
|
|
|
if ( m_wndSecondary )
|
|
m_wndSecondary->Refresh();
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
/*void wxPropertyGrid::CorrectEditorWidgetSizeY( int cy )
|
|
{
|
|
if ( m_selected )
|
|
{
|
|
wxPoint cp(0,cy);
|
|
|
|
if ( m_wndPrimary )
|
|
m_wndPrimary->Move ( m_wndPrimary->GetPosition() + cp );
|
|
|
|
if ( m_wndSecondary )
|
|
m_wndSecondary->Move ( m_wndSecondary->GetPosition() + cp );
|
|
}
|
|
}*/
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// takes scrolling into account
|
|
void wxPropertyGrid::ImprovedClientToScreen( int* px, int* py )
|
|
{
|
|
int vx, vy;
|
|
GetViewStart(&vx,&vy);
|
|
vy*=wxPG_PIXELS_PER_UNIT;
|
|
vx*=wxPG_PIXELS_PER_UNIT;
|
|
*px -= vx;
|
|
*py -= vy;
|
|
ClientToScreen ( px, py );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// custom set cursor
|
|
void wxPropertyGrid::CustomSetCursor( int type, bool override )
|
|
{
|
|
if ( type == m_curcursor && !override ) return;
|
|
|
|
wxCursor* cursor = &wxPG_DEFAULT_CURSOR;
|
|
|
|
if ( type == wxCURSOR_SIZEWE )
|
|
cursor = m_cursorSizeWE;
|
|
|
|
SetCursor ( *cursor );
|
|
|
|
//if ( m_wndPrimary ) m_wndPrimary->SetCursor(wxNullCursor);
|
|
|
|
m_curcursor = type;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid property selection
|
|
// -----------------------------------------------------------------------
|
|
|
|
#define CONNECT_CHILD(EVT,FUNCTYPE,FUNC) \
|
|
wnd->Connect(id, EVT, \
|
|
(wxObjectEventFunction) (wxEventFunction) \
|
|
FUNCTYPE (&wxPropertyGrid::FUNC), \
|
|
NULL, this );
|
|
|
|
/*
|
|
class MyEvtHandler : public wxEvtHandler
|
|
{
|
|
public:
|
|
virtual bool ProcessEvent( wxEvent& event )
|
|
{
|
|
if ( event.GetEventType() == wxEVT_NAVIGATION_KEY )
|
|
wxLogDebug(wxT("wxEVT_NAVIGATION_KEY(id=%i)"),event.GetId());
|
|
else if ( event.GetEventType() == wxEVT_KEY_DOWN )
|
|
wxLogDebug(wxT("wxEVT_KEY_DOWN"));
|
|
event.Skip();
|
|
return wxEvtHandler::ProcessEvent(event);
|
|
}
|
|
};
|
|
*/
|
|
|
|
// Setups event handling for child control
|
|
void wxPropertyGrid::SetupEventHandling( wxWindow* argWnd, int id )
|
|
{
|
|
wxWindow* wnd = argWnd;
|
|
|
|
#if wxPG_ENABLE_CLIPPER_WINDOW
|
|
// Pass real control instead of clipper window
|
|
if ( wnd->IsKindOf(CLASSINFO(wxPGClipperWindow)) )
|
|
{
|
|
wnd = ((wxPGClipperWindow*)argWnd)->GetControl();
|
|
}
|
|
#endif
|
|
|
|
if ( argWnd == m_wndPrimary )
|
|
{
|
|
CONNECT_CHILD(wxEVT_MOTION,(wxMouseEventFunction),OnMouseMoveChild)
|
|
CONNECT_CHILD(wxEVT_LEFT_UP,(wxMouseEventFunction),OnMouseUpChild)
|
|
CONNECT_CHILD(wxEVT_LEFT_DOWN,(wxMouseEventFunction),OnMouseClickChild)
|
|
//CONNECT_CHILD(wxEVT_LEFT_DCLICK,(wxMouseEventFunction),OnMouseClickChild)
|
|
CONNECT_CHILD(wxEVT_RIGHT_UP,(wxMouseEventFunction),OnMouseRightClickChild)
|
|
CONNECT_CHILD(wxEVT_ENTER_WINDOW,(wxMouseEventFunction),OnMouseEntry)
|
|
CONNECT_CHILD(wxEVT_LEAVE_WINDOW,(wxMouseEventFunction),OnMouseEntry)
|
|
}
|
|
else
|
|
{
|
|
CONNECT_CHILD(wxEVT_NAVIGATION_KEY,(wxNavigationKeyEventFunction),OnNavigationKey)
|
|
}
|
|
CONNECT_CHILD(wxEVT_KEY_DOWN,(wxCharEventFunction),OnChildKeyDown)
|
|
CONNECT_CHILD(wxEVT_KEY_UP,(wxCharEventFunction),OnChildKeyUp)
|
|
CONNECT_CHILD(wxEVT_KILL_FOCUS,(wxFocusEventFunction),OnFocusEvent)
|
|
}
|
|
|
|
void wxPropertyGrid::FreeEditors()
|
|
{
|
|
// Do not free editors immediately if processing events
|
|
if ( !m_windowsToDelete )
|
|
m_windowsToDelete = new wxArrayPtrVoid;
|
|
|
|
if ( m_wndSecondary )
|
|
{
|
|
m_windowsToDelete->push_back(m_wndSecondary);
|
|
m_wndSecondary->Hide();
|
|
m_wndSecondary = (wxWindow*) NULL;
|
|
}
|
|
|
|
if ( m_wndPrimary )
|
|
{
|
|
m_windowsToDelete->push_back(m_wndPrimary);
|
|
m_wndPrimary->Hide();
|
|
m_wndPrimary = (wxWindow*) NULL;
|
|
}
|
|
}
|
|
|
|
// Call with NULL to de-select property
|
|
bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags )
|
|
{
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
if (p)
|
|
wxLogDebug(wxT("SelectProperty( %s (%s[%i]) )"),p->m_label.c_str(),
|
|
p->m_parent->m_label.c_str(),p->GetIndexInParent());
|
|
else
|
|
wxLogDebug(wxT("SelectProperty( NULL, -1 )"));
|
|
#endif
|
|
|
|
//
|
|
// Delete windows pending for deletion
|
|
if ( m_windowsToDelete && !m_processingEvent && m_windowsToDelete->size() )
|
|
{
|
|
unsigned int i;
|
|
|
|
for ( i=0; i<m_windowsToDelete->size(); i++ )
|
|
delete ((wxWindow*)((*m_windowsToDelete)[i]));
|
|
|
|
m_windowsToDelete->clear();
|
|
}
|
|
|
|
wxPGProperty* prev = m_selected;
|
|
|
|
//
|
|
// If we are frozen, then just set the values.
|
|
if ( m_frozen )
|
|
{
|
|
m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR);
|
|
m_editorFocused = 0;
|
|
m_selected = p;
|
|
FROM_STATE(m_selected) = p;
|
|
|
|
// If frozen, always free controls. But don't worry, as Thaw will
|
|
// recall SelectProperty to recreate them.
|
|
FreeEditors();
|
|
|
|
// Prevent any further selection measures in this call
|
|
p = (wxPGProperty*) NULL;
|
|
}
|
|
else
|
|
{
|
|
// Is it the same?
|
|
if ( m_selected == p && !(flags & wxPG_SEL_FORCE) )
|
|
{
|
|
// Only set focus if not deselecting
|
|
if ( p )
|
|
{
|
|
if ( flags & wxPG_SEL_FOCUS )
|
|
{
|
|
if ( m_wndPrimary )
|
|
{
|
|
m_wndPrimary->SetFocus();
|
|
m_editorFocused = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wxScrolledWindow::SetFocus();
|
|
m_editorFocused = 0;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
wxClientDC dc(this);
|
|
PrepareDC(dc);
|
|
|
|
// Don't put this earlier, due to return statements
|
|
m_iFlags |= wxPG_FL_IN_SELECT_PROPERTY;
|
|
|
|
//
|
|
// First, deactivate previous
|
|
if ( m_selected )
|
|
{
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug(wxT(" (closing previous (%s))"), m_selected->m_label.c_str() );
|
|
#endif
|
|
|
|
// Must double-check if this is an selected in case of forceswitch
|
|
if ( p != prev )
|
|
{
|
|
if ( !CommitChangesFromEditor(flags) )
|
|
{
|
|
// Validation has failed, so we can't exit the previous editor
|
|
//::wxMessageBox(_("Please correct the value or press ESC to cancel the edit."),
|
|
// _("Invalid Value"),wxOK|wxICON_ERROR);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
FreeEditors();
|
|
|
|
m_iFlags &= ~(wxPG_FL_SELECTED_IS_PAINT_FLEXIBLE|wxPG_FL_SELECTED_IS_FULL_PAINT);
|
|
m_selected = (wxPGProperty*) NULL;
|
|
FROM_STATE(m_selected) = (wxPGProperty*) NULL;
|
|
|
|
// Make sure the previous selection is refreshed
|
|
|
|
// JACS: must use paint handler whenever possible
|
|
Refresh(false);
|
|
|
|
/*if ( m_iFlags & wxPG_FL_ABNORMAL_EDITOR )
|
|
Refresh(false);
|
|
else if ( prev->m_y < (int)m_bottomy )
|
|
DoDrawItems( dc, prev, prev, NULL );
|
|
*/
|
|
|
|
m_iFlags &= ~(wxPG_FL_VALUE_MODIFIED|wxPG_FL_ABNORMAL_EDITOR);
|
|
}
|
|
|
|
//
|
|
// Then, activate the one given.
|
|
if ( p )
|
|
{
|
|
|
|
m_editorFocused = 0;
|
|
m_selected = p;
|
|
FROM_STATE(m_selected) = p;
|
|
m_iFlags |= wxPG_FL_PRIMARY_FILLS_ENTIRE;
|
|
if ( p != prev )
|
|
m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
|
|
|
|
//m_wndPrimary = (wxWindow*) NULL;
|
|
wxASSERT( m_wndPrimary == (wxWindow*) NULL );
|
|
|
|
|
|
// Do we need OnMeasureCalls?
|
|
wxSize imsz = p->GetImageSize();
|
|
if ( imsz.y < -1 )
|
|
m_iFlags |= wxPG_FL_SELECTED_IS_PAINT_FLEXIBLE;
|
|
|
|
// Is the entire cell/row custom painted?
|
|
if ( imsz.x == wxPG_FULL_CUSTOM_PAINT_WIDTH )
|
|
m_iFlags |= wxPG_FL_SELECTED_IS_FULL_PAINT;
|
|
|
|
|
|
//
|
|
// Only create editor for non-disabled non-caption
|
|
if ( p->GetParentingType() <= 0 && !(p->m_flags & wxPG_PROP_DISABLED) )
|
|
{
|
|
// do this for non-caption items
|
|
|
|
// Do we need to paint the custom image, if any?
|
|
m_iFlags &= ~(wxPG_FL_CUR_USES_CUSTOM_IMAGE);
|
|
if ( (p->m_flags & wxPG_PROP_CUSTOMIMAGE) &&
|
|
!p->GetEditorClass()->CanContainCustomImage()
|
|
)
|
|
m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
|
|
|
|
wxRect grect = GetEditorWidgetRect(p);
|
|
wxPoint good_pos = grect.GetPosition();
|
|
#if wxPG_CREATE_CONTROLS_HIDDEN
|
|
int coord_adjust = m_height - good_pos.y;
|
|
good_pos.y += coord_adjust;
|
|
#endif
|
|
|
|
const wxPGEditor* editor = p->GetEditorClass();
|
|
wxCHECK_MSG(editor, false,
|
|
wxT("NULL editor class not allowed"));
|
|
|
|
#ifndef __WXPYTHON__
|
|
m_wndPrimary = editor->CreateControls(this,
|
|
p,
|
|
good_pos,
|
|
grect.GetSize(),
|
|
&m_wndSecondary);
|
|
#else
|
|
wxPGWindowPair wndPair = editor->CreateControls(this,
|
|
p,
|
|
good_pos,
|
|
grect.GetSize());
|
|
m_wndPrimary = wndPair.m_primary;
|
|
m_wndSecondary = wndPair.m_secondary;
|
|
#endif
|
|
|
|
// NOTE: It is allowed for m_wndPrimary to be NULL - in this case
|
|
// value is drawn as normal, and m_wndSecondary is assumed
|
|
// to be a right-aligned button that triggers a separate editor
|
|
// window.
|
|
|
|
if ( m_wndPrimary )
|
|
{
|
|
//wxLogDebug(wxT("%s Editor created for %s"),editor->GetName(),p->GetName().c_str());
|
|
|
|
// Set validator, if any
|
|
/*#if wxUSE_VALIDATORS
|
|
if ( validator ) m_wndPrimary->SetValidator(*validator);
|
|
#endif*/
|
|
|
|
if ( m_wndPrimary->GetSize().y > (m_lineHeight+6) )
|
|
m_iFlags |= wxPG_FL_ABNORMAL_EDITOR;
|
|
|
|
// If it has modified status, use bold font
|
|
// (must be done before capturing m_ctrlXAdjust)
|
|
if ( (p->m_flags & wxPG_PROP_MODIFIED) && (m_windowStyle & wxPG_BOLD_MODIFIED) )
|
|
SetCurControlBoldFont();
|
|
|
|
//
|
|
// Fix TextCtrl indentation
|
|
#if defined(__WXMSW__) && !defined(__WXWINCE__)
|
|
wxTextCtrl* tc = wxDynamicCast(m_wndPrimary, wxTextCtrl);
|
|
if ( tc )
|
|
::SendMessage(GetHwndOf(tc), EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0));
|
|
#endif
|
|
|
|
// Store x relative to splitter (we'll need it).
|
|
m_ctrlXAdjust = m_wndPrimary->GetPosition().x - m_splitterx;
|
|
|
|
// Check if background clear is not necessary
|
|
wxPoint pos = m_wndPrimary->GetPosition();
|
|
if ( pos.x > (m_splitterx+1) || pos.y > p->m_y )
|
|
{
|
|
m_iFlags &= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE);
|
|
}
|
|
|
|
m_wndPrimary->SetSizeHints(3,3);
|
|
|
|
#if wxPG_CREATE_CONTROLS_HIDDEN
|
|
m_wndPrimary->Show(false);
|
|
m_wndPrimary->Freeze();
|
|
|
|
good_pos = m_wndPrimary->GetPosition();
|
|
good_pos.y -= coord_adjust;
|
|
m_wndPrimary->Move( good_pos );
|
|
#endif
|
|
|
|
SetupEventHandling(m_wndPrimary, wxPG_SUBID1);
|
|
|
|
// Focus and select all (wxTextCtrl, wxComboBox etc)
|
|
if ( flags & wxPG_SEL_FOCUS )
|
|
{
|
|
wxWindow* ctrl = m_wndPrimary;
|
|
ctrl->SetFocus();
|
|
|
|
#if wxPG_NAT_TEXTCTRL_BORDER_ANY
|
|
// Take into account textctrl in clipper window
|
|
if ( ctrl->IsKindOf(CLASSINFO(wxPGClipperWindow)) )
|
|
ctrl = ((wxPGClipperWindow*)ctrl)->GetControl();
|
|
#endif
|
|
|
|
p->GetEditorClass()->OnFocus(p,m_wndPrimary);
|
|
}
|
|
}
|
|
|
|
if ( m_wndSecondary )
|
|
{
|
|
|
|
m_wndSecondary->SetSizeHints(3,3);
|
|
|
|
#if wxPG_CREATE_CONTROLS_HIDDEN
|
|
wxRect sec_rect = m_wndSecondary->GetRect();
|
|
sec_rect.y -= coord_adjust;
|
|
|
|
// Fine tuning required to fix "oversized"
|
|
// button disappearance bug.
|
|
if ( sec_rect.y < 0 )
|
|
{
|
|
sec_rect.height += sec_rect.y;
|
|
sec_rect.y = 0;
|
|
}
|
|
m_wndSecondary->SetSize( sec_rect );
|
|
#endif
|
|
m_wndSecondary->Show();
|
|
|
|
SetupEventHandling(m_wndSecondary,wxPG_SUBID2);
|
|
|
|
// If no primary editor, focus to button to allow
|
|
// it to interprete ENTER etc.
|
|
// NOTE: Due to problems focusing away from it, this
|
|
// has been disabled.
|
|
/*
|
|
if ( (flags & wxPG_SEL_FOCUS) && !m_wndPrimary )
|
|
m_wndSecondary->SetFocus();
|
|
*/
|
|
|
|
}
|
|
|
|
if ( flags & wxPG_SEL_FOCUS )
|
|
m_editorFocused = 1;
|
|
|
|
}
|
|
else
|
|
{
|
|
// wxGTK atleast seems to need this (wxMSW not)
|
|
SetFocus();
|
|
}
|
|
|
|
m_iFlags &= ~(wxPG_FL_VALUE_MODIFIED);
|
|
|
|
//Update();
|
|
|
|
// If it's inside collapsed section, expand parent, scroll, etc.
|
|
// Also, if it was partially visible, scroll it into view.
|
|
int vx, vy;
|
|
GetViewStart(&vx,&vy);
|
|
vy*=wxPG_PIXELS_PER_UNIT;
|
|
int vy2 = vy + m_height;
|
|
|
|
if ( (p->m_y < vy ||
|
|
(p->m_y <= vy2 &&
|
|
(p->m_y+m_lineHeight) > vy2)) &&
|
|
!(flags & wxPG_SEL_NONVISIBLE) )
|
|
EnsureVisible( wxPGIdGen(p) );
|
|
|
|
if ( m_wndPrimary )
|
|
{
|
|
// Clear its background
|
|
// (why can't this be optimized by some other drawing?)
|
|
if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) )
|
|
{
|
|
dc.SetPen(*wxTRANSPARENT_PEN);
|
|
dc.SetBrush( wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW) );
|
|
dc.DrawRectangle(m_splitterx+1,p->m_y,
|
|
m_width-m_splitterx,m_lineHeight-1);
|
|
}
|
|
|
|
#if wxPG_CREATE_CONTROLS_HIDDEN
|
|
m_wndPrimary->Thaw();
|
|
#endif
|
|
m_wndPrimary->Show(true);
|
|
}
|
|
|
|
DoDrawItems( dc, p, p, (const wxRect*) NULL );
|
|
|
|
}
|
|
}
|
|
|
|
#if wxUSE_STATUSBAR
|
|
|
|
//
|
|
// Show help text in status bar.
|
|
// (if found and grid not embedded in manager with help box and
|
|
// style wxPG_EX_HELP_AS_TOOLTIPS is not used).
|
|
//
|
|
|
|
if ( !(GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS) )
|
|
{
|
|
wxStatusBar* statusbar = (wxStatusBar*) NULL;
|
|
if ( !(m_iFlags & wxPG_FL_NOSTATUSBARHELP) )
|
|
{
|
|
wxFrame* frame = wxDynamicCast(::wxGetTopLevelParent(this),wxFrame);
|
|
if ( frame )
|
|
statusbar = frame->GetStatusBar();
|
|
}
|
|
|
|
if ( statusbar )
|
|
{
|
|
const wxString* pHelpString = (const wxString*) NULL;
|
|
|
|
if ( p && p->m_dataExt )
|
|
{
|
|
pHelpString = &p->m_dataExt->m_helpString;
|
|
if ( pHelpString->length() )
|
|
{
|
|
// Set help box text.
|
|
statusbar->SetStatusText( *pHelpString );
|
|
m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR;
|
|
}
|
|
}
|
|
|
|
if ( (!pHelpString || !pHelpString->length()) &&
|
|
(m_iFlags & wxPG_FL_STRING_IN_STATUSBAR) )
|
|
{
|
|
// Clear help box - but only if it was written
|
|
// by us at previous time.
|
|
statusbar->SetStatusText( m_emptyString );
|
|
m_iFlags &= ~(wxPG_FL_STRING_IN_STATUSBAR);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
m_iFlags &= ~(wxPG_FL_IN_SELECT_PROPERTY);
|
|
|
|
// call wx event handler (here so that it also occurs on deselection)
|
|
SendEvent( wxEVT_PG_SELECTED, m_selected, flags );
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// This method is not inline because it called dozens of times
|
|
// (i.e. two-arg function calls create smaller code size).
|
|
bool wxPropertyGrid::ClearSelection()
|
|
{
|
|
return DoSelectProperty((wxPGProperty*)NULL);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid expand/collapse state and priority (compact mode) related
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::_Collapse( wxPGProperty* p, bool sendEvents )
|
|
{
|
|
wxCHECK_MSG( p, false, wxT("invalid property id") );
|
|
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
if ( pwc->GetParentingType() == 0 ) return false;
|
|
|
|
if ( !pwc->m_expanded ) return false;
|
|
|
|
// If active editor was inside collapsed section, then disable it
|
|
if ( m_selected && m_selected->IsSomeParent (p) )
|
|
{
|
|
if ( !ClearSelection() )
|
|
return false;
|
|
}
|
|
|
|
// Store dont-center-splitter flag 'cause we need to temporarily set it
|
|
wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
|
|
m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
|
|
|
|
// m_expanded must be set just before call to CalculateYs
|
|
pwc->m_expanded = 0;
|
|
|
|
// Redraw etc. only if collapsed was visible.
|
|
if (pwc->m_y >= 0 &&
|
|
!m_frozen &&
|
|
( pwc->GetParentingType() != 1 || !(m_windowStyle & wxPG_HIDE_CATEGORIES) ) )
|
|
{
|
|
/*int y_adjust = 0;
|
|
|
|
if ( m_selected && m_selected->m_y > pwc->m_y )
|
|
{
|
|
wxPGProperty* next_vis = GetNeighbourItem(pwc,true,1);
|
|
wxASSERT( next_vis );
|
|
|
|
y_adjust = next_vis->m_y - pwc->m_y - m_lineHeight;
|
|
}*/
|
|
|
|
CalculateYs( pwc->m_parent, pwc->m_arrIndex );
|
|
|
|
// Fix control position.
|
|
/*if ( y_adjust )
|
|
CorrectEditorWidgetSizeY ( -y_adjust );*/
|
|
|
|
// When item is collapsed so that scrollbar would move,
|
|
// graphics mess is about (unless we redraw everything).
|
|
Refresh();
|
|
}
|
|
|
|
// Clear dont-center-splitter flag if it wasn't set
|
|
m_iFlags = m_iFlags & ~(wxPG_FL_DONT_CENTER_SPLITTER) | old_flag;
|
|
|
|
if ( sendEvents )
|
|
SendEvent( wxEVT_PG_ITEM_COLLAPSED, p );
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::_Expand( wxPGProperty* p, bool sendEvents )
|
|
{
|
|
wxCHECK_MSG( p, false, wxT("invalid property id") );
|
|
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
if ( pwc->GetParentingType() == 0 ) return false;
|
|
|
|
if ( pwc->m_expanded ) return false;
|
|
|
|
// Store dont-center-splitter flag 'cause we need to temporarily set it
|
|
wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
|
|
m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
|
|
|
|
// m_expanded must be set just before call to CalculateYs
|
|
pwc->m_expanded = 1;
|
|
|
|
// Redraw etc. only if expanded was visible.
|
|
if ( pwc->m_y >= 0 && !m_frozen &&
|
|
( pwc->GetParentingType() != 1 || !(m_windowStyle & wxPG_HIDE_CATEGORIES) )
|
|
)
|
|
{
|
|
CalculateYs( pwc->m_parent, pwc->m_arrIndex );
|
|
|
|
/*int y_adjust = pwc->GetCount()*m_lineHeight;
|
|
|
|
// Fix widget position as well
|
|
if ( m_selected && m_selected->m_y > pwc->m_y )
|
|
CorrectEditorWidgetSizeY ( y_adjust );*/
|
|
|
|
// Redraw
|
|
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
|
|
Refresh();
|
|
#else
|
|
//wxPG_CLIENT_DC_INIT_R(true)
|
|
//DrawItems( dc, pwc->m_y, m_bottomy );
|
|
DrawItems(pwc,(wxPGProperty*) NULL);
|
|
#endif
|
|
}
|
|
|
|
// Clear dont-center-splitter flag if it wasn't set
|
|
m_iFlags = m_iFlags & ~(wxPG_FL_DONT_CENTER_SPLITTER) | old_flag;
|
|
|
|
if ( sendEvents )
|
|
SendEvent( wxEVT_PG_ITEM_EXPANDED, p );
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::Compact( bool compact )
|
|
{
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug( wxT("wxPropertyGrid::Compact()") );
|
|
#endif
|
|
if ( compact )
|
|
{
|
|
if ( !(m_iFlags & wxPG_FL_HIDE_STATE) )
|
|
{
|
|
// Deselect selected if it was hideable
|
|
if ( m_selected && ( m_selected->m_flags & wxPG_PROP_HIDEABLE ) )
|
|
{
|
|
if ( !ClearSelection() )
|
|
return false;
|
|
}
|
|
|
|
m_iFlags |= wxPG_FL_HIDE_STATE;
|
|
|
|
if ( !m_frozen )
|
|
{
|
|
CalculateYs( NULL, -1 );
|
|
RedrawAllVisible();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( m_iFlags & wxPG_FL_HIDE_STATE )
|
|
{
|
|
|
|
m_iFlags &= ~(wxPG_FL_HIDE_STATE);
|
|
|
|
if ( !m_frozen )
|
|
{
|
|
CalculateYs( NULL, -1 );
|
|
RedrawAllVisible();
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Used by HideProperty as well
|
|
bool wxPropertyGrid::SetPropertyPriority( wxPGProperty* p, int priority )
|
|
{
|
|
/*
|
|
// Old code (Commented Aug-09-2007)
|
|
if ( m_frozen )
|
|
return m_pState->SetPropertyPriority(p,priority);
|
|
|
|
if ( (m_iFlags & wxPG_FL_HIDE_STATE) && m_selected &&
|
|
( m_selected == p || m_selected->IsSomeParent(p) )
|
|
)
|
|
{
|
|
if ( !ClearSelection() )
|
|
return false;
|
|
}
|
|
|
|
m_pState->SetPropertyPriority(p,priority);
|
|
|
|
if ( m_iFlags & wxPG_FL_HIDE_STATE )
|
|
{
|
|
CalculateYs(NULL,-1);
|
|
RedrawAllVisible();
|
|
}
|
|
|
|
return true;
|
|
*/
|
|
// Stefan Battmer:
|
|
// Changed in a way that this update is only forced when the
|
|
// properties new priority actually differs from the current
|
|
// priority to improve update speed
|
|
if ( p )
|
|
{
|
|
int oldPriority = ( p->IsFlagSet(wxPG_PROP_HIDEABLE) ) ? wxPG_LOW : wxPG_HIGH;
|
|
if( oldPriority != priority )
|
|
{
|
|
if ( m_frozen )
|
|
return m_pState->SetPropertyPriority(p,priority);
|
|
|
|
if ( (m_iFlags & wxPG_FL_HIDE_STATE) && m_selected &&
|
|
( m_selected == p || m_selected->IsSomeParent(p) )
|
|
)
|
|
{
|
|
if ( !ClearSelection() )
|
|
return false;
|
|
}
|
|
|
|
m_pState->SetPropertyPriority(p,priority);
|
|
if ( m_iFlags & wxPG_FL_HIDE_STATE )
|
|
{
|
|
CalculateYs(NULL,-1);
|
|
RedrawAllVisible();
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid size related methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
// This is called by CalculateYs (so those calling it won't need to call this)
|
|
void wxPropertyGrid::RecalculateVirtualSize()
|
|
{
|
|
|
|
int x = m_width;
|
|
int y = m_bottomy;
|
|
|
|
//SetClientSize(x,y);
|
|
|
|
// Now adjust virtual size.
|
|
SetVirtualSize(x, y);
|
|
|
|
PGAdjustScrollbars(y);
|
|
|
|
//
|
|
// FIXME: Is this really needed? I mean, can't OnResize handle this?
|
|
int width, height;
|
|
GetClientSize(&width,&height);
|
|
|
|
if ( m_selected && width != m_width )
|
|
{
|
|
CorrectEditorWidgetSizeX( m_splitterx, width );
|
|
}
|
|
|
|
m_width = width;
|
|
m_height = height;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::PGAdjustScrollbars( int y )
|
|
{
|
|
// Adjust scrollbars.
|
|
|
|
y += wxPG_PIXELS_PER_UNIT+2; // One more scrollbar unit + 2 pixels.
|
|
int y_amount = y/wxPG_PIXELS_PER_UNIT;
|
|
|
|
int y_pos = GetScrollPos( wxVERTICAL );
|
|
SetScrollbars( 0, wxPG_PIXELS_PER_UNIT, 0,
|
|
y_amount, 0, y_pos, true );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
/*
|
|
bool wxPropertyGrid::DetectScrollbar()
|
|
{
|
|
// Call at every time scrollbar may have appeared/disappeared
|
|
// Returns true if scrollbar was toggled
|
|
|
|
bool toggled = false;
|
|
|
|
// Use functions instead of m_width for total independence
|
|
wxCoord width = GetSize().x;
|
|
wxCoord cwidth = GetClientSize().x;
|
|
|
|
if ( abs(width-cwidth) >= wxPG_MIN_SCROLLBAR_WIDTH )
|
|
{
|
|
// There is a scrollbar.
|
|
if ( !(m_iFlags & wxPG_FL_SCROLLBAR_DETECTED) )
|
|
{
|
|
//wxLogDebug(wxT("Scrollbar Appeared"));
|
|
toggled = true;
|
|
m_iFlags |= wxPG_FL_SCROLLBAR_DETECTED;
|
|
}
|
|
}
|
|
else if ( m_iFlags & wxPG_FL_SCROLLBAR_DETECTED )
|
|
{
|
|
//wxLogDebug(wxT("Scrollbar Disappeared"));
|
|
toggled = true;
|
|
m_iFlags &= ~(wxPG_FL_SCROLLBAR_DETECTED);
|
|
}
|
|
|
|
return toggled;
|
|
}
|
|
*/
|
|
|
|
void wxPropertyGrid::OnResize( wxSizeEvent& event )
|
|
{
|
|
|
|
if ( !(m_iFlags & wxPG_FL_INITIALIZED) )
|
|
return;
|
|
|
|
if ( FROM_STATE(m_itemsAdded) && !m_frozen )
|
|
PrepareAfterItemsAdded();
|
|
|
|
int width, height;
|
|
GetClientSize(&width,&height);
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug(wxT("wxPropertyGrid::OnResize ( %i, %i )"),width,height);
|
|
#endif
|
|
|
|
//int old_width = m_width;
|
|
//int old_height = m_height;
|
|
int old_fwidth = m_fWidth; // non-client width
|
|
int old_splitterx = m_splitterx;
|
|
|
|
int fwidth = event.GetSize().x;
|
|
m_fWidth = fwidth;
|
|
m_width = width;
|
|
m_height = height;
|
|
|
|
int widthDiff = fwidth - old_fwidth;
|
|
|
|
#if wxPG_DOUBLE_BUFFER
|
|
if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
|
|
{
|
|
int dblh = (m_lineHeight*2);
|
|
if ( !m_doubleBuffer )
|
|
{
|
|
// Create double buffer bitmap to draw on, if none
|
|
int w = (width>250)?width:250;
|
|
int h = height + dblh;
|
|
h = (h>400)?h:400;
|
|
m_doubleBuffer = new wxBitmap ( w, h );
|
|
}
|
|
else
|
|
{
|
|
int w = m_doubleBuffer->GetWidth();
|
|
int h = m_doubleBuffer->GetHeight();
|
|
|
|
// Double buffer must be large enough
|
|
if ( w < width || h < (height+dblh) )
|
|
{
|
|
if ( w < width ) w = width;
|
|
if ( h < (height+dblh) ) h = height + dblh;
|
|
delete m_doubleBuffer;
|
|
m_doubleBuffer = new wxBitmap ( w, h );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Consider full update on every resize
|
|
//m_iFlags |= wxPG_FL_CHANGED;
|
|
|
|
#endif
|
|
|
|
//
|
|
// Center splitter when...
|
|
// * always when propGrid not shown yet or its full size is not realized yet
|
|
// and then only if splitter's position was not pre-set
|
|
// * auto-centering is enabled and scrollbar was not toggled
|
|
//
|
|
|
|
// Need to center splitter?
|
|
//if ( width!=old_width )
|
|
{
|
|
bool needSplitterCheck = true;
|
|
|
|
//if ( !sb_vis_toggled )
|
|
{
|
|
if ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER )
|
|
{
|
|
float centerX = float(width) * 0.5;
|
|
|
|
float splitterX = m_fSplitterX + (float(widthDiff) * 0.5);
|
|
|
|
float deviation = fabs(centerX - splitterX);
|
|
//wxLogDebug(wxT("deviation: %.1f"),deviation);
|
|
|
|
// If deviated too far from the center, reset it
|
|
if ( deviation > 30.0 )
|
|
splitterX = centerX;
|
|
|
|
DoSetSplitterPosition( (int)splitterX, false );
|
|
|
|
m_fSplitterX = splitterX; // needed to retain accuracy
|
|
|
|
needSplitterCheck = false;
|
|
}
|
|
else if ( !(m_iFlags & wxPG_FL_SPLITTER_PRE_SET) )
|
|
{
|
|
long timeSinceCreation = (::wxGetLocalTimeMillis() - m_timeCreated).ToLong();
|
|
|
|
if ( m_pState->m_properties->GetCount() || timeSinceCreation > 750 )
|
|
{
|
|
SetSplitterLeft( false );
|
|
needSplitterCheck = false;
|
|
}
|
|
else
|
|
{
|
|
DoSetSplitterPosition( width / 2, false );
|
|
m_iFlags &= ~(wxPG_FL_SPLITTER_PRE_SET);
|
|
needSplitterCheck = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( needSplitterCheck && (m_splitterx + wxPG_DRAG_MARGIN) > width )
|
|
{
|
|
long timeSinceCreation = (::wxGetLocalTimeMillis() - m_timeCreated).ToLong();
|
|
|
|
if ( timeSinceCreation >= 750 )
|
|
{
|
|
DoSetSplitterPosition( width - wxPG_DRAG_MARGIN - 1, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Need to correct widget position?
|
|
if ( m_selected /*&& (width != old_width || sb_vis_toggled)*/ )
|
|
{
|
|
// Take splitter position change into account
|
|
CorrectEditorWidgetSizeX( m_splitterx, width );
|
|
}
|
|
|
|
if ( !m_frozen )
|
|
{
|
|
|
|
// Need to recalculate visibles array?
|
|
//if ( height != old_height )
|
|
if ( height > m_calcVisHeight )
|
|
CalculateVisibles( -1, false );
|
|
|
|
/*if ( sb_vis_toggled )
|
|
{
|
|
Refresh();
|
|
}
|
|
else*/
|
|
if ( m_splitterx != old_splitterx )
|
|
{
|
|
Refresh();
|
|
/*if ( abs(height-old_height) < 100 )
|
|
{
|
|
Update(); // Necessary, atleast on wxMSW
|
|
RedrawAllVisible();
|
|
}
|
|
else
|
|
{
|
|
Refresh();
|
|
}*/
|
|
}
|
|
}
|
|
|
|
// Without this, virtual size (atleast under wxGTK) will be skewed
|
|
RecalculateVirtualSize();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid mouse event handling
|
|
// -----------------------------------------------------------------------
|
|
|
|
// selFlags uses same values DoSelectProperty's flags
|
|
void wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p, unsigned int selFlags )
|
|
{
|
|
// Send property grid event of specific type and with specific property
|
|
wxPropertyGridEvent evt( eventType, GetId() );
|
|
evt.SetPropertyGrid(this);
|
|
evt.SetEventObject(m_eventObject);
|
|
evt.SetProperty(p);
|
|
wxEvtHandler* evtHandler = GetEventHandler();
|
|
|
|
// Always need to process event immediately if the property in question is
|
|
// about to be deleted.
|
|
if ( (selFlags & wxPG_SEL_DELETING) ||
|
|
(GetExtraStyle() & wxPG_EX_PROCESS_EVENTS_IMMEDIATELY) )
|
|
{
|
|
evtHandler->ProcessEvent(evt);
|
|
}
|
|
else
|
|
{
|
|
evt.SetPending(true);
|
|
evtHandler->AddPendingEvent(evt);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Return false if should be skipped
|
|
bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &event )
|
|
{
|
|
bool res = true;
|
|
|
|
#if __MOUSE_DEBUGGING__
|
|
wxLogDebug( wxT(" \\--> HandleMouseClick") );
|
|
#endif
|
|
|
|
// Need to set focus?
|
|
if ( !(m_iFlags & wxPG_FL_FOCUSED) )
|
|
{
|
|
SetFocus();
|
|
}
|
|
|
|
if ( y < m_bottomy )
|
|
{
|
|
|
|
wxPGProperty* p = DoGetItemAtY(y);
|
|
|
|
if ( p )
|
|
{
|
|
int parenting = p->GetParentingType();
|
|
int depth = (int)p->GetDepth() - 1;
|
|
|
|
int marginEnds = m_marginWidth + ( depth * m_subgroup_extramargin );
|
|
|
|
if ( x >= marginEnds )
|
|
{
|
|
// Outside margin.
|
|
|
|
if ( parenting > 0 )
|
|
{
|
|
// This is category.
|
|
wxPropertyCategoryClass* pwc = (wxPropertyCategoryClass*)p;
|
|
|
|
int text_x = m_marginWidth + ((unsigned int)((pwc->m_depth-1)*m_subgroup_extramargin));
|
|
|
|
// Expand, collapse, activate etc. if click on text or left of splitter.
|
|
if ( x >= text_x
|
|
&&
|
|
( x < (text_x+pwc->GetTextExtent()+(wxPG_CAPRECTXMARGIN*2))
|
|
||
|
|
x < m_splitterx
|
|
)
|
|
)
|
|
{
|
|
if ( !DoSelectProperty( p ) )
|
|
return res;
|
|
|
|
// On double-click, expand/collapse.
|
|
if ( event.ButtonDClick() && !(m_windowStyle & wxPG_HIDE_MARGIN) )
|
|
{
|
|
if ( pwc->m_expanded ) _Collapse ( p, true );
|
|
else _Expand ( p, true );
|
|
}
|
|
}
|
|
}
|
|
else if ( x > (m_splitterx + wxPG_SPLITTERX_DETECTMARGIN2) ||
|
|
x < (m_splitterx - wxPG_SPLITTERX_DETECTMARGIN1) )
|
|
{
|
|
// Click on value.
|
|
unsigned int selFlag = 0;
|
|
if ( x > m_splitterx )
|
|
{
|
|
m_iFlags |= wxPG_FL_ACTIVATION_BY_CLICK;
|
|
selFlag = wxPG_SEL_FOCUS;
|
|
}
|
|
if ( !DoSelectProperty( p, selFlag ) )
|
|
return res;
|
|
|
|
m_iFlags &= ~(wxPG_FL_ACTIVATION_BY_CLICK);
|
|
|
|
if ( p->GetParentingType() < 0 )
|
|
// On double-click, expand/collapse.
|
|
if ( event.ButtonDClick() && !(m_windowStyle & wxPG_HIDE_MARGIN) )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
if ( pwc->m_expanded ) _Collapse ( p, true );
|
|
else _Expand ( p, true );
|
|
}
|
|
|
|
res = false;
|
|
}
|
|
else
|
|
{
|
|
// click on splitter
|
|
if ( !(m_windowStyle & wxPG_STATIC_SPLITTER) )
|
|
{
|
|
|
|
if ( event.GetEventType() == wxEVT_LEFT_DCLICK )
|
|
{
|
|
// Double-clicking the splitter causes auto-centering
|
|
CenterSplitter( true );
|
|
// TODO: Would this be more natural?
|
|
// .NET grid doesn't do it but maybe we should.
|
|
//CustomSetCursor ( wxCURSOR_ARROW );
|
|
}
|
|
else if ( m_dragStatus == 0 )
|
|
{
|
|
//
|
|
// Begin draggin the splitter
|
|
//
|
|
#if __MOUSE_DEBUGGING__
|
|
wxLogDebug( wxT(" dragging begins at splitter + %i"),
|
|
(int)(x - m_splitterx) );
|
|
#endif
|
|
|
|
if ( m_wndPrimary )
|
|
{
|
|
// Changes must be committed here or the
|
|
// value won't be drawn correctly
|
|
if ( !CommitChangesFromEditor() )
|
|
return res;
|
|
|
|
m_wndPrimary->Show ( false );
|
|
}
|
|
|
|
BEGIN_MOUSE_CAPTURE
|
|
|
|
m_dragStatus = 1;
|
|
|
|
m_dragOffset = x - m_splitterx;
|
|
|
|
wxPG_CLIENT_DC_INIT()
|
|
|
|
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
|
|
// Fixes button disappearance bug
|
|
if ( m_wndSecondary )
|
|
m_wndSecondary->Show ( false );
|
|
#endif
|
|
|
|
m_startingSplitterX = m_splitterx;
|
|
#if wxPG_HEAVY_GFX
|
|
#else
|
|
Update(); // clear graphics mess
|
|
DrawSplitterDragColumn( dc, m_splitterx );
|
|
m_splitterprevdrawnx = m_splitterx;
|
|
#endif
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Click on margin.
|
|
if ( parenting != 0 )
|
|
{
|
|
int nx = x + m_marginWidth - marginEnds; // Normalize x.
|
|
|
|
if ( (nx >= m_gutterWidth && nx < (m_gutterWidth+m_iconWidth)) )
|
|
{
|
|
int y2 = y - p->m_y;
|
|
if ( (y2 >= m_buttonSpacingY && y2 < (m_buttonSpacingY+m_iconHeight)) )
|
|
{
|
|
// On click on expander button, expand/collapse
|
|
if ( ((wxPGPropertyWithChildren*)p)->m_expanded )
|
|
_Collapse ( p, true );
|
|
else
|
|
_Expand ( p, true );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x), unsigned int y,
|
|
wxMouseEvent& WXUNUSED(event) )
|
|
{
|
|
if ( y < m_bottomy )
|
|
{
|
|
// Select property here as well
|
|
wxPGProperty* p = m_propHover;
|
|
if ( p != m_selected )
|
|
DoSelectProperty( p );
|
|
|
|
// Send right click event.
|
|
SendEvent( wxEVT_PG_RIGHT_CLICK, p );
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x), unsigned int y,
|
|
wxMouseEvent& WXUNUSED(event) )
|
|
{
|
|
if ( y < m_bottomy )
|
|
{
|
|
// Select property here as well
|
|
wxPGProperty* p = m_propHover;
|
|
|
|
if ( p != m_selected )
|
|
DoSelectProperty( p );
|
|
|
|
// Send double-click event.
|
|
SendEvent( wxEVT_PG_DOUBLE_CLICK, m_propHover );
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
/*
|
|
// Splits text into lines so that each will have width less than arg maxWidth.
|
|
// * Returns string with line breaks inserted into appropriate positions.
|
|
// * Keeps words together.
|
|
// * Useful in conjunction with wxWindow::SetToolTip and wxDC::DrawLabel.
|
|
static wxString SplitTextByPixelWidth(wxDC& dc, const wxString& text, int lineWidth)
|
|
{
|
|
if ( !text.length() )
|
|
return text;
|
|
|
|
wxString resultLine;
|
|
wxArrayInt extents;
|
|
|
|
unsigned int index = 0;
|
|
unsigned int maxIndex = text.length() - 1;
|
|
unsigned int prevSplitIndex = 0;
|
|
unsigned int prevCanSplitIndex = 0;
|
|
int lineCheckWidth = lineWidth;
|
|
wxChar prevA = wxT('\0');
|
|
|
|
dc.GetPartialTextExtents(text,extents);
|
|
|
|
wxASSERT( text.length() == extents.GetCount() );
|
|
|
|
while ( index <= maxIndex )
|
|
{
|
|
const wxChar A = text[index];
|
|
|
|
if ( !wxIsalnum(prevA) )
|
|
{
|
|
// Can split here
|
|
prevCanSplitIndex = index;
|
|
}
|
|
else
|
|
{
|
|
// Can't split here
|
|
}
|
|
|
|
if ( ( (extents[index] >= lineCheckWidth || A == wxT('\n')) &&
|
|
index > prevCanSplitIndex ) ||
|
|
index == maxIndex )
|
|
{
|
|
// Need to split now
|
|
|
|
unsigned int useSplit = prevCanSplitIndex;
|
|
if ( useSplit <= prevSplitIndex ||
|
|
index >= maxIndex )
|
|
useSplit = index;
|
|
|
|
resultLine << text.Mid(prevSplitIndex,useSplit-prevSplitIndex);
|
|
|
|
if ( index >= maxIndex )
|
|
break;
|
|
else
|
|
if ( A != wxT('\n') )
|
|
{
|
|
resultLine.Append(_T("\n"));
|
|
//resultLine.Append(text.Mid(useSplit,text.length()-useSplit));
|
|
//break;
|
|
}
|
|
prevSplitIndex = useSplit;
|
|
lineCheckWidth = extents[useSplit] + lineWidth;
|
|
//widSum = 0;
|
|
index = useSplit;
|
|
prevA = wxT('\0');
|
|
}
|
|
else
|
|
{
|
|
index++;
|
|
prevA = A;
|
|
}
|
|
}
|
|
|
|
return resultLine;
|
|
}
|
|
*/
|
|
// -----------------------------------------------------------------------
|
|
|
|
#if wxPG_SUPPORT_TOOLTIPS
|
|
|
|
void wxPropertyGrid::SetToolTip( const wxString& tipString )
|
|
{
|
|
if ( tipString.length() )
|
|
{
|
|
//wxClientDC dc(this);
|
|
//wxString finalString = SplitTextByPixelWidth(dc,tipString,350);
|
|
//wxScrolledWindow::SetToolTip(finalString);
|
|
wxScrolledWindow::SetToolTip(tipString);
|
|
}
|
|
else
|
|
{
|
|
#if wxPG_ALLOW_EMPTY_TOOLTIPS
|
|
wxScrolledWindow::SetToolTip( m_emptyString );
|
|
#else
|
|
wxScrolledWindow::SetToolTip( NULL );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#endif // #if wxPG_SUPPORT_TOOLTIPS
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Return false if should be skipped
|
|
bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event )
|
|
{
|
|
// Safety check (needed because mouse capturing may
|
|
// otherwise freeze the control)
|
|
if ( m_dragStatus > 0 && !event.Dragging() )
|
|
{
|
|
//wxLogDebug(wxT("MOUSE CAPTURE SAFETY RELEASE TRIGGERED"));
|
|
HandleMouseUp(x,y,event);
|
|
}
|
|
|
|
if ( m_dragStatus > 0 )
|
|
{
|
|
|
|
if ( x > (m_marginWidth + wxPG_DRAG_MARGIN) &&
|
|
x < (m_width - wxPG_DRAG_MARGIN) )
|
|
{
|
|
|
|
#if wxPG_HEAVY_GFX
|
|
|
|
int new_splitterx = x - m_dragOffset;
|
|
|
|
// Splitter redraw required?
|
|
if ( new_splitterx != m_splitterx )
|
|
{
|
|
|
|
if ( m_selected )
|
|
CorrectEditorWidgetSizeX( new_splitterx, m_width );
|
|
|
|
// Move everything
|
|
m_splitterx = new_splitterx;
|
|
m_fSplitterX = (float) new_splitterx;
|
|
|
|
Update();
|
|
RedrawAllVisible();
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if ( x != m_splitterx )
|
|
{
|
|
wxPG_CLIENT_DC_INIT_R(false)
|
|
|
|
if ( m_splitterprevdrawnx != -1 )
|
|
DrawSplitterDragColumn( dc, m_splitterprevdrawnx );
|
|
|
|
m_splitterx = x;
|
|
m_fSplitterX = (float) x;
|
|
|
|
DrawSplitterDragColumn( dc, x );
|
|
|
|
m_splitterprevdrawnx = x;
|
|
}
|
|
|
|
#endif
|
|
|
|
m_dragStatus = 2;
|
|
|
|
}
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
|
|
int ih = m_lineHeight;
|
|
int sy = y;
|
|
|
|
#if wxPG_SUPPORT_TOOLTIPS
|
|
wxPGProperty* prevHover = m_propHover;
|
|
unsigned char prevSide = m_mouseSide;
|
|
#endif
|
|
|
|
// On which item it hovers
|
|
if ( ( !m_propHover && y < m_bottomy)
|
|
||
|
|
( m_propHover && ( sy < m_propHover->m_y || sy >= (m_propHover->m_y+ih) ) )
|
|
)
|
|
{
|
|
// Mouse moves on another property
|
|
|
|
m_propHover = DoGetItemAtY(y);
|
|
|
|
// Send hover event
|
|
SendEvent( wxEVT_PG_HIGHLIGHTED, m_propHover );
|
|
}
|
|
|
|
#if wxPG_SUPPORT_TOOLTIPS
|
|
// Store which side we are on
|
|
m_mouseSide = 0;
|
|
if ( x >= m_splitterx )
|
|
m_mouseSide = 2;
|
|
else if ( x >= m_marginWidth )
|
|
m_mouseSide = 1;
|
|
|
|
//
|
|
// If tooltips are enabled, show label or value as a tip
|
|
// in case it doesn't otherwise show in full length.
|
|
//
|
|
if ( m_windowStyle & wxPG_TOOLTIPS )
|
|
{
|
|
wxToolTip* tooltip = GetToolTip();
|
|
|
|
if ( m_propHover != prevHover || prevSide != m_mouseSide )
|
|
{
|
|
if ( m_propHover && m_propHover->GetParentingType() <= 0 )
|
|
{
|
|
|
|
if ( GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS )
|
|
{
|
|
// Show help string as a tooltip
|
|
wxString tipString = m_propHover->GetHelpString();
|
|
|
|
SetToolTip(tipString);
|
|
}
|
|
else
|
|
{
|
|
// Show cropped value string as a tooltip
|
|
wxString tipString;
|
|
int space = 0;
|
|
|
|
if ( m_mouseSide == 1 )
|
|
{
|
|
tipString = m_propHover->m_label;
|
|
space = m_splitterx-m_marginWidth-3;
|
|
}
|
|
else if ( m_mouseSide == 2 )
|
|
{
|
|
tipString = m_propHover->GetDisplayedString();
|
|
|
|
space = m_width - m_splitterx;
|
|
if ( m_propHover->m_flags & wxPG_PROP_CUSTOMIMAGE )
|
|
space -= wxPG_CUSTOM_IMAGE_WIDTH + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2;
|
|
}
|
|
|
|
if ( space )
|
|
{
|
|
int tw, th;
|
|
GetTextExtent( tipString, &tw, &th, 0, 0, &m_font );
|
|
if ( tw > space )
|
|
{
|
|
SetToolTip( tipString );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( tooltip )
|
|
{
|
|
#if wxPG_ALLOW_EMPTY_TOOLTIPS
|
|
wxScrolledWindow::SetToolTip( m_emptyString );
|
|
#else
|
|
wxScrolledWindow::SetToolTip( NULL );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( tooltip )
|
|
{
|
|
#if wxPG_ALLOW_EMPTY_TOOLTIPS
|
|
wxScrolledWindow::SetToolTip( m_emptyString );
|
|
#else
|
|
wxScrolledWindow::SetToolTip( NULL );
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ( x > (m_splitterx + wxPG_SPLITTERX_DETECTMARGIN2) ||
|
|
x < (m_splitterx - wxPG_SPLITTERX_DETECTMARGIN1) ||
|
|
y >= m_bottomy ||
|
|
(m_windowStyle & wxPG_STATIC_SPLITTER) )
|
|
{
|
|
// hovering on something else
|
|
if ( m_curcursor != wxCURSOR_ARROW )
|
|
CustomSetCursor( wxCURSOR_ARROW );
|
|
}
|
|
else
|
|
{
|
|
// Do not allow splitter cursor on caption items.
|
|
// (also not if we were dragging and its started
|
|
// outside the splitter region)
|
|
|
|
if ( m_propHover &&
|
|
m_propHover->GetParentingType() <= 0 &&
|
|
!event.Dragging() )
|
|
{
|
|
|
|
// hovering on splitter
|
|
|
|
// NB: Condition disabled since MouseLeave event (from the editor control) cannot be
|
|
// reliably detected.
|
|
//if ( m_curcursor != wxCURSOR_SIZEWE )
|
|
CustomSetCursor( wxCURSOR_SIZEWE, true );
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// hovering on something else
|
|
if ( m_curcursor != wxCURSOR_ARROW )
|
|
CustomSetCursor( wxCURSOR_ARROW );
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Also handles Leaving event
|
|
bool wxPropertyGrid::HandleMouseUp( int x, unsigned int y, wxMouseEvent &WXUNUSED(event) )
|
|
{
|
|
bool res = false;
|
|
|
|
#if __MOUSE_DEBUGGING__
|
|
wxLogDebug( wxT(" \\--> HandleMouseUp") );
|
|
#endif
|
|
|
|
// No event type check - basicly calling this method should
|
|
// just stop dragging.
|
|
//if( event.LeftUp() || event.Leaving() )
|
|
//{
|
|
// Left up after dragged?
|
|
if ( m_dragStatus >= 1 )
|
|
{
|
|
//
|
|
// End Splitter Dragging
|
|
//
|
|
#if __MOUSE_DEBUGGING__
|
|
wxLogDebug( wxT(" dragging ends") );
|
|
#endif
|
|
|
|
// DO NOT ENABLE FOLLOWING LINE!
|
|
// (it is only here as a reminder to not to do it)
|
|
//m_splitterx = x;
|
|
|
|
#if wxPG_HEAVY_GFX
|
|
//Refresh();
|
|
#else
|
|
DoSetSplitterPosition( -1 ); // -1 tells not to make change
|
|
|
|
// Hack to clear-up editor graphics mess (on wxMSW, atleast)
|
|
if ( m_selected )
|
|
DrawItem ( m_selected );
|
|
|
|
#endif
|
|
// Disable splitter auto-centering
|
|
m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
|
|
|
|
// This is necessary to return cursor
|
|
END_MOUSE_CAPTURE
|
|
|
|
// Set back the default cursor, if necessary
|
|
if ( x > (m_splitterx + wxPG_SPLITTERX_DETECTMARGIN2) ||
|
|
x < (m_splitterx - wxPG_SPLITTERX_DETECTMARGIN1) ||
|
|
y >= m_bottomy )
|
|
{
|
|
CustomSetCursor( wxCURSOR_ARROW );
|
|
}
|
|
|
|
m_dragStatus = 0;
|
|
|
|
#if wxPG_HEAVY_GFX
|
|
// Control background needs to be cleared
|
|
if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) && m_selected )
|
|
DrawItem ( m_selected );
|
|
#endif
|
|
|
|
if ( m_wndPrimary )
|
|
{
|
|
m_wndPrimary->Show ( true );
|
|
}
|
|
|
|
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
|
|
// Fixes button disappearance bug
|
|
if ( m_wndSecondary )
|
|
m_wndSecondary->Show ( true );
|
|
#endif
|
|
|
|
// This clears the focus.
|
|
m_editorFocused = 0;
|
|
|
|
}
|
|
//}
|
|
return res;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::OnMouseCommon( wxMouseEvent& event, int* px, int* py )
|
|
{
|
|
int ux, uy;
|
|
CalcUnscrolledPosition( event.m_x, event.m_y, &ux, &uy );
|
|
|
|
// Hide popup on clicks
|
|
// FIXME: Not necessary after transient window implemented
|
|
if ( event.GetEventType() != wxEVT_MOTION )
|
|
if ( m_wndPrimary && m_wndPrimary->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
|
|
{
|
|
((wxPGOwnerDrawnComboBox*)m_wndPrimary)->HidePopup();
|
|
}
|
|
|
|
//if (printmsg) wxLogDebug( wxT("On") wxT(#func) wxT("( %i, %i )"),(int)ux,(int)uy );
|
|
wxRect r;
|
|
wxWindow* wnd = m_wndPrimary;
|
|
if ( wnd )
|
|
r = wnd->GetRect();
|
|
if ( wnd == (wxWindow*) NULL || m_dragStatus ||
|
|
(
|
|
ux <= (m_splitterx + wxPG_SPLITTERX_DETECTMARGIN2) ||
|
|
event.m_y < r.y ||
|
|
event.m_y >= (r.y+r.height)
|
|
)
|
|
)
|
|
{
|
|
*px = ux;
|
|
*py = uy;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if ( m_curcursor != wxCURSOR_ARROW ) CustomSetCursor ( wxCURSOR_ARROW );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnMouseClick( wxMouseEvent &event )
|
|
{
|
|
int x, y;
|
|
if ( OnMouseCommon( event, &x, &y ) )
|
|
{
|
|
HandleMouseClick(x,y,event);
|
|
}
|
|
event.Skip();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnMouseRightClick( wxMouseEvent &event )
|
|
{
|
|
int x, y;
|
|
CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
|
|
HandleMouseRightClick(x,y,event);
|
|
event.Skip();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnMouseDoubleClick( wxMouseEvent &event )
|
|
{
|
|
// Always run standard mouse-down handler as well
|
|
OnMouseClick(event);
|
|
|
|
int x, y;
|
|
CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
|
|
HandleMouseDoubleClick(x,y,event);
|
|
event.Skip();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnMouseMove( wxMouseEvent &event )
|
|
{
|
|
int x, y;
|
|
if ( OnMouseCommon ( event, &x, &y ) )
|
|
{
|
|
HandleMouseMove(x,y,event);
|
|
}
|
|
event.Skip();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnMouseUp( wxMouseEvent &event )
|
|
{
|
|
int x, y;
|
|
if ( OnMouseCommon ( event, &x, &y ) )
|
|
{
|
|
HandleMouseUp(x,y,event);
|
|
}
|
|
event.Skip();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnMouseEntry( wxMouseEvent &event )
|
|
{
|
|
// This may get called from child control as well, so event's
|
|
// mouse position cannot be relied on.
|
|
//int x = event.m_x;
|
|
//int y = event.m_y;
|
|
|
|
if ( event.Entering() )
|
|
{
|
|
if ( !(m_iFlags & wxPG_FL_MOUSE_INSIDE) )
|
|
{
|
|
#if __MOUSE_DEBUGGING__
|
|
wxLogDebug(wxT("Mouse Enters Window"));
|
|
#endif
|
|
//SetCursor ( *wxSTANDARD_CURSOR );
|
|
// TODO: Fix this (detect parent and only do
|
|
// cursor trick if it is a manager).
|
|
wxASSERT( GetParent() );
|
|
GetParent()->SetCursor(wxNullCursor);
|
|
|
|
m_iFlags |= wxPG_FL_MOUSE_INSIDE;
|
|
//if ( m_wndPrimary ) m_wndPrimary->Show ( true );
|
|
}
|
|
else
|
|
GetParent()->SetCursor(wxNullCursor);
|
|
}
|
|
else if ( event.Leaving() )
|
|
{
|
|
// Without this, wxSpinCtrl editor will sometimes have wrong cursor
|
|
SetCursor( wxNullCursor );
|
|
|
|
// Get real cursor position
|
|
wxPoint pt = ScreenToClient(::wxGetMousePosition());
|
|
|
|
if ( ( pt.x <= 0 || pt.y <= 0 || pt.x >= m_width || pt.y >= m_height ) )
|
|
{
|
|
//if ( CommitChangesFromEditor() )
|
|
{
|
|
|
|
if ( (m_iFlags & wxPG_FL_MOUSE_INSIDE) )
|
|
{
|
|
#if __MOUSE_DEBUGGING__
|
|
wxLogDebug(wxT("Mouse Leaves Window"));
|
|
#endif
|
|
m_iFlags &= ~(wxPG_FL_MOUSE_INSIDE);
|
|
//if ( m_wndPrimary ) m_wndPrimary->Show ( false );
|
|
}
|
|
|
|
if ( m_dragStatus )
|
|
wxPropertyGrid::HandleMouseUp ( -1, 10000, event );
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*#if wxPG_NO_CHILD_EVT_MOTION
|
|
// cursor must be reset because EVT_MOTION handler is not there to do it
|
|
if ( m_curcursor != wxCURSOR_ARROW ) CustomSetCursor ( wxCURSOR_ARROW );
|
|
#endif*/
|
|
}
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// if (printmsg) wxLogDebug( wxT("On") wxT(#func) wxT("Child ( %i, %i )"),(int)event.m_x,(int)event.m_y );
|
|
|
|
// Common code used by various OnMouseXXXChild methods.
|
|
bool wxPropertyGrid::OnMouseChildCommon( wxMouseEvent &event, int* px, int *py )
|
|
{
|
|
wxWindow* topCtrlWnd = (wxWindow*)event.GetEventObject();
|
|
wxASSERT( topCtrlWnd );
|
|
int x, y;
|
|
event.GetPosition(&x,&y);
|
|
|
|
#if wxPG_ENABLE_CLIPPER_WINDOW
|
|
// Take clipper window into account
|
|
if (topCtrlWnd->GetPosition().x < 1 &&
|
|
!topCtrlWnd->IsKindOf(CLASSINFO(wxPGClipperWindow)))
|
|
{
|
|
topCtrlWnd = topCtrlWnd->GetParent();
|
|
wxASSERT( topCtrlWnd->IsKindOf(CLASSINFO(wxPGClipperWindow)) );
|
|
x -= ((wxPGClipperWindow*)topCtrlWnd)->GetXClip();
|
|
y -= ((wxPGClipperWindow*)topCtrlWnd)->GetYClip();
|
|
}
|
|
#endif
|
|
|
|
wxRect r = topCtrlWnd->GetRect();
|
|
if ( !m_dragStatus &&
|
|
x > (m_splitterx-r.x+wxPG_SPLITTERX_DETECTMARGIN2) &&
|
|
y >= 0 && y < r.height \
|
|
)
|
|
{
|
|
if ( m_curcursor != wxCURSOR_ARROW ) CustomSetCursor ( wxCURSOR_ARROW );
|
|
event.Skip();
|
|
}
|
|
else
|
|
{
|
|
CalcUnscrolledPosition( event.m_x + r.x, event.m_y + r.y, \
|
|
px, py );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*void wxPropertyGrid::OnMouseEntryChild ( wxMouseEvent &event )
|
|
{
|
|
wxLogDebug(wxT("Entering/Leaving Child..."));
|
|
event.Skip();
|
|
}*/
|
|
|
|
void wxPropertyGrid::OnMouseClickChild( wxMouseEvent &event )
|
|
{
|
|
int x,y;
|
|
if ( OnMouseChildCommon(event,&x,&y) )
|
|
{
|
|
bool res = HandleMouseClick(x,y,event);
|
|
if ( !res ) event.Skip();
|
|
|
|
/*if ( event.GetEventType() == wxEVT_LEFT_DCLICK )
|
|
{
|
|
HandleMouseDoubleClick( x, y, event );
|
|
event.Skip();
|
|
}*/
|
|
}
|
|
}
|
|
|
|
void wxPropertyGrid::OnMouseRightClickChild( wxMouseEvent &event )
|
|
{
|
|
int x,y;
|
|
wxASSERT( m_wndPrimary );
|
|
// These coords may not be exact (about +-2),
|
|
// but that should not matter (right click is about item, not position).
|
|
wxPoint pt = m_wndPrimary->GetPosition();
|
|
CalcUnscrolledPosition( event.m_x + pt.x, event.m_y + pt.y, &x, &y );
|
|
wxASSERT( m_selected );
|
|
m_propHover = m_selected;
|
|
bool res = HandleMouseRightClick(x,y,event);
|
|
if ( !res ) event.Skip();
|
|
}
|
|
|
|
void wxPropertyGrid::OnMouseMoveChild( wxMouseEvent &event )
|
|
{
|
|
int x,y;
|
|
if ( OnMouseChildCommon(event,&x,&y) )
|
|
{
|
|
bool res = HandleMouseMove(x,y,event);
|
|
if ( !res ) event.Skip();
|
|
}
|
|
}
|
|
|
|
void wxPropertyGrid::OnMouseUpChild( wxMouseEvent &event )
|
|
{
|
|
int x,y;
|
|
if ( OnMouseChildCommon(event,&x,&y) )
|
|
{
|
|
bool res = HandleMouseUp(x,y,event);
|
|
if ( !res ) event.Skip();
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid keyboard event handling
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SendNavigationKeyEvent( int dir )
|
|
{
|
|
wxNavigationKeyEvent evt;
|
|
evt.SetFlags(wxNavigationKeyEvent::FromTab|
|
|
(dir?wxNavigationKeyEvent::IsForward:
|
|
wxNavigationKeyEvent::IsBackward));
|
|
evt.SetEventObject(this);
|
|
GetEventHandler()->AddPendingEvent(evt);
|
|
}
|
|
|
|
void wxPropertyGrid::HandleKeyEvent(wxKeyEvent &event)
|
|
{
|
|
|
|
//
|
|
// Handles key event when editor control is not focused.
|
|
//
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug( wxT("wxPropertyGrid::HandleKeyEvent(%i)"),(int)event.GetKeyCode() );
|
|
#endif
|
|
|
|
wxASSERT( !m_frozen );
|
|
if ( m_frozen )
|
|
return;
|
|
|
|
// Travelsal between items, collapsing/expanding, etc.
|
|
int keycode = event.GetKeyCode();
|
|
|
|
if ( keycode == WXK_TAB )
|
|
{
|
|
SendNavigationKeyEvent( event.ShiftDown()?0:1 );
|
|
return;
|
|
}
|
|
|
|
// Ignore Alt and Control when they are down alone
|
|
if ( keycode == WXK_ALT ||
|
|
keycode == WXK_CONTROL )
|
|
{
|
|
event.Skip();
|
|
return;
|
|
}
|
|
|
|
if ( m_selected )
|
|
{
|
|
|
|
// Show dialog?
|
|
if ( ButtonTriggerKeyTest(event) )
|
|
return;
|
|
|
|
wxPGProperty* p = m_selected;
|
|
|
|
int selectDir = -2;
|
|
|
|
if ( p->GetParentingType() != 0 &&
|
|
!(p->m_flags & wxPG_PROP_DISABLED)
|
|
)
|
|
{
|
|
//wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
if ( keycode == WXK_LEFT )
|
|
{
|
|
if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Collapse ( p ) )
|
|
keycode = 0;
|
|
}
|
|
else if ( keycode == WXK_RIGHT )
|
|
{
|
|
if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Expand ( p ) )
|
|
keycode = 0;
|
|
}
|
|
}
|
|
|
|
if ( keycode )
|
|
{
|
|
if ( keycode == WXK_UP || keycode == WXK_LEFT )
|
|
{
|
|
selectDir = 0;
|
|
}
|
|
else if ( keycode == WXK_DOWN || keycode == WXK_RIGHT )
|
|
{
|
|
selectDir = 1;
|
|
}
|
|
else
|
|
{
|
|
event.Skip();
|
|
}
|
|
|
|
}
|
|
|
|
if ( selectDir >= -1 )
|
|
{
|
|
p = GetNeighbourItem( p, true, selectDir );
|
|
if ( p )
|
|
DoSelectProperty(p);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
// If nothing was selected, select the first item now
|
|
// (or navigate out of tab).
|
|
if ( keycode != WXK_ESCAPE )
|
|
{
|
|
wxPGProperty* p = GetFirst();
|
|
if ( p ) DoSelectProperty(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Potentially handles a keyboard event for editor controls.
|
|
// Returns false if event should *not* be skipped (on true it can
|
|
// be optionally skipped).
|
|
// Basicly, false means that SelectProperty was called (or was about
|
|
// to be called, if canDestroy was false).
|
|
bool wxPropertyGrid::HandleChildKey( wxKeyEvent& event, bool canDestroy )
|
|
{
|
|
int keycode = event.GetKeyCode();
|
|
bool res = true;
|
|
|
|
#if __INTENSE_DEBUGGING__
|
|
wxLogDebug( wxT("wxPropertyGrid::HandleChildKey(%i)"),(int)event.GetKeyCode() );
|
|
#endif
|
|
|
|
// Unfocus?
|
|
if ( keycode == WXK_ESCAPE )
|
|
{
|
|
// Esc cancels any changes
|
|
EditorsValueWasNotModified();
|
|
|
|
wxPGProperty* p = m_selected;
|
|
|
|
res = false;
|
|
|
|
if ( canDestroy )
|
|
{
|
|
DoSelectProperty( (wxPGProperty*)NULL, wxPG_SEL_NOVALIDATE );
|
|
DoSelectProperty( p );
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnKey( wxKeyEvent &event )
|
|
{
|
|
|
|
//
|
|
// Events to editor controls should get relayed here.
|
|
//
|
|
wxWindow* focused = wxWindow::FindFocus();
|
|
//wxLogDebug(wxT("OnKey"));
|
|
|
|
if ( m_wndPrimary &&
|
|
(focused==m_wndPrimary
|
|
|| m_editorFocused
|
|
#if wxPG_ENABLE_CLIPPER_WINDOW
|
|
|| ((m_wndPrimary->IsKindOf(CLASSINFO(wxPGClipperWindow))) &&
|
|
((wxPGClipperWindow*)m_wndPrimary)->GetControl() == focused)
|
|
#endif
|
|
) )
|
|
{
|
|
// Child key must be processed here, since it can
|
|
// destroy the control which is referred by its own
|
|
// event handling.
|
|
HandleChildKey( event, true );
|
|
}
|
|
else
|
|
HandleKeyEvent( event );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnKeyUp(wxKeyEvent &event)
|
|
{
|
|
m_keyComboConsumed = 0;
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnNavigationKey( wxNavigationKeyEvent& event )
|
|
{
|
|
// Ignore events that occur very close to focus set
|
|
if ( m_iFlags & wxPG_FL_IGNORE_NEXT_NAVKEY )
|
|
{
|
|
m_iFlags &= ~(wxPG_FL_IGNORE_NEXT_NAVKEY);
|
|
event.Skip();
|
|
return;
|
|
}
|
|
|
|
wxPGProperty* next = (wxPGProperty*) NULL;
|
|
|
|
int dir = event.GetDirection()?1:0;
|
|
|
|
if ( m_selected )
|
|
{
|
|
if ( dir == 1 && (m_wndPrimary || m_wndSecondary) )
|
|
{
|
|
wxWindow* focused = wxWindow::FindFocus();
|
|
|
|
wxWindow* wndToCheck = GetEditorControl();
|
|
|
|
// ODComboBox focus goes to its text ctrl, so we need to use it instead
|
|
if ( wndToCheck && wndToCheck->IsKindOf(CLASSINFO(wxPGOwnerDrawnComboBox)) )
|
|
{
|
|
wxTextCtrl* comboTextCtrl = ((wxPGOwnerDrawnComboBox*)wndToCheck)->GetTextCtrl();
|
|
if ( comboTextCtrl )
|
|
wndToCheck = comboTextCtrl;
|
|
}
|
|
|
|
/*
|
|
// Because of problems navigating from wxButton, do not go to it.
|
|
if ( !wndToCheck )
|
|
{
|
|
// No primary, use secondary
|
|
wndToCheck = m_wndSecondary;
|
|
}
|
|
// If it has editor button, focus to it after the primary editor.
|
|
// NB: Doesn't work since wxButton on wxMSW doesn't seem to propagate
|
|
// key events (yes, I'm using wxWANTS_CHARS with it, and yes I
|
|
// have somewhat debugged in window.cpp itself).
|
|
else if ( focused == wndToCheck &&
|
|
m_wndSecondary &&
|
|
!(GetExtraStyle() & wxPG_EX_NO_TAB_TO_BUTTON) )
|
|
{
|
|
wndToCheck = m_wndSecondary;
|
|
wxLogDebug(wxT("Exp1"));
|
|
}
|
|
*/
|
|
|
|
if ( focused != wndToCheck &&
|
|
wndToCheck )
|
|
{
|
|
wndToCheck->SetFocus();
|
|
|
|
// Select all text in wxTextCtrl etc.
|
|
if ( m_wndPrimary && wndToCheck == m_wndPrimary )
|
|
m_selected->GetEditorClass()->OnFocus(m_selected,wndToCheck);
|
|
|
|
m_editorFocused = 1;
|
|
next = m_selected;
|
|
}
|
|
}
|
|
|
|
if ( !next )
|
|
{
|
|
next = GetNeighbourItem(m_selected,true,dir);
|
|
|
|
if ( next )
|
|
{
|
|
// This allows preventing NavigateOut to occur
|
|
DoSelectProperty( next, wxPG_SEL_FOCUS );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !next )
|
|
event.Skip();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGrid::ButtonTriggerKeyTest( wxKeyEvent &event )
|
|
{
|
|
int keycode = event.GetKeyCode();
|
|
|
|
// Does the keycode trigger button?
|
|
if ( keycode == m_pushButKeyCode &&
|
|
m_wndSecondary &&
|
|
(!m_pushButKeyCodeNeedsAlt || event.AltDown()) &&
|
|
(!m_pushButKeyCodeNeedsCtrl || event.ControlDown()) )
|
|
{
|
|
m_keyComboConsumed = 1;
|
|
|
|
wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,m_wndSecondary->GetId());
|
|
GetEventHandler()->AddPendingEvent(evt);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnChildKeyDown( wxKeyEvent &event )
|
|
{
|
|
int keycode = event.GetKeyCode();
|
|
|
|
// Ignore Alt and Control when they are down alone
|
|
if ( keycode == WXK_ALT ||
|
|
keycode == WXK_CONTROL )
|
|
{
|
|
event.Skip();
|
|
return;
|
|
}
|
|
|
|
if ( ButtonTriggerKeyTest(event) )
|
|
return;
|
|
|
|
// Since event handling may destroy the control which
|
|
// triggered this event, we need to send it separately
|
|
// to the wxPropertyGrid itself. Also, to allow pushed
|
|
// event handler to grab ENTER, ESC and such, this
|
|
// has been changed to add all keys as events.
|
|
if ( HandleChildKey(event,false) == true )
|
|
event.Skip();
|
|
|
|
GetEventHandler()->AddPendingEvent(event);
|
|
}
|
|
|
|
void wxPropertyGrid::OnChildKeyUp( wxKeyEvent &event )
|
|
{
|
|
m_keyComboConsumed = 0;
|
|
|
|
GetEventHandler()->AddPendingEvent(event);
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGrid miscellaneous event handling
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnIdle( wxIdleEvent& WXUNUSED(event) )
|
|
{
|
|
//
|
|
// Check if the focus is in this control or one of its children
|
|
wxWindow* newFocused = wxWindow::FindFocus();
|
|
|
|
if ( newFocused != m_curFocused )
|
|
HandleFocusChange( newFocused );
|
|
}
|
|
|
|
// Called by focus event handlers. newFocused is the window that becomes focused.
|
|
void wxPropertyGrid::HandleFocusChange( wxWindow* newFocused )
|
|
{
|
|
unsigned int oldFlags = m_iFlags;
|
|
|
|
//wxLogDebug(wxT("HandleFocusChange: %s"),newFocused?newFocused->GetClassInfo()->GetClassName():wxT("NULL"));
|
|
|
|
m_iFlags &= ~(wxPG_FL_FOCUSED);
|
|
|
|
wxWindow* parent = newFocused;
|
|
|
|
// This must be one of nextFocus' parents.
|
|
while ( parent )
|
|
{
|
|
// Use m_eventObject, which is either wxPropertyGrid or
|
|
// wxPropertyGridManager, as appropriate.
|
|
if ( parent == m_eventObject )
|
|
{
|
|
m_iFlags |= wxPG_FL_FOCUSED;
|
|
break;
|
|
}
|
|
parent = parent->GetParent();
|
|
}
|
|
|
|
m_curFocused = newFocused;
|
|
|
|
if ( (m_iFlags & wxPG_FL_FOCUSED) !=
|
|
(oldFlags & wxPG_FL_FOCUSED) )
|
|
{
|
|
// On each focus kill, mark the next nav key event
|
|
// to be ignored (can't do on set focus since the
|
|
// event would occur before it).
|
|
if ( !(m_iFlags & wxPG_FL_FOCUSED) )
|
|
{
|
|
m_iFlags |= wxPG_FL_IGNORE_NEXT_NAVKEY;
|
|
|
|
// Need to store changed value
|
|
CommitChangesFromEditor();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
//
|
|
// Preliminary code for tab-order respecting
|
|
// tab-traversal (but should be moved to
|
|
// OnNav handler)
|
|
//
|
|
wxWindow* prevFocus = event.GetWindow();
|
|
wxWindow* useThis = this;
|
|
if ( m_iFlags & wxPG_FL_IN_MANAGER )
|
|
useThis = GetParent();
|
|
|
|
if ( prevFocus &&
|
|
prevFocus->GetParent() == useThis->GetParent() )
|
|
{
|
|
wxList& children = useThis->GetParent()->GetChildren();
|
|
|
|
wxNode* node = children.Find(prevFocus);
|
|
|
|
if ( node->GetNext() &&
|
|
useThis == node->GetNext()->GetData() )
|
|
DoSelectProperty(GetFirst());
|
|
else if ( node->GetPrevious () &&
|
|
useThis == node->GetPrevious()->GetData() )
|
|
DoSelectProperty(GetLastProperty());
|
|
|
|
}
|
|
*/
|
|
|
|
m_iFlags &= ~(wxPG_FL_IGNORE_NEXT_NAVKEY);
|
|
}
|
|
|
|
// Redraw selected
|
|
if ( m_selected && (m_iFlags & wxPG_FL_INITIALIZED) )
|
|
DrawItem( m_selected );
|
|
}
|
|
}
|
|
|
|
void wxPropertyGrid::OnFocusEvent( wxFocusEvent& event )
|
|
{
|
|
#if 1
|
|
if ( event.GetEventType() == wxEVT_SET_FOCUS )
|
|
HandleFocusChange((wxWindow*)event.GetEventObject());
|
|
// Line changed to "else" when applying patch #1675902
|
|
//else if ( event.GetWindow() )
|
|
else
|
|
HandleFocusChange(event.GetWindow());
|
|
|
|
event.Skip();
|
|
#else
|
|
unsigned int oldFlags = m_iFlags;
|
|
|
|
//
|
|
// Determine the current focus state
|
|
if ( event.GetEventType() == wxEVT_SET_FOCUS ||
|
|
event.GetEventType() == wxEVT_CHILD_FOCUS )
|
|
{
|
|
m_iFlags |= wxPG_FL_FOCUSED;
|
|
}
|
|
else
|
|
{
|
|
wxWindow* nextFocus = event.GetWindow();
|
|
|
|
m_iFlags &= ~(wxPG_FL_FOCUSED);
|
|
|
|
wxWindow* parent = nextFocus;
|
|
//wxLogDebug(wxT("KillFocus: %s"),parent->GetClassInfo()->GetClassName());
|
|
|
|
// This must be one of nextFocus' parents.
|
|
while ( parent )
|
|
{
|
|
if ( parent == this )
|
|
{
|
|
m_iFlags |= wxPG_FL_FOCUSED;
|
|
break;
|
|
}
|
|
parent = parent->GetParent();
|
|
}
|
|
}
|
|
|
|
if ( (m_iFlags & wxPG_FL_FOCUSED) !=
|
|
(oldFlags & wxPG_FL_FOCUSED) )
|
|
{
|
|
// On each focus kill, mark the next nav key event
|
|
// to be ignored (can't do on set focus since the
|
|
// event would occur before it).
|
|
if ( !(m_iFlags & wxPG_FL_FOCUSED) )
|
|
{
|
|
m_iFlags |= wxPG_FL_IGNORE_NEXT_NAVKEY;
|
|
|
|
// Need to store changed value
|
|
CommitChangesFromEditor();
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
//
|
|
// Preliminary code for tab-order respecting
|
|
// tab-traversal (but should be moved to
|
|
// OnNav handler)
|
|
//
|
|
wxWindow* prevFocus = event.GetWindow();
|
|
wxWindow* useThis = this;
|
|
if ( m_iFlags & wxPG_FL_IN_MANAGER )
|
|
useThis = GetParent();
|
|
|
|
if ( prevFocus &&
|
|
prevFocus->GetParent() == useThis->GetParent() )
|
|
{
|
|
wxList& children = useThis->GetParent()->GetChildren();
|
|
|
|
wxNode* node = children.Find(prevFocus);
|
|
|
|
if ( node->GetNext() &&
|
|
useThis == node->GetNext()->GetData() )
|
|
DoSelectProperty(GetFirst());
|
|
else if ( node->GetPrevious () &&
|
|
useThis == node->GetPrevious()->GetData() )
|
|
DoSelectProperty(GetLastProperty());
|
|
|
|
}
|
|
*/
|
|
|
|
m_iFlags &= ~(wxPG_FL_IGNORE_NEXT_NAVKEY);
|
|
}
|
|
|
|
// Redraw selected
|
|
if ( m_selected && (m_iFlags & wxPG_FL_INITIALIZED) )
|
|
DrawItem( m_selected );
|
|
}
|
|
|
|
event.Skip();
|
|
#endif
|
|
}
|
|
|
|
void wxPropertyGrid::OnChildFocusEvent( wxChildFocusEvent& event )
|
|
{
|
|
HandleFocusChange((wxWindow*)event.GetEventObject());
|
|
event.Skip();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnScrollEvent( wxScrollWinEvent &event )
|
|
{
|
|
m_iFlags |= wxPG_FL_SCROLLED;
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::OnCaptureChange( wxMouseCaptureChangedEvent& WXUNUSED(event) )
|
|
{
|
|
if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
|
|
{
|
|
#if __MOUSE_DEBUGGING__
|
|
wxLogDebug( wxT("wxPropertyGrid: mouse capture lost") );
|
|
#endif
|
|
m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Property text-based storage
|
|
// -----------------------------------------------------------------------
|
|
|
|
#define wxPG_PROPERTY_FLAGS_COUNT 8
|
|
|
|
// property-flag-to-text array
|
|
static const wxChar* gs_property_flag_to_string[wxPG_PROPERTY_FLAGS_COUNT] =
|
|
{
|
|
wxT("Modified"),
|
|
wxT("Disabled"),
|
|
wxT("LowPriority"),
|
|
(const wxChar*) NULL, // wxPG_PROP_CUSTOMIMAGE is auto-generated flag
|
|
wxT("LimitedEditing"),
|
|
wxT("Unspecified"),
|
|
(const wxChar*) NULL, // Special flags cannot be stored as-is
|
|
(const wxChar*) NULL //
|
|
};
|
|
|
|
|
|
wxString wxPGProperty::GetAttributes( unsigned int flagmask )
|
|
{
|
|
wxASSERT(this);
|
|
|
|
wxString s;
|
|
unsigned int i;
|
|
unsigned int flags = ((unsigned int)m_flags) &
|
|
flagmask &
|
|
~(wxPG_PROP_CUSTOMIMAGE |
|
|
wxPG_PROP_CLASS_SPECIFIC_1 |
|
|
wxPG_PROP_CLASS_SPECIFIC_2);
|
|
|
|
if ( !flags )
|
|
return wxEmptyString;
|
|
|
|
for ( i=0; i<wxPG_PROPERTY_FLAGS_COUNT; i++ )
|
|
{
|
|
if ( flags & (1<<i) )
|
|
{
|
|
s.append( gs_property_flag_to_string[i] );
|
|
flags &= ~(1<<i);
|
|
if ( !flags )
|
|
break;
|
|
s.append(wxT(", "));
|
|
}
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPGProperty::SetAttributes( const wxString& attributes )
|
|
{
|
|
wxASSERT(this);
|
|
size_t i;
|
|
|
|
WX_PG_TOKENIZER1_BEGIN(attributes,wxT(','))
|
|
|
|
for (i=0;i<wxPG_PROPERTY_FLAGS_COUNT;i++)
|
|
{
|
|
const wxChar* flagText = gs_property_flag_to_string[i];
|
|
if ( flagText && token == flagText )
|
|
{
|
|
m_flags |= ( 1<<i );
|
|
break;
|
|
}
|
|
}
|
|
|
|
WX_PG_TOKENIZER1_END()
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Returns name of property without 'Property' at the end, and 'wx'
|
|
// in the beginning (if any).
|
|
wxString wxPropertyContainerMethods::GetPropertyShortClassName( wxPGId id )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxEmptyString)
|
|
|
|
if ( p->GetParentingType() != 1 )
|
|
{
|
|
const wxChar* src = p->GetClassName();
|
|
wxString s;
|
|
if ( src[0] == wxT('w') && src[1] == wxT('x') )
|
|
s = &src[2];
|
|
else
|
|
s = src;
|
|
wxASSERT( (((int)s.length())-8) > 0 );
|
|
s.Truncate(s.length()-8);
|
|
//s.LowerCase();
|
|
return s;
|
|
}
|
|
return wxT("Category");
|
|
}
|
|
|
|
wxPGId wxPropertyContainerMethods::GetPropertyByNameA( wxPGPropNameStr name ) const
|
|
{
|
|
wxPGId id = GetPropertyByName(name);
|
|
wxASSERT_MSG(wxPGIdIsOk(id),wxString::Format(wxT("no property with name '%s'"),name.c_str()));
|
|
return id;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// VariantDatas
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#if wxPG_PGVARIANT_IS_VARIANT
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataPoint, wxVariantData)
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataSize, wxVariantData)
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataArrayInt, wxVariantData)
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataLongLong, wxVariantData)
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataULongLong, wxVariantData)
|
|
#ifdef __WXPYTHON__
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataPyObject, wxVariantData)
|
|
#endif
|
|
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Value type related methods (should all be pretty much static).
|
|
|
|
wxPGValueType::~wxPGValueType()
|
|
{
|
|
}
|
|
|
|
wxPG_CONST_WXCHAR_PTR wxPGValueType::GetCustomTypeName() const
|
|
{
|
|
return GetTypeName();
|
|
}
|
|
|
|
// Implement default types.
|
|
WX_PG_IMPLEMENT_VALUE_TYPE(wxString,wxStringProperty,wxPGTypeName_wxString,GetString,wxEmptyString)
|
|
WX_PG_IMPLEMENT_VALUE_TYPE(long,wxIntProperty,wxPGTypeName_long,GetLong,(long)0)
|
|
WX_PG_IMPLEMENT_VALUE_TYPE(double,wxFloatProperty,wxPGTypeName_double,GetDouble,0.0)
|
|
WX_PG_IMPLEMENT_VALUE_TYPE(wxArrayString,wxArrayStringProperty,wxPGTypeName_wxArrayString,GetArrayString,wxArrayString())
|
|
|
|
// Bool is a special case... thanks to the C++'s bool vs int vs long inconsistency issues.
|
|
const wxPGValueType *wxPGValueType_bool = (wxPGValueType *) NULL;
|
|
class wxPGValueTypeboolClass : public wxPGValueType
|
|
{
|
|
public:
|
|
virtual wxPG_CONST_WXCHAR_PTR GetTypeName() const { return wxPGTypeName_long; }
|
|
virtual wxPG_CONST_WXCHAR_PTR GetCustomTypeName() const { return wxPGTypeName_bool; }
|
|
virtual wxPGVariant GetDefaultValue() const { return wxPGVariant((long)0); }
|
|
virtual wxVariant GenerateVariant( wxPGVariant value, const wxString& name ) const
|
|
{ return wxVariant ( value.GetBool(), name ); }
|
|
virtual wxPGProperty* GenerateProperty( const wxString& label, const wxString& name ) const
|
|
{
|
|
return wxPG_NEWPROPERTY(Bool,label,name,false);
|
|
}
|
|
virtual void SetValueFromVariant( wxPGProperty* property, wxVariant& value ) const
|
|
{
|
|
#if defined(__WXDEBUG__) || defined(__WXPYTHON__)
|
|
wxCHECK_RET( wxStrcmp(wxPGTypeName_bool,value.GetType().c_str()) == 0,
|
|
wxT("SetValueFromVariant: wxVariant type mismatch.") );
|
|
#endif
|
|
property->DoSetValue(value.GetBool()?(long)1:(long)0);
|
|
}
|
|
};
|
|
|
|
// Implement nonetype.
|
|
const wxPGValueType *wxPGValueType_none = (wxPGValueType*) NULL;
|
|
class wxPGValueTypenoneClass : public wxPGValueType
|
|
{
|
|
public:
|
|
virtual wxPG_CONST_WXCHAR_PTR GetTypeName() const { return wxT("null"); }
|
|
virtual wxPGVariant GetDefaultValue() const { return wxPGVariant((long)0); }
|
|
virtual wxVariant GenerateVariant( wxPGVariant, const wxString& name ) const
|
|
{ return wxVariant( (long)0, name ); }
|
|
virtual wxPGProperty* GenerateProperty( const wxString&, const wxString& ) const
|
|
{ return (wxPGProperty*) NULL; }
|
|
virtual void SetValueFromVariant( wxPGProperty*, wxVariant& ) const
|
|
{ }
|
|
};
|
|
|
|
// Implement void* type.
|
|
const wxPGValueType *wxPGValueType_void = (wxPGValueType*) NULL;
|
|
class wxPGValueTypevoidClass : public wxPGValueType
|
|
{
|
|
public:
|
|
virtual wxPG_CONST_WXCHAR_PTR GetTypeName() const { return wxPGTypeName_void; }
|
|
virtual wxPGVariant GetDefaultValue() const { return wxPGVariant((void*)NULL); }
|
|
virtual wxVariant GenerateVariant( wxPGVariant value, const wxString& name ) const
|
|
{ return wxVariant( wxPGVariantToVoidPtr(value), name ); }
|
|
virtual wxPGProperty* GenerateProperty( const wxString&, const wxString& ) const
|
|
{ return (wxPGProperty*) NULL; }
|
|
virtual void SetValueFromVariant( wxPGProperty* property, wxVariant& value ) const
|
|
{
|
|
#if defined(__WXDEBUG__) || defined(__WXPYTHON__)
|
|
wxCHECK_RET( wxStrcmp(GetTypeName(),value.GetType().c_str()) == 0,
|
|
wxT("SetValueFromVariant: wxVariant type mismatch.") );
|
|
#endif
|
|
property->DoSetValue(value.GetVoidPtr());
|
|
}
|
|
};
|
|
|
|
#ifdef __WXPYTHON__
|
|
// Implement PyObject* type.
|
|
const wxPGValueType *wxPGValueType_PyObject = (wxPGValueType*) NULL;
|
|
class wxPGValueTypePyObjectClass : public wxPGValueType
|
|
{
|
|
public:
|
|
virtual wxPG_CONST_WXCHAR_PTR GetTypeName() const { return wxT("PyObject"); }
|
|
virtual wxPGVariant GetDefaultValue() const
|
|
{
|
|
return wxVariant( new wxPGVariantDataPyObject(Py_None) );
|
|
}
|
|
virtual wxVariant GenerateVariant( wxPGVariant value, const wxString& name ) const
|
|
{
|
|
value.SetName( name );
|
|
return value; // Can be done since under wxPython, wxPGVariant is wxVariant
|
|
}
|
|
virtual wxPGProperty* GenerateProperty( const wxString&, const wxString& ) const
|
|
{
|
|
return (wxPGProperty*) NULL;
|
|
}
|
|
virtual void SetValueFromVariant( wxPGProperty* property, wxVariant& value ) const
|
|
{
|
|
#if defined(__WXDEBUG__) || defined(__WXPYTHON__)
|
|
wxCHECK_RET( wxStrcmp(GetTypeName(),value.GetType().c_str()) == 0,
|
|
wxT("SetValueFromVariant: wxVariant type mismatch.") );
|
|
#endif
|
|
property->DoSetValue(value);
|
|
}
|
|
};
|
|
#endif // __WXPYTHON__
|
|
|
|
// Registers all default value types
|
|
void wxPropertyGrid::RegisterDefaultValues()
|
|
{
|
|
wxPGRegisterDefaultValueType( none );
|
|
wxPGRegisterDefaultValueType( wxString );
|
|
wxPGRegisterDefaultValueType( long );
|
|
wxPGRegisterDefaultValueType( bool );
|
|
wxPGRegisterDefaultValueType( double );
|
|
wxPGRegisterDefaultValueType( void );
|
|
wxPGRegisterDefaultValueType( wxArrayString );
|
|
#ifdef __WXPYTHON__
|
|
wxPGRegisterDefaultValueType( PyObject );
|
|
#endif
|
|
}
|
|
|
|
// noDefCheck = true prevents infinite recursion.
|
|
wxPGValueType* wxPropertyGrid::RegisterValueType( wxPGValueType* valueclass, bool noDefCheck, const wxString& className )
|
|
{
|
|
wxASSERT( valueclass );
|
|
|
|
WX_PG_GLOBALS_LOCKER()
|
|
|
|
if ( !noDefCheck && wxPGGlobalVars->m_dictValueType.empty() )
|
|
RegisterDefaultValues();
|
|
|
|
wxString temp_str;
|
|
wxPG_CONST_WXCHAR_PTR name_ = valueclass->GetType();
|
|
const wxChar* name = wxPG_TO_WXCHAR_PTR(name_);
|
|
|
|
wxPGValueType* p_at_slot = (wxPGValueType*) wxPGGlobalVars->m_dictValueType[name];
|
|
|
|
if ( !p_at_slot )
|
|
{
|
|
wxPGGlobalVars->m_dictValueType[name] = (void*) valueclass;
|
|
|
|
#if wxPG_VALUETYPE_IS_STRING
|
|
wxPGGlobalVars->m_dictValueTypeByClass[className] = (void*) valueclass;
|
|
#else
|
|
wxUnusedVar(className);
|
|
#endif
|
|
|
|
return valueclass;
|
|
}
|
|
|
|
// Delete given object instance, but only if it wasn't the same as in the hashmap.
|
|
if ( p_at_slot != valueclass )
|
|
{
|
|
delete valueclass;
|
|
}
|
|
|
|
return p_at_slot;
|
|
}
|
|
|
|
|
|
/*
|
|
* wxPGVariantDataWxObj
|
|
*/
|
|
|
|
//IMPLEMENT_DYNAMIC_CLASS(wxPGVariantDataWxObj, wxVariantData)
|
|
|
|
wxPGVariantDataWxObj::wxPGVariantDataWxObj()
|
|
: wxVariantData()
|
|
{
|
|
}
|
|
|
|
wxPGVariantDataWxObj::~wxPGVariantDataWxObj()
|
|
{
|
|
}
|
|
|
|
#if wxUSE_STD_IOSTREAM
|
|
bool wxPGVariantDataWxObj::Write(wxSTD ostream&) const
|
|
{
|
|
// Not implemented
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
bool wxPGVariantDataWxObj::Write(wxString&) const
|
|
{
|
|
// Not implemented
|
|
return true;
|
|
}
|
|
|
|
#if wxUSE_STD_IOSTREAM
|
|
bool wxPGVariantDataWxObj::Read(wxSTD istream& WXUNUSED(str))
|
|
{
|
|
// Not implemented
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool wxPGVariantDataWxObj::Read(wxString& WXUNUSED(str))
|
|
{
|
|
// Not implemented
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Editor class specific.
|
|
|
|
// noDefCheck = true prevents infinite recursion.
|
|
wxPGEditor* wxPropertyGrid::RegisterEditorClass( wxPGEditor* editorclass,
|
|
const wxString& name,
|
|
bool noDefCheck )
|
|
{
|
|
wxASSERT( editorclass );
|
|
|
|
WX_PG_GLOBALS_LOCKER()
|
|
|
|
if ( !noDefCheck && wxPGGlobalVars->m_mapEditorClasses.empty() )
|
|
RegisterDefaultEditors();
|
|
|
|
wxPGGlobalVars->m_mapEditorClasses[name] = (void*)editorclass;
|
|
|
|
return editorclass;
|
|
}
|
|
|
|
// Registers all default editor classes
|
|
void wxPropertyGrid::RegisterDefaultEditors()
|
|
{
|
|
wxPGRegisterDefaultEditorClass( TextCtrl );
|
|
wxPGRegisterDefaultEditorClass( Choice );
|
|
wxPGRegisterDefaultEditorClass( ComboBox );
|
|
wxPGRegisterDefaultEditorClass( TextCtrlAndButton );
|
|
#if wxPG_INCLUDE_CHECKBOX
|
|
wxPGRegisterDefaultEditorClass( CheckBox );
|
|
#endif
|
|
wxPGRegisterDefaultEditorClass( ChoiceAndButton );
|
|
|
|
// Register SpinCtrl etc. editors before use
|
|
RegisterAdditionalEditors();
|
|
}
|
|
|
|
wxPGEditor* wxPropertyContainerMethods::GetEditorByName( const wxString& editor_name )
|
|
{
|
|
wxPGEditor* editor = (wxPGEditor*) wxPGGlobalVars->m_mapEditorClasses[editor_name];
|
|
wxASSERT_MSG( editor,
|
|
wxT("unregistered editor name") );
|
|
return editor;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGStringTokenizer
|
|
// Needed to handle C-style string lists (e.g. "str1" "str2")
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGStringTokenizer::wxPGStringTokenizer( const wxString& str, wxChar delimeter )
|
|
: m_str(&str), m_curPos(str.begin()), m_delimeter(delimeter)
|
|
{
|
|
}
|
|
|
|
wxPGStringTokenizer::~wxPGStringTokenizer()
|
|
{
|
|
}
|
|
|
|
bool wxPGStringTokenizer::HasMoreTokens()
|
|
{
|
|
const wxString& str = *m_str;
|
|
|
|
//wxASSERT_MSG( m_curPos != str.end(), wxT("Do not call wxPGStringTokenizer methods after HasMoreTokens has returned false."));
|
|
|
|
wxString::const_iterator i = m_curPos;
|
|
|
|
wxUniChar delim = m_delimeter;
|
|
wxUniChar a;
|
|
wxUniChar prev_a = wxT('\0');
|
|
|
|
bool inToken = false;
|
|
|
|
while ( i != str.end() )
|
|
{
|
|
a = wxPGGetIterChar(str, i);
|
|
|
|
if ( !inToken )
|
|
{
|
|
if ( a == delim )
|
|
{
|
|
inToken = true;
|
|
m_readyToken.clear();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( prev_a != wxT('\\') )
|
|
{
|
|
if ( a != delim )
|
|
{
|
|
if ( a != wxT('\\') )
|
|
m_readyToken << a;
|
|
}
|
|
else
|
|
{
|
|
//wxLogDebug(m_readyToken);
|
|
i++;
|
|
m_curPos = i;
|
|
return true;
|
|
}
|
|
prev_a = a;
|
|
}
|
|
else
|
|
{
|
|
m_readyToken << a;
|
|
prev_a = wxT('\0');
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
|
|
m_curPos = str.end();
|
|
|
|
if ( inToken )
|
|
return true;
|
|
|
|
return false;
|
|
|
|
/*
|
|
const wxChar* ptr = m_curPos;
|
|
const wxChar* ptr_end = &m_str->c_str()[m_str->length()];
|
|
|
|
size_t store_index = 0xFFFFFFFF;
|
|
|
|
#if !wxUSE_STL
|
|
wxChar* store_ptr_base = (wxChar*) NULL;
|
|
#endif
|
|
|
|
wxChar delim = m_delimeter;
|
|
wxChar a = *ptr;
|
|
wxChar prev_a = 0;
|
|
|
|
while ( a )
|
|
{
|
|
if ( store_index == 0xFFFFFFFF )
|
|
{
|
|
if ( a == delim )
|
|
{
|
|
size_t req_len = ptr_end-ptr+1;
|
|
#if wxUSE_STL
|
|
if ( m_readyToken.length() < req_len )
|
|
m_readyToken.resize( req_len, wxT(' ') );
|
|
#else
|
|
store_ptr_base = m_readyToken.GetWriteBuf( req_len );
|
|
#endif
|
|
store_index = 0;
|
|
prev_a = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( prev_a != wxT('\\') )
|
|
{
|
|
if ( a != delim )
|
|
{
|
|
if ( a != wxT('\\') )
|
|
{
|
|
#if wxUSE_STL
|
|
m_readyToken[store_index] = a;
|
|
#else
|
|
store_ptr_base[store_index] = a;
|
|
#endif
|
|
store_index++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if wxUSE_STL
|
|
m_readyToken[store_index] = 0;
|
|
m_readyToken.resize(store_index,wxT(' '));
|
|
#else
|
|
store_ptr_base[store_index] = 0;
|
|
m_readyToken.UngetWriteBuf( store_index );
|
|
#endif
|
|
m_curPos = ptr+1;
|
|
return true;
|
|
}
|
|
prev_a = a;
|
|
}
|
|
else
|
|
{
|
|
#if wxUSE_STL
|
|
m_readyToken[store_index] = a;
|
|
#else
|
|
store_ptr_base[store_index] = a;
|
|
#endif
|
|
store_index++;
|
|
prev_a = 0;
|
|
}
|
|
}
|
|
ptr++;
|
|
a = *ptr;
|
|
}
|
|
#if !wxUSE_STL
|
|
if ( store_index != 0xFFFFFFFF )
|
|
m_readyToken.UngetWriteBuf( store_index );
|
|
#endif
|
|
m_curPos = (const wxChar*) NULL;
|
|
return false;
|
|
*/
|
|
}
|
|
|
|
wxString wxPGStringTokenizer::GetNextToken()
|
|
{
|
|
//wxASSERT_MSG( m_curPos != m_str->end(), wxT("Do not call wxPGStringTokenizer methods after HasMoreTokens has returned false."));
|
|
return m_readyToken;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGChoicesData
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGChoicesData::wxPGChoicesData()
|
|
{
|
|
m_refCount = 1;
|
|
}
|
|
|
|
wxPGChoicesData::~wxPGChoicesData()
|
|
{
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPGChoices
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPGChoices::Add( const wxChar* label, int value )
|
|
{
|
|
EnsureData();
|
|
|
|
if ( value != wxPG_INVALID_VALUE && m_data->m_arrLabels.GetCount() == m_data->m_arrValues.GetCount() )
|
|
m_data->m_arrValues.Add( value );
|
|
else if ( m_data->m_arrValues.GetCount() > 0 )
|
|
m_data->m_arrValues.Add( 0 );
|
|
|
|
m_data->m_arrLabels.Add ( label );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
#if wxCHECK_VERSION(2,9,0)
|
|
void wxPGChoices::Insert( const wxString& label, int index, int value )
|
|
#else
|
|
void wxPGChoices::Insert( const wxChar* label, int index, int value )
|
|
#endif
|
|
{
|
|
EnsureData();
|
|
|
|
if ( value != wxPG_INVALID_VALUE && m_data->m_arrLabels.GetCount() == m_data->m_arrValues.GetCount() )
|
|
m_data->m_arrValues.Insert( value, index );
|
|
else if ( m_data->m_arrValues.GetCount() > 0 )
|
|
m_data->m_arrValues.Insert( 0, index );
|
|
|
|
m_data->m_arrLabels.Insert( label, index );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPGChoices::AddAsSorted( const wxString& label, int value )
|
|
{
|
|
//wxASSERT_MSG( IsOk(),
|
|
// wxT("do not add items to invalid wxPGChoices") );
|
|
EnsureData();
|
|
|
|
size_t index = 0;
|
|
|
|
wxArrayString& labels = m_data->m_arrLabels;
|
|
wxArrayInt& values = m_data->m_arrValues;
|
|
|
|
while ( index < labels.GetCount() )
|
|
{
|
|
int cmpRes = labels[index].Cmp(label);
|
|
if ( cmpRes > 0 )
|
|
break;
|
|
index++;
|
|
}
|
|
|
|
if ( value != wxPG_INVALID_VALUE &&
|
|
labels.GetCount() == values.GetCount() )
|
|
values.Insert ( value, index );
|
|
|
|
labels.Insert ( label, index );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPGChoices::Add( const wxChar** labels, const long* values )
|
|
{
|
|
//wxASSERT_MSG( IsOk(),
|
|
// wxT("do not add items to invalid wxPGChoices") );
|
|
EnsureData();
|
|
|
|
unsigned int itemcount = 0;
|
|
const wxChar** p = &labels[0];
|
|
while ( *p ) { p++; itemcount++; }
|
|
|
|
wxArrayString& i_labels = m_data->m_arrLabels;
|
|
wxArrayInt& i_values = m_data->m_arrValues;
|
|
|
|
unsigned int i;
|
|
for ( i = 0; i < itemcount; i++ )
|
|
{
|
|
i_labels.Add ( labels[i] );
|
|
}
|
|
if ( values )
|
|
{
|
|
for ( i = 0; i < itemcount; i++ )
|
|
{
|
|
i_values.Add ( values[i] );
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPGChoices::Add( const wxArrayString& arr, const long* values )
|
|
{
|
|
//wxASSERT_MSG( IsOk(),
|
|
// wxT("do not add items to invalid wxPGChoices") );
|
|
EnsureData();
|
|
|
|
wxArrayString& labels = m_data->m_arrLabels;
|
|
wxArrayInt& i_values = m_data->m_arrValues;
|
|
|
|
unsigned int i;
|
|
unsigned int itemcount = arr.GetCount();
|
|
|
|
for ( i = 0; i < itemcount; i++ )
|
|
{
|
|
labels.Add ( arr[i] );
|
|
}
|
|
if ( values )
|
|
{
|
|
for ( i = 0; i < itemcount; i++ )
|
|
i_values.Add ( values[i] );
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPGChoices::Add( const wxArrayString& arr, const wxArrayInt& arrint )
|
|
{
|
|
//wxASSERT_MSG( IsOk(),
|
|
// wxT("do not add items to invalid wxPGChoices") );
|
|
EnsureData();
|
|
|
|
wxArrayString& labels = m_data->m_arrLabels;
|
|
wxArrayInt& values = m_data->m_arrValues;
|
|
|
|
unsigned int i;
|
|
unsigned int itemcount = arr.GetCount();
|
|
|
|
for ( i = 0; i < itemcount; i++ )
|
|
{
|
|
labels.Add ( arr[i] );
|
|
}
|
|
|
|
if ( &arrint && arrint.GetCount() )
|
|
for ( i = 0; i < itemcount; i++ )
|
|
{
|
|
values.Add ( arrint[i] );
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPGChoices::AssignData( wxPGChoicesData* data )
|
|
{
|
|
Free();
|
|
|
|
if ( data != wxPGChoicesEmptyData )
|
|
{
|
|
m_data = data;
|
|
data->m_refCount++;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPGChoices::Init()
|
|
{
|
|
m_data = wxPGChoicesEmptyData;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPGChoices::Free()
|
|
{
|
|
if ( m_data != wxPGChoicesEmptyData )
|
|
{
|
|
m_data->m_refCount--;
|
|
if ( m_data->m_refCount < 1 )
|
|
delete m_data;
|
|
m_data = wxPGChoicesEmptyData;
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridEvent
|
|
// -----------------------------------------------------------------------
|
|
|
|
IMPLEMENT_DYNAMIC_CLASS(wxPropertyGridEvent, wxCommandEvent)
|
|
|
|
|
|
DEFINE_EVENT_TYPE( wxEVT_PG_SELECTED )
|
|
DEFINE_EVENT_TYPE( wxEVT_PG_CHANGED )
|
|
DEFINE_EVENT_TYPE( wxEVT_PG_HIGHLIGHTED )
|
|
DEFINE_EVENT_TYPE( wxEVT_PG_RIGHT_CLICK )
|
|
DEFINE_EVENT_TYPE( wxEVT_PG_PAGE_CHANGED )
|
|
DEFINE_EVENT_TYPE( wxEVT_PG_ITEM_EXPANDED )
|
|
DEFINE_EVENT_TYPE( wxEVT_PG_ITEM_COLLAPSED )
|
|
DEFINE_EVENT_TYPE( wxEVT_PG_DOUBLE_CLICK )
|
|
DEFINE_EVENT_TYPE( wxEVT_PG_COMPACT_MODE_ENTERED )
|
|
DEFINE_EVENT_TYPE( wxEVT_PG_EXPANDED_MODE_ENTERED )
|
|
|
|
|
|
wxPropertyGridEvent::wxPropertyGridEvent(wxEventType commandType, int id)
|
|
: wxCommandEvent(commandType,id)
|
|
{
|
|
m_property = NULL;
|
|
m_pending = false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGridEvent::wxPropertyGridEvent(const wxPropertyGridEvent& event)
|
|
: wxCommandEvent(event)
|
|
{
|
|
m_eventType = event.GetEventType();
|
|
m_eventObject = event.m_eventObject;
|
|
m_pg = event.m_pg;
|
|
m_property = event.m_property;
|
|
m_pending = false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGridEvent::~wxPropertyGridEvent()
|
|
{
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxEvent* wxPropertyGridEvent::Clone() const
|
|
{
|
|
return new wxPropertyGridEvent( *this );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyContainerMethods
|
|
// - common methods for wxPropertyGrid and wxPropertyGridManager -
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyContainerMethods::DoSetPropertyAttribute( wxPGId id, int attrid,
|
|
wxVariant& value, long argFlags )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG()
|
|
|
|
p->SetAttribute(attrid,value);
|
|
|
|
if ( ( argFlags & wxPG_RECURSE ) && p->GetParentingType() != 0 )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
size_t i;
|
|
for ( i = 0; i < pwc->GetCount(); i++ )
|
|
DoSetPropertyAttribute(pwc->Item(i),attrid,value,argFlags);
|
|
}
|
|
}
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGrid::SetPropertyAttributeAll( int attrid, wxVariant value )
|
|
{
|
|
DoSetPropertyAttribute(GetRoot(),attrid,value,wxPG_RECURSE);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyContainerMethods::SetBoolChoices( const wxChar* true_choice,
|
|
const wxChar* false_choice )
|
|
{
|
|
WX_PG_GLOBALS_LOCKER()
|
|
wxPGGlobalVars->m_boolChoices[0] = false_choice;
|
|
wxPGGlobalVars->m_boolChoices[1] = true_choice;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGChoices gs_emptyChoices;
|
|
|
|
wxPGChoices& wxPropertyContainerMethods::GetPropertyChoices( wxPGId id )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(gs_emptyChoices)
|
|
|
|
wxPGChoiceInfo ci;
|
|
ci.m_choices = (wxPGChoices*) NULL;
|
|
|
|
p->GetChoiceInfo(&ci);
|
|
|
|
if ( !ci.m_choices )
|
|
return gs_emptyChoices;
|
|
|
|
return *ci.m_choices;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGChoices& wxPropertyContainerMethods::GetPropertyChoices( wxPGPropNameStr name )
|
|
{
|
|
wxPG_PROP_NAME_CALL_PROLOG_RETVAL(gs_emptyChoices)
|
|
return GetPropertyChoices(wxPGIdGen(p));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyContainerMethods::DoGetPropertyByName( wxPGPropNameStr name ) const
|
|
{
|
|
return m_pState->BaseGetPropertyByName(name);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyContainerMethods::GetPropertyByName( wxPGPropNameStr name,
|
|
wxPGPropNameStr subname ) const
|
|
{
|
|
wxPGId id = DoGetPropertyByName(name);
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*) wxPGIdToPtr(id);
|
|
if ( !pwc || !pwc->GetParentingType() )
|
|
return wxNullProperty;
|
|
|
|
return wxPGIdGen(pwc->GetPropertyByName(subname));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Since GetPropertyByName is used *a lot*, this makes sense
|
|
// since non-virtual method can be called with less code.
|
|
wxPGId wxPropertyContainerMethods::GetPropertyByName( wxPGPropNameStr name ) const
|
|
{
|
|
wxPGId id = DoGetPropertyByName(name);
|
|
if ( wxPGIdIsOk(id) )
|
|
return id;
|
|
|
|
// Check if its "Property.SubProperty" format
|
|
int pos = name.Find(wxT('.'));
|
|
if ( pos <= 0 )
|
|
return id;
|
|
|
|
return GetPropertyByName(name.substr(0,pos),
|
|
name.substr(pos+1,name.length()-pos-1));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyContainerMethods::HideProperty( wxPGId id, bool hide )
|
|
{
|
|
// Hiding properties requires that we are always in the compact mode
|
|
m_pState->GetGrid()->Compact(true);
|
|
return SetPropertyPriority(id,hide?wxPG_LOW:wxPG_HIGH);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Used by HideProperty as well
|
|
bool wxPropertyContainerMethods::SetPropertyPriority( wxPGId id, int priority )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
|
|
|
|
wxPropertyGrid* pg = m_pState->GetGrid();
|
|
|
|
if ( pg == p->GetGrid() )
|
|
return pg->SetPropertyPriority(p,priority);
|
|
else
|
|
m_pState->SetPropertyPriority(p,priority);
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyContainerMethods::SetPropertyMaxLength( wxPGId id, int maxLen )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
|
|
|
|
wxPropertyGrid* pg = m_pState->GetGrid();
|
|
|
|
p->m_maxLen = (short) maxLen;
|
|
|
|
// Adjust control if selected currently
|
|
if ( pg == p->GetGrid() && p == m_pState->GetSelection() )
|
|
{
|
|
wxWindow* wnd = pg->GetEditorControl();
|
|
wxTextCtrl* tc = wxDynamicCast(wnd,wxTextCtrl);
|
|
if ( tc )
|
|
tc->SetMaxLength( maxLen );
|
|
else
|
|
// Not a text ctrl
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// GetPropertyValueAsXXX methods
|
|
|
|
#define IMPLEMENT_GET_VALUE(T,TRET,BIGNAME,DEFRETVAL) \
|
|
TRET wxPropertyContainerMethods::GetPropertyValueAs##BIGNAME( wxPGId id ) wxPG_GETVALUE_CONST \
|
|
{ \
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(DEFRETVAL) \
|
|
if ( p->GetValueTypePtr()->GetTypeName() != wxPGTypeName_##T ) \
|
|
{ \
|
|
wxPGGetFailed(p,wxPGTypeName_##T); \
|
|
return (TRET)DEFRETVAL; \
|
|
} \
|
|
return (TRET)wxPGVariantTo##BIGNAME(p->DoGetValue()); \
|
|
}
|
|
|
|
// String is different than others.
|
|
wxString wxPropertyContainerMethods::GetPropertyValueAsString( wxPGId id ) wxPG_GETVALUE_CONST
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxEmptyString)
|
|
return p->GetValueAsString(wxPG_FULL_VALUE);
|
|
}
|
|
|
|
IMPLEMENT_GET_VALUE(long,long,Long,0)
|
|
IMPLEMENT_GET_VALUE(long,bool,Bool,false)
|
|
IMPLEMENT_GET_VALUE(double,double,Double,0.0)
|
|
IMPLEMENT_GET_VALUE(void,void*,VoidPtr,NULL)
|
|
#ifdef __WXPYTHON__
|
|
IMPLEMENT_GET_VALUE(PyObject,PyObject*,PyObject,Py_None)
|
|
#endif
|
|
|
|
#if !wxPG_PGVARIANT_IS_VARIANT
|
|
IMPLEMENT_GET_VALUE(wxArrayString,const wxArrayString&,ArrayString,*((wxArrayString*)NULL))
|
|
#endif
|
|
|
|
// wxObject is different than others.
|
|
const wxObject* wxPropertyContainerMethods::GetPropertyValueAsWxObjectPtr( wxPGId id ) wxPG_GETVALUE_CONST
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL((const wxObject*)NULL)
|
|
wxPG_CONST_WXCHAR_PTR typestr = p->GetValueTypePtr()->GetTypeName();
|
|
if ( typestr[0] != wxT('w') || typestr[1] != wxT('x') )
|
|
{
|
|
wxPGGetFailed(p,wxT("wxObject"));
|
|
return (const wxObject*) NULL;
|
|
}
|
|
return (const wxObject*)wxPGVariantGetWxObjectPtr(p->DoGetValue());
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyContainerMethods::IsPropertyExpanded( wxPGId id )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(false)
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
if ( pwc->GetParentingType() == 0 )
|
|
return false;
|
|
return pwc->IsExpanded();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// returns value type class for type name
|
|
wxPGValueType* wxPropertyContainerMethods::GetValueType(const wxString &type)
|
|
{
|
|
wxPGHashMapS2P::iterator it;
|
|
|
|
it = wxPGGlobalVars->m_dictValueType.find(type);
|
|
|
|
if ( it != wxPGGlobalVars->m_dictValueType.end() )
|
|
return (wxPGValueType*) it->second;
|
|
|
|
return (wxPGValueType*) NULL;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
#if wxPG_VALUETYPE_IS_STRING
|
|
wxPGValueType* wxPropertyContainerMethods::GetValueTypeByName(const wxString &className)
|
|
{
|
|
wxPGHashMapS2P::iterator it;
|
|
|
|
it = wxPGGlobalVars->m_dictValueTypeByClass.find(className);
|
|
|
|
if ( it != wxPGGlobalVars->m_dictValueTypeByClass.end() )
|
|
return (wxPGValueType*) it->second;
|
|
|
|
return (wxPGValueType*) NULL;
|
|
}
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGProperty* wxPropertyContainerMethods::CreatePropertyByType(const wxString &valuetype,
|
|
const wxString &label,
|
|
const wxString &name)
|
|
{
|
|
wxPGHashMapS2P::iterator it;
|
|
|
|
it = wxPGGlobalVars->m_dictValueType.find(valuetype);
|
|
|
|
if ( it != wxPGGlobalVars->m_dictValueType.end() )
|
|
{
|
|
wxPGValueType* vt = (wxPGValueType*) it->second;
|
|
wxPGProperty* p = vt->GenerateProperty(label,name);
|
|
#ifdef __WXDEBUG__
|
|
if ( !p )
|
|
{
|
|
wxLogDebug(wxT("WARNING: CreatePropertyByValueType generated NULL property for ValueType \"%s\""),valuetype.c_str());
|
|
return (wxPGProperty*) NULL;
|
|
}
|
|
#endif
|
|
return p;
|
|
}
|
|
|
|
wxLogDebug(wxT("WARNING: No value type registered with name \"%s\""),valuetype.c_str());
|
|
return (wxPGProperty*) NULL;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGProperty* wxPropertyContainerMethods::CreatePropertyByClass(const wxString &classname,
|
|
const wxString &label,
|
|
const wxString &name)
|
|
{
|
|
wxPGHashMapS2P* cis =
|
|
(wxPGHashMapS2P*) &wxPGGlobalVars->m_dictPropertyClassInfo;
|
|
|
|
const wxString* pClassname = &classname;
|
|
wxString s;
|
|
|
|
// Translate to long name, if necessary
|
|
if ( (pClassname->GetChar(0) != wxT('w') || pClassname->GetChar(1) != wxT('x')) &&
|
|
pClassname->Find(wxT("Property")) < 0 )
|
|
{
|
|
if ( classname != wxT("Category") )
|
|
s.Printf(wxT("wx%sProperty"),pClassname->c_str());
|
|
else
|
|
s = wxT("wxPropertyCategory");
|
|
pClassname = &s;
|
|
}
|
|
|
|
wxPGHashMapS2P::iterator it;
|
|
it = cis->find(*pClassname);
|
|
|
|
if ( it != cis->end() )
|
|
{
|
|
wxPGPropertyClassInfo* pci = (wxPGPropertyClassInfo*) it->second;
|
|
wxPGProperty* p = pci->m_constructor(label,name);
|
|
return p;
|
|
}
|
|
wxLogError(wxT("No such property class: %s"),pClassname->c_str());
|
|
return (wxPGProperty*) NULL;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// lazy way to prevent RegisterPropertyClass infinite recursion
|
|
static int gs_registering_standard_props = 0;
|
|
|
|
bool wxPropertyContainerMethods::RegisterPropertyClass( const wxChar* name,
|
|
wxPGPropertyClassInfo* classinfo )
|
|
{
|
|
|
|
WX_PG_GLOBALS_LOCKER()
|
|
|
|
// Standard classes must be registered first!
|
|
if ( !gs_registering_standard_props &&
|
|
wxPGGlobalVars->m_dictPropertyClassInfo.empty()
|
|
)
|
|
wxPGRegisterStandardPropertyClasses();
|
|
|
|
wxPGHashMapS2P::iterator it;
|
|
|
|
it = wxPGGlobalVars->m_dictPropertyClassInfo.find(name);
|
|
|
|
// only register if not registered already
|
|
if ( it == wxPGGlobalVars->m_dictPropertyClassInfo.end() )
|
|
{
|
|
wxPGGlobalVars->m_dictPropertyClassInfo[name] = classinfo;
|
|
return true;
|
|
}
|
|
|
|
wxLogDebug(wxT("WARNING: Property class named \"%s\" was already registered."),name);
|
|
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
static void wxPGRegisterStandardPropertyClasses()
|
|
{
|
|
|
|
if ( gs_registering_standard_props )
|
|
return;
|
|
|
|
gs_registering_standard_props = 1; // no need to reset this
|
|
|
|
wxPGRegisterPropertyClass(wxStringProperty);
|
|
wxPGRegisterPropertyClass(wxIntProperty);
|
|
wxPGRegisterPropertyClass(wxUIntProperty);
|
|
wxPGRegisterPropertyClass(wxFloatProperty);
|
|
wxPGRegisterPropertyClass(wxBoolProperty);
|
|
wxPGRegisterPropertyClass(wxEnumProperty);
|
|
wxPGRegisterPropertyClass(wxFlagsProperty);
|
|
wxPGRegisterPropertyClass(wxLongStringProperty);
|
|
|
|
wxPGRegisterPropertyClass(wxPropertyCategory);
|
|
wxPGRegisterPropertyClass(wxParentProperty);
|
|
wxPGRegisterPropertyClass(wxCustomProperty);
|
|
|
|
// TODO: Are these really "standard" ?
|
|
wxPGRegisterPropertyClass(wxArrayStringProperty);
|
|
wxPGRegisterPropertyClass(wxFileProperty);
|
|
wxPGRegisterPropertyClass(wxDirProperty);
|
|
|
|
#ifdef __WXPYTHON__
|
|
wxPropertyContainerMethods::RegisterAdvancedPropertyClasses();
|
|
#endif
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridState
|
|
// -----------------------------------------------------------------------
|
|
|
|
// reset helper macro
|
|
#undef FROM_STATE
|
|
#define FROM_STATE(A) A
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridState item iteration methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Skips categories and sub-properties (unless in wxCustomProperty/wxParentProperty).
|
|
wxPGId wxPropertyGridState::GetFirstProperty() const
|
|
{
|
|
if ( !m_properties->GetCount() ) return wxPGIdGen((wxPGProperty*)NULL);
|
|
wxPGProperty* p = m_properties->Item(0);
|
|
int parenting = p->GetParentingType();
|
|
if ( parenting > 0 )
|
|
return GetNextProperty ( wxPGIdGen(p) );
|
|
return wxPGIdGen(p);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Skips categories and sub-properties (unless in wxParentProperty).
|
|
wxPGId wxPropertyGridState::GetNextProperty( wxPGId id ) const
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
|
|
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
|
|
// Go with first child?
|
|
int parenting = pwc->GetParentingType();
|
|
if ( parenting == 0 || parenting == -1 || !pwc->GetCount() )
|
|
{
|
|
// No...
|
|
|
|
wxPGPropertyWithChildren* parent = pwc->m_parent;
|
|
|
|
// As long as last item, go up and get parent' sibling
|
|
while ( pwc->m_arrIndex >= (parent->GetCount()-1) )
|
|
{
|
|
pwc = parent;
|
|
if ( pwc == m_properties ) return wxPGIdGen((wxPGProperty*)NULL);
|
|
parent = parent->m_parent;
|
|
}
|
|
|
|
pwc = (wxPGPropertyWithChildren*)parent->Item(pwc->m_arrIndex+1);
|
|
|
|
// Go with the next sibling of parent's parent?
|
|
}
|
|
else
|
|
{
|
|
// Yes...
|
|
pwc = (wxPGPropertyWithChildren*)pwc->Item(0);
|
|
}
|
|
|
|
// If it's category or parentproperty, then go recursive
|
|
parenting = pwc->GetParentingType();
|
|
if ( parenting > PT_NONE )
|
|
return GetNextProperty( wxPGIdGen(pwc) );
|
|
|
|
return wxPGIdGen(pwc);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridState::GetNextSibling( wxPGId id )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
|
|
|
|
wxPGPropertyWithChildren* parent = p->m_parent;
|
|
size_t next_ind = p->m_arrIndex + 1;
|
|
if ( next_ind >= parent->GetCount() ) return wxPGIdGen((wxPGProperty*)NULL);
|
|
return wxPGIdGen(parent->Item(next_ind));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridState::GetPrevSibling( wxPGId id )
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
|
|
|
|
size_t ind = p->m_arrIndex;
|
|
if ( ind < 1 ) return wxPGIdGen((wxPGProperty*)NULL);
|
|
return wxPGIdGen(p->m_parent->Item(ind-1));
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Skips categories and sub-properties (unless in wxParentProperty).
|
|
wxPGId wxPropertyGridState::GetPrevProperty( wxPGId id ) const
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
|
|
|
|
wxPGPropertyWithChildren* p2 = (wxPGPropertyWithChildren*) p;
|
|
wxPGPropertyWithChildren* parent = p2->m_parent;
|
|
|
|
// Is there a previous sibling?
|
|
if ( p2->m_arrIndex > 0 )
|
|
{
|
|
// There is!
|
|
p2 = (wxPGPropertyWithChildren*)parent->Item ( p2->m_arrIndex-1 );
|
|
int parenting = p2->GetParentingType();
|
|
|
|
// Do we return it's last child?
|
|
while ( (parenting > 0 || parenting == PT_CUSTOMPROPERTY) && p2->GetCount() )
|
|
{
|
|
p2 = (wxPGPropertyWithChildren*)p2->Last();
|
|
parenting = p2->GetParentingType();
|
|
}
|
|
}
|
|
else if ( parent != m_properties )
|
|
// Return parent if it isnt' the root
|
|
p2 = parent;
|
|
else
|
|
return wxPGIdGen((wxPGProperty*)NULL);
|
|
|
|
// Skip category and parentproperty.
|
|
int parenting = p2->GetParentingType();
|
|
if ( parenting > PT_NONE )
|
|
return GetPrevProperty ( wxPGIdGen(p2) );
|
|
|
|
return wxPGIdGen(p2);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridState::GetFirstCategory() const
|
|
{
|
|
//if ( IsInNonCatMode() )
|
|
// return wxPGIdGen((wxPGProperty*)NULL);
|
|
|
|
wxPGProperty* found = (wxPGProperty*)NULL;
|
|
|
|
size_t i;
|
|
for ( i=0; i<m_regularArray.GetCount(); i++ )
|
|
{
|
|
wxPGProperty* p = m_regularArray.Item(i);
|
|
if ( p->GetParentingType() > 0 )
|
|
{
|
|
found = p;
|
|
break;
|
|
}
|
|
}
|
|
return wxPGIdGen(found);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridState::GetNextCategory( wxPGId id ) const
|
|
{
|
|
wxPG_PROP_ID_CALL_PROLOG_RETVAL(wxNullProperty)
|
|
|
|
wxPGPropertyWithChildren* current = (wxPGPropertyWithChildren*)p;
|
|
|
|
wxCHECK_MSG( !IsInNonCatMode() || current->GetParentingType() == 1, wxPGIdGen((wxPGProperty*)NULL),
|
|
wxT("GetNextCategory should not be called with non-category argument in non-categoric mode.") );
|
|
|
|
wxPGPropertyWithChildren* parent = current->m_parent;
|
|
wxPGProperty* found = (wxPGProperty*) NULL;
|
|
size_t i;
|
|
|
|
// Find sub-category, if any.
|
|
if ( current->GetParentingType() > 0 )
|
|
{
|
|
// Find first sub-category in current's array.
|
|
for ( i = 0; i<current->GetCount(); i++ )
|
|
{
|
|
wxPGProperty* p = current->Item(i);
|
|
if ( p->GetParentingType() > 0 )
|
|
{
|
|
found = p;
|
|
break;
|
|
}
|
|
}
|
|
if ( found )
|
|
return wxPGIdGen(found);
|
|
}
|
|
|
|
// Find next category in parent's array.
|
|
// (and go up in hierarchy until one found or
|
|
// top is reached).
|
|
do
|
|
{
|
|
for ( i = current->m_arrIndex+1; i<parent->GetCount(); i++ )
|
|
{
|
|
wxPGProperty* p = parent->Item(i);
|
|
if ( p->GetParentingType() > 0 )
|
|
{
|
|
found = p;
|
|
break;
|
|
}
|
|
}
|
|
current = parent;
|
|
parent = parent->m_parent;
|
|
} while ( !found && parent );
|
|
|
|
return wxPGIdGen(found);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridState GetPropertyXXX methods
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridState::GetPropertyByLabel( const wxString& label,
|
|
wxPGPropertyWithChildren* parent ) const
|
|
{
|
|
|
|
size_t i;
|
|
|
|
if ( !parent ) parent = (wxPGPropertyWithChildren*) &m_regularArray;
|
|
|
|
for ( i=0; i<parent->GetCount(); i++ )
|
|
{
|
|
wxPGProperty* p = parent->Item(i);
|
|
if ( p->m_label == label )
|
|
return wxPGIdGen(p);
|
|
// Check children recursively.
|
|
if ( p->GetParentingType() != 0 )
|
|
{
|
|
p = wxPGIdToPtr(GetPropertyByLabel(label,(wxPGPropertyWithChildren*)p));
|
|
if ( p )
|
|
return wxPGIdGen(p);
|
|
}
|
|
}
|
|
|
|
return wxPGIdGen((wxPGProperty*) NULL);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridState::BaseGetPropertyByName( wxPGPropNameStr name ) const
|
|
{
|
|
wxPGHashMapS2P::const_iterator it;
|
|
it = m_dictName.find(name);
|
|
if ( it != m_dictName.end() )
|
|
return wxPGIdGen( (wxPGProperty*) it->second );
|
|
return wxPGIdGen( (wxPGProperty*) NULL );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridState global operations
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::EnableCategories( bool enable )
|
|
{
|
|
ITEM_ITERATION_VARIABLES
|
|
|
|
if ( enable )
|
|
{
|
|
//
|
|
// Enable categories
|
|
//
|
|
|
|
if ( !IsInNonCatMode() )
|
|
return false;
|
|
|
|
m_properties = &m_regularArray;
|
|
|
|
// fix parents, indexes, and depths
|
|
ITEM_ITERATION_INIT_FROM_THE_TOP
|
|
|
|
ITEM_ITERATION_LOOP_BEGIN
|
|
|
|
p->m_arrIndex = i;
|
|
|
|
p->m_parent = parent;
|
|
|
|
// If parent was category, and this is not,
|
|
// then the depth stays the same.
|
|
if ( parent->GetParentingType() == 1 &&
|
|
p->GetParentingType() <= 0 )
|
|
p->m_depth = parent->m_depth;
|
|
else
|
|
p->m_depth = parent->m_depth + 1;
|
|
|
|
ITEM_ITERATION_LOOP_END
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Disable categories
|
|
//
|
|
|
|
if ( IsInNonCatMode() )
|
|
return false;
|
|
|
|
// Create array, if necessary.
|
|
if ( !m_abcArray )
|
|
InitNonCatMode();
|
|
|
|
m_properties = m_abcArray;
|
|
|
|
// fix parents, indexes, and depths
|
|
ITEM_ITERATION_INIT_FROM_THE_TOP
|
|
|
|
//ITEM_ITERATION_DCAE_ISP_LOOP_BEGIN
|
|
ITEM_ITERATION_DCAE_LOOP_BEGIN
|
|
|
|
p->m_arrIndex = i;
|
|
|
|
p->m_parent = parent;
|
|
|
|
p->m_depth = parent->m_depth + 1;
|
|
|
|
//ITEM_ITERATION_DCAE_ISP_LOOP_END
|
|
ITEM_ITERATION_DCAE_LOOP_END
|
|
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
static int wxPG_SortFunc(void **p1, void **p2)
|
|
{
|
|
wxPGProperty *pp1 = *((wxPGProperty**)p1);
|
|
wxPGProperty *pp2 = *((wxPGProperty**)p2);
|
|
return pp1->GetLabel().compare( pp2->GetLabel() );
|
|
}
|
|
|
|
void wxPropertyGridState::Sort( wxPGProperty* p )
|
|
{
|
|
if ( !p )
|
|
p = (wxPGProperty*)m_properties;
|
|
|
|
wxCHECK_RET( p->GetParentingType() != 0,
|
|
wxT("cannot sort non-parenting property") );
|
|
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
|
|
// Can only sort items with children
|
|
if ( pwc->m_children.GetCount() < 1 )
|
|
return;
|
|
|
|
pwc->m_children.Sort( wxPG_SortFunc );
|
|
|
|
// Fix indexes
|
|
pwc->FixIndexesOfChildren();
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::Sort()
|
|
{
|
|
Sort( m_properties );
|
|
|
|
// Sort categories as well
|
|
if ( !IsInNonCatMode() )
|
|
{
|
|
size_t i;
|
|
for ( i=0;i<m_properties->GetCount();i++)
|
|
{
|
|
wxPGProperty* p = m_properties->Item(i);
|
|
if ( p->GetParentingType() > 0 )
|
|
Sort ( p );
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::ExpandAll( unsigned char doExpand )
|
|
{
|
|
ITEM_ITERATION_DCAE_VARIABLES
|
|
|
|
bool isGrid = m_pPropGrid->GetState() == this;
|
|
|
|
if ( isGrid &&
|
|
m_selected &&
|
|
m_selected->GetParent() != m_properties )
|
|
{
|
|
if ( !m_pPropGrid->ClearSelection() )
|
|
return false;
|
|
}
|
|
|
|
if ( !doExpand )
|
|
{
|
|
if ( isGrid )
|
|
{
|
|
if ( !m_pPropGrid->ClearSelection() )
|
|
return false;
|
|
}
|
|
else m_selected = (wxPGProperty*) NULL;
|
|
}
|
|
|
|
ITEM_ITERATION_INIT_FROM_THE_TOP
|
|
|
|
ITEM_ITERATION_DCAE_LOOP_BEGIN
|
|
|
|
if ( parenting != 0 )
|
|
((wxPGPropertyWithChildren*)p)->m_expanded = doExpand;
|
|
|
|
ITEM_ITERATION_DCAE_LOOP_END
|
|
|
|
if ( m_pPropGrid->GetState() == this )
|
|
{
|
|
m_pPropGrid->CalculateYs((wxPGPropertyWithChildren*)NULL,-1);
|
|
|
|
m_pPropGrid->RedrawAllVisible();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Used by SetSplitterLeft
|
|
int wxPropertyGridState::GetLeftSplitterPos(wxClientDC& dc,
|
|
wxPGPropertyWithChildren* pwc,
|
|
bool subProps)
|
|
{
|
|
wxPropertyGrid* pg = m_pPropGrid;
|
|
size_t i;
|
|
int maxW = 0;
|
|
int w, h;
|
|
|
|
for ( i=0; i<pwc->GetCount(); i++ )
|
|
{
|
|
wxPGProperty* p = pwc->Item(i);
|
|
if ( p->GetParentingType() <= 0 )
|
|
{
|
|
dc.GetTextExtent( p->GetLabel(), &w, &h );
|
|
|
|
w += pg->m_marginWidth + ( ((int)p->m_depth-1) * pg->m_subgroup_extramargin ) + (wxPG_XBEFORETEXT*2);
|
|
|
|
if ( w > maxW )
|
|
maxW = w;
|
|
}
|
|
|
|
if ( p->GetParentingType() &&
|
|
( subProps || p->GetParentingType() > 0 ) )
|
|
{
|
|
w = GetLeftSplitterPos( dc, (wxPGPropertyWithChildren*) p, subProps );
|
|
|
|
if ( w > maxW )
|
|
maxW = w;
|
|
}
|
|
}
|
|
|
|
return maxW;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridState property value setting and getting
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::SetPropVal( wxPGProperty* p, const wxPGVariant& value )
|
|
{
|
|
p->DoSetValue(value);
|
|
if ( m_selected==p && this==m_pPropGrid->GetState() )
|
|
p->UpdateControl(m_pPropGrid->m_wndPrimary);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::ClearPropertyValue( wxPGProperty* p )
|
|
{
|
|
if ( p )
|
|
{
|
|
const wxPGValueType* valueclass = p->GetValueTypePtr();
|
|
|
|
if ( valueclass != wxPG_VALUETYPE_PTR(none) )
|
|
{
|
|
// wnd_primary has to be given so the editor control can be updated as well.
|
|
SetPropVal(p,valueclass->GetDefaultValue());
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::SetPropertyValue( wxPGProperty* p,
|
|
const wxPGValueType* typeclass,
|
|
const wxPGVariant& value )
|
|
{
|
|
if ( p )
|
|
{
|
|
if ( p->GetValueTypePtr()->GetTypeName() == typeclass->GetTypeName() )
|
|
{
|
|
CLEAR_PROPERTY_UNSPECIFIED_FLAG(p);
|
|
|
|
SetPropVal(p,value);
|
|
|
|
return true;
|
|
}
|
|
wxPGTypeOperationFailed ( p, typeclass->GetTypeName(), wxT("Set") );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::SetPropertyValue( wxPGProperty* p, const wxChar* typestring, const wxPGVariant& value )
|
|
{
|
|
if ( p )
|
|
{
|
|
if ( wxStrcmp(p->GetValueTypePtr()->GetCustomTypeName(),typestring) == 0 )
|
|
{
|
|
// wnd_primary has to be given so the control can be updated as well.
|
|
|
|
SetPropVal(p,value);
|
|
return true;
|
|
}
|
|
wxPGTypeOperationFailed ( p, typestring, wxT("Set") );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::SetPropertyValueString( wxPGProperty* p, const wxString& value )
|
|
{
|
|
if ( p )
|
|
{
|
|
int flags = wxPG_REPORT_ERROR|wxPG_FULL_VALUE;
|
|
CLEAR_PROPERTY_UNSPECIFIED_FLAG(p);
|
|
|
|
if ( p->GetMaxLength() <= 0 )
|
|
p->SetValueFromString( value, flags );
|
|
else
|
|
p->SetValueFromString( value.Mid(0,p->GetMaxLength()), flags );
|
|
|
|
if ( m_selected==p && this==m_pPropGrid->GetState() )
|
|
p->UpdateControl(m_pPropGrid->m_wndPrimary);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::SetPropertyValue( wxPGProperty* p, wxVariant& value )
|
|
{
|
|
if ( p )
|
|
{
|
|
CLEAR_PROPERTY_UNSPECIFIED_FLAG(p);
|
|
p->GetValueTypePtr()->SetValueFromVariant(p,value);
|
|
if ( m_selected==p && this==m_pPropGrid->GetState() )
|
|
p->UpdateControl(m_pPropGrid->m_wndPrimary);
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::SetPropertyValueWxObjectPtr( wxPGProperty* p, wxObject* value )
|
|
{
|
|
if ( p )
|
|
{
|
|
if ( wxStrcmp( p->GetValueTypePtr()->GetTypeName(),
|
|
value->GetClassInfo()->GetClassName()
|
|
) == 0
|
|
)
|
|
{
|
|
CLEAR_PROPERTY_UNSPECIFIED_FLAG(p);
|
|
// wnd_primary has to be given so the control can be updated as well.
|
|
SetPropVal(p,wxPGVariantFromWxObject(value));
|
|
return true;
|
|
}
|
|
wxPGTypeOperationFailed ( p, wxT("wxObject"), wxT("Set") );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::SetPropertyUnspecified( wxPGProperty* p )
|
|
{
|
|
wxCHECK_RET( p, wxT("invalid property id") );
|
|
|
|
if ( !(p->m_flags & wxPG_PROP_UNSPECIFIED) )
|
|
{
|
|
// Flag should be set first - editor class methods may need it
|
|
p->m_flags |= wxPG_PROP_UNSPECIFIED;
|
|
|
|
wxASSERT( m_pPropGrid );
|
|
|
|
if ( m_pPropGrid->GetState() == this )
|
|
{
|
|
if ( m_pPropGrid->m_selected == p && m_pPropGrid->m_wndPrimary )
|
|
{
|
|
p->GetEditorClass()->SetValueToUnspecified(m_pPropGrid->m_wndPrimary);
|
|
}
|
|
}
|
|
|
|
if ( p->GetParentingType() != 0 )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
|
|
size_t i;
|
|
for ( i = 0; i < pwc->GetCount(); i++ )
|
|
SetPropertyUnspecified( pwc->Item(i) );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridState property operations
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::LimitPropertyEditing( wxPGProperty* p, bool limit )
|
|
{
|
|
if ( p )
|
|
{
|
|
if ( limit )
|
|
p->m_flags |= wxPG_PROP_NOEDITOR;
|
|
else
|
|
p->m_flags &= ~(wxPG_PROP_NOEDITOR);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::ClearModifiedStatus( wxPGProperty* p )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
|
|
if ( p->m_flags & wxPG_PROP_MODIFIED )
|
|
{
|
|
p->m_flags &= ~(wxPG_PROP_MODIFIED);
|
|
|
|
if ( m_pPropGrid->GetState() == this )
|
|
{
|
|
// Clear active editor bold
|
|
if ( p == m_selected && m_pPropGrid->m_wndPrimary )
|
|
m_pPropGrid->m_wndPrimary->SetFont( m_pPropGrid->GetFont() );
|
|
|
|
m_pPropGrid->DrawItem( p );
|
|
}
|
|
}
|
|
|
|
if ( pwc->GetParentingType() != 0 )
|
|
{
|
|
size_t i;
|
|
for ( i = 0; i < pwc->GetCount(); i++ )
|
|
ClearModifiedStatus( pwc->Item(i) );
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::Collapse( wxPGProperty* p )
|
|
{
|
|
wxCHECK_MSG( p, false, wxT("invalid property id") );
|
|
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
if ( pwc->GetParentingType() == 0 ) return false;
|
|
|
|
if ( !pwc->m_expanded ) return false;
|
|
|
|
// m_expanded must be set just before call to CalculateYs
|
|
pwc->m_expanded = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::Expand( wxPGProperty* p )
|
|
{
|
|
wxCHECK_MSG( p, false, wxT("invalid property id") );
|
|
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
if ( pwc->GetParentingType() == 0 ) return false;
|
|
|
|
if ( pwc->m_expanded ) return false;
|
|
|
|
// m_expanded must be set just before call to CalculateYs
|
|
pwc->m_expanded = 1;
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::DoSelectProperty( wxPGProperty* p, unsigned int flags )
|
|
{
|
|
if ( this == m_pPropGrid->GetState() )
|
|
return m_pPropGrid->DoSelectProperty( p, flags );
|
|
|
|
m_selected = p;
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::SetPropertyLabel( wxPGProperty* p, const wxString& newlabel )
|
|
{
|
|
wxCHECK_RET(p, wxT("invalid property id"));
|
|
p->SetLabel(newlabel);
|
|
if ( m_pPropGrid->GetWindowStyleFlag() & wxPG_AUTO_SORT )
|
|
Sort(p->GetParent());
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::SetPropertyPriority( wxPGProperty* p, int priority )
|
|
{
|
|
int parenting = p->GetParentingType();
|
|
|
|
if ( priority == wxPG_HIGH ) p->ClearFlag( wxPG_PROP_HIDEABLE );
|
|
else p->SetFlag( wxPG_PROP_HIDEABLE );
|
|
|
|
if ( parenting != 0 )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
size_t i;
|
|
for ( i = 0; i < pwc->GetCount(); i++ )
|
|
SetPropertyPriority(pwc->Item(i),priority);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::SetPropertyAndChildrenFlags( wxPGProperty* p, long flags )
|
|
{
|
|
p->m_flags |= flags;
|
|
|
|
if ( p->GetParentingType() != 0 )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
|
|
size_t i;
|
|
for ( i = 0; i < pwc->GetCount(); i++ )
|
|
ClearPropertyAndChildrenFlags ( pwc->Item(i), flags );
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::ClearPropertyAndChildrenFlags( wxPGProperty* p, long flags )
|
|
{
|
|
p->m_flags &= ~(flags);
|
|
|
|
if ( p->GetParentingType() != 0 )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
|
|
size_t i;
|
|
for ( i = 0; i < pwc->GetCount(); i++ )
|
|
ClearPropertyAndChildrenFlags ( pwc->Item(i), flags );
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridState::EnableProperty( wxPGProperty* p, bool enable )
|
|
{
|
|
if ( p )
|
|
{
|
|
if ( enable )
|
|
{
|
|
if ( !(p->m_flags & wxPG_PROP_DISABLED) )
|
|
return false;
|
|
|
|
// Enabling
|
|
|
|
p->m_flags &= ~(wxPG_PROP_DISABLED);
|
|
}
|
|
else
|
|
{
|
|
if ( p->m_flags & wxPG_PROP_DISABLED )
|
|
return false;
|
|
|
|
// Disabling
|
|
|
|
p->m_flags |= wxPG_PROP_DISABLED;
|
|
|
|
}
|
|
|
|
if ( p->GetParentingType() == 0 )
|
|
return true;
|
|
|
|
// Apply same to sub-properties as well
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)p;
|
|
|
|
size_t i;
|
|
for ( i = 0; i < pwc->GetCount(); i++ )
|
|
EnableProperty ( pwc->Item(i), enable );
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridState wxVariant related routines
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Returns list of wxVariant objects (non-categories and non-sub-properties only).
|
|
// Never includes sub-properties (unless they are parented by wxParentProperty).
|
|
wxVariant wxPropertyGridState::GetPropertyValues( const wxString& listname,
|
|
wxPGId baseparent,
|
|
long flags ) const
|
|
{
|
|
ITEM_ITERATION_DCAE_VARIABLES
|
|
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)wxPGIdToPtr(baseparent);
|
|
|
|
// Root is the default base-parent.
|
|
if ( !pwc )
|
|
pwc = m_properties;
|
|
|
|
wxVariantList temp_list;
|
|
wxVariant v( temp_list, listname );
|
|
|
|
if ( flags & wxPG_KEEP_STRUCTURE )
|
|
{
|
|
wxASSERT( (pwc->GetParentingType() < -1) || (pwc->GetParentingType() > 0) );
|
|
|
|
size_t i;
|
|
for ( i=0; i<pwc->GetCount(); i++ )
|
|
{
|
|
wxPGProperty* p = pwc->Item(i);
|
|
int parenting = p->GetParentingType();
|
|
if ( parenting == 0 || parenting == -1 )
|
|
{
|
|
v.Append( p->GetValueAsVariant() );
|
|
}
|
|
else
|
|
{
|
|
v.Append( GetPropertyValues(p->m_name,wxPGIdGen(p),wxPG_KEEP_STRUCTURE) );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ITEM_ITERATION_INIT((wxPGPropertyWithChildren*)wxPGIdToPtr(baseparent),0)
|
|
ITEM_ITERATION_DCAE_ISP_LOOP_BEGIN
|
|
|
|
// Use a trick to ignore wxParentProperty itself, but not its sub-properties.
|
|
if ( parenting == PT_CUSTOMPROPERTY )
|
|
{
|
|
parenting = PT_CAPTION;
|
|
}
|
|
else if ( parenting <= 0 )
|
|
{
|
|
v.Append ( p->GetValueAsVariant() );
|
|
}
|
|
|
|
ITEM_ITERATION_DCAE_ISP_LOOP_END
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::SetPropertyValues( const wxVariantList& list, wxPGId default_category )
|
|
{
|
|
|
|
unsigned char origFrozen = 1;
|
|
|
|
if ( m_pPropGrid->GetState() == this )
|
|
{
|
|
origFrozen = m_pPropGrid->m_frozen;
|
|
if ( !origFrozen ) m_pPropGrid->Freeze();
|
|
}
|
|
|
|
wxPropertyCategoryClass* use_category = (wxPropertyCategoryClass*)wxPGIdToPtr(default_category);
|
|
|
|
if ( !use_category )
|
|
use_category = (wxPropertyCategoryClass*)m_properties;
|
|
|
|
// Let's iterate over the list of variants.
|
|
wxVariantList::const_iterator node;
|
|
|
|
//for ( wxVariantList::Node *node = list.GetFirst(); node; node = node->GetNext() )
|
|
for ( node = list.begin(); node != list.end(); node++ )
|
|
{
|
|
wxVariant *current = (wxVariant*)*node;
|
|
|
|
// Make sure it is wxVariant.
|
|
wxASSERT( current );
|
|
wxASSERT( wxStrcmp(current->GetClassInfo()->GetClassName(),wxT("wxVariant")) == 0 );
|
|
|
|
if ( current->GetName().length() > 0 )
|
|
{
|
|
wxPGId foundProp = BaseGetPropertyByName(current->GetName());
|
|
if ( wxPGIdIsOk(foundProp) )
|
|
{
|
|
wxPGProperty* p = wxPGIdToPtr(foundProp);
|
|
|
|
const wxPGValueType* vtype = p->GetValueTypePtr();
|
|
|
|
// If it was a list, we still have to go through it.
|
|
if ( current->GetType() == wxT("list") )
|
|
{
|
|
SetPropertyValues( current->GetList(),
|
|
wxPGIdGen(
|
|
p->GetParentingType()>0?p:((wxPGProperty*)NULL)
|
|
) );
|
|
}
|
|
else
|
|
{
|
|
#ifdef __WXDEBUG__
|
|
if ( current->GetType() != vtype->GetTypeName() &&
|
|
current->GetType() != vtype->GetCustomTypeName() )
|
|
{
|
|
wxLogDebug(wxT("wxPropertyGridState::SetPropertyValues Warning: Setting value of property \"%s\" from variant"),
|
|
p->m_name.c_str());
|
|
wxLogDebug(wxT(" but variant's type name (%s) doesn't match either base type name (%s) nor custom type name (%s)."),
|
|
#ifndef __WXPYTHON__
|
|
current->GetType().c_str(),vtype->GetTypeName(),vtype->GetCustomTypeName());
|
|
#else
|
|
current->GetType().c_str(),vtype->GetTypeName().c_str(),vtype->GetCustomTypeName().c_str());
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
vtype->SetValueFromVariant(p,*current);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Is it list?
|
|
if ( current->GetType() != wxT("list") )
|
|
{
|
|
// Not.
|
|
AppendIn(use_category,current->GetName(),wxPG_LABEL,(wxVariant&)*current);
|
|
}
|
|
else
|
|
{
|
|
// Yes, it is; create a sub category and append contents there.
|
|
wxPGId newCat = DoInsert(use_category,-1,new wxPropertyCategoryClass(current->GetName(),wxPG_LABEL));
|
|
SetPropertyValues( current->GetList(), newCat );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( !origFrozen )
|
|
{
|
|
m_pPropGrid->Thaw();
|
|
|
|
if ( this == m_pPropGrid->GetState() )
|
|
{
|
|
m_selected->UpdateControl(m_pPropGrid->m_wndPrimary);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridState property adding and removal
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Call for after sub-properties added with AddChild
|
|
void wxPGPropertyWithChildren::PrepareSubProperties()
|
|
{
|
|
// TODO: When in 1.0.5, move extra stuff from AddChild to here.
|
|
wxPropertyGridState* state = GetParentState();
|
|
|
|
wxASSERT(state);
|
|
|
|
if ( !GetCount() )
|
|
return;
|
|
|
|
wxByte depth = m_depth + 1;
|
|
wxByte depthBgCol = m_depthBgCol;
|
|
|
|
wxByte inheritFlags = m_flags & wxPG_INHERITED_PROPFLAGS;
|
|
|
|
wxByte bgColIndex = m_bgColIndex;
|
|
wxByte fgColIndex = m_fgColIndex;
|
|
|
|
//
|
|
// Set some values to the children
|
|
//
|
|
size_t i = 0;
|
|
wxPGPropertyWithChildren* nparent = this;
|
|
|
|
while ( i < nparent->GetCount() )
|
|
{
|
|
wxPGProperty* np = nparent->Item(i);
|
|
|
|
np->m_flags |= inheritFlags; // Hideable also if parent.
|
|
np->m_depth = depth;
|
|
np->m_depthBgCol = depthBgCol;
|
|
np->m_bgColIndex = bgColIndex;
|
|
np->m_fgColIndex = fgColIndex;
|
|
|
|
// Also handle children of children
|
|
if ( np->GetParentingType() != 0 &&
|
|
((wxPGPropertyWithChildren*)np)->GetCount() > 0 )
|
|
{
|
|
nparent = (wxPGPropertyWithChildren*) np;
|
|
i = 0;
|
|
|
|
// Init
|
|
nparent->m_expanded = 0;
|
|
nparent->m_parentState = state;
|
|
depth++;
|
|
}
|
|
else
|
|
{
|
|
// Next sibling
|
|
i++;
|
|
}
|
|
|
|
// After reaching last sibling, go back to processing
|
|
// siblings of the parent
|
|
while ( i >= nparent->GetCount() )
|
|
{
|
|
// Exit the loop when top parent hit
|
|
if ( nparent == this )
|
|
break;
|
|
|
|
depth--;
|
|
|
|
i = nparent->GetArrIndex() + 1;
|
|
nparent = nparent->GetParent();
|
|
}
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
// Call after fixed sub-properties added/removed after creation.
|
|
// if oldSelInd >= 0 and < new max items, then selection is
|
|
// moved to it. Note: oldSelInd -2 indicates that this property
|
|
// should be selected.
|
|
void wxPGPropertyWithChildren::SubPropsChanged( int oldSelInd )
|
|
{
|
|
wxPropertyGridState* state = GetParentState();
|
|
wxPropertyGrid* grid = state->GetGrid();
|
|
|
|
PrepareSubProperties();
|
|
|
|
wxPGProperty* sel = (wxPGProperty*) NULL;
|
|
if ( oldSelInd >= (int)m_children.GetCount() )
|
|
oldSelInd = (int)m_children.GetCount() - 1;
|
|
|
|
if ( oldSelInd >= 0 )
|
|
sel = (wxPGProperty*) m_children[oldSelInd];
|
|
else if ( oldSelInd == -2 )
|
|
sel = this;
|
|
|
|
if ( sel )
|
|
state->DoSelectProperty(sel);
|
|
|
|
if ( state == grid->GetState() )
|
|
{
|
|
if ( m_expanded )
|
|
grid->CalculateYs( GetParent(), m_arrIndex );
|
|
grid->Refresh();
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
int wxPropertyGridState::PrepareToAddItem( wxPGProperty* property,
|
|
wxPGPropertyWithChildren* scheduledParent )
|
|
{
|
|
wxPropertyGrid* propGrid = m_pPropGrid;
|
|
wxASSERT( propGrid );
|
|
|
|
int parenting = property->GetParentingType();
|
|
|
|
// This will allow better behaviour.
|
|
if ( scheduledParent == m_properties )
|
|
scheduledParent = (wxPGPropertyWithChildren*) NULL;
|
|
|
|
if ( parenting > 0 )
|
|
{
|
|
/*
|
|
if ( scheduledParent )
|
|
wxLogDebug(wxT("scheduledParent= %s, %i"),
|
|
scheduledParent->GetName().c_str(), (int)scheduledParent->GetParentingType());
|
|
*/
|
|
|
|
// Parent of a category must be either root or another category
|
|
// (otherwise Bad Things might happen).
|
|
wxASSERT_MSG( scheduledParent == (wxPGPropertyWithChildren*) NULL ||
|
|
scheduledParent == m_properties ||
|
|
scheduledParent->GetParentingType() > 0,
|
|
wxT("Parent of a category must be either root or another category."));
|
|
|
|
/*
|
|
wxASSERT_MSG( m_properties == &m_regularArray,
|
|
wxT("Do not add categories in non-categoric mode!"));
|
|
*/
|
|
|
|
// If we already have category with same name, delete given property
|
|
// and use it instead as most recent caption item.
|
|
wxPGId found_id = BaseGetPropertyByName( property->GetName() );
|
|
if ( wxPGIdIsOk(found_id) )
|
|
{
|
|
wxPropertyCategoryClass* pwc = (wxPropertyCategoryClass*)wxPGIdToPtr(found_id);
|
|
if ( pwc->GetParentingType() > 0 ) // Must be a category.
|
|
{
|
|
delete property;
|
|
m_currentCategory = pwc;
|
|
return 2; // Tells the caller what we did.
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef __WXDEBUG__
|
|
// Warn for identical names in debug mode.
|
|
if ( property->GetName().length() &&
|
|
wxPGIdIsOk(BaseGetPropertyByName(property->GetName())) &&
|
|
(!scheduledParent || scheduledParent->GetParentingType() >= 1) )
|
|
wxLogError(wxT("wxPropertyGrid: Warning - item with name \"%s\" already exists."),
|
|
property->GetName().c_str());
|
|
#endif
|
|
|
|
// Make sure nothing is selected.
|
|
if ( propGrid && propGrid->m_selected )
|
|
{
|
|
bool selRes = propGrid->ClearSelection();
|
|
wxPG_CHECK_MSG_DBG( selRes,
|
|
-1,
|
|
wxT("failed to deselect a property (editor probably had invalid value)") );
|
|
}
|
|
|
|
property->m_y = -1;
|
|
|
|
if ( scheduledParent )
|
|
{
|
|
// Use parent's colours.
|
|
property->m_bgColIndex = scheduledParent->m_bgColIndex;
|
|
property->m_fgColIndex = scheduledParent->m_fgColIndex;
|
|
}
|
|
|
|
// If in hideable adding mode, or if assigned parent is hideable, then
|
|
// make this one hideable.
|
|
if (
|
|
( scheduledParent && (scheduledParent->m_flags & wxPG_PROP_HIDEABLE) ) ||
|
|
( propGrid && (propGrid->m_iFlags & wxPG_FL_ADDING_HIDEABLES) )
|
|
)
|
|
property->SetFlag ( wxPG_PROP_HIDEABLE );
|
|
|
|
// Set custom image flag.
|
|
int custImgHeight = property->GetImageSize().y;
|
|
if ( custImgHeight < 0 /*|| custImgHeight > 1*/ )
|
|
{
|
|
property->m_flags |= wxPG_PROP_CUSTOMIMAGE;
|
|
}
|
|
|
|
if ( propGrid->GetWindowStyleFlag() & wxPG_LIMITED_EDITING )
|
|
property->m_flags |= wxPG_PROP_NOEDITOR;
|
|
|
|
if ( parenting < 1 )
|
|
{
|
|
// This is not a category.
|
|
|
|
wxASSERT_MSG( property->GetEditorClass(), wxT("Editor class not initialized!") );
|
|
|
|
// Depth.
|
|
//
|
|
unsigned char depth = 1;
|
|
if ( scheduledParent )
|
|
{
|
|
depth = scheduledParent->m_depth;
|
|
if ( scheduledParent->GetParentingType() != PT_CAPTION )
|
|
depth++;
|
|
}
|
|
property->m_depth = depth;
|
|
unsigned char greyDepth = depth;
|
|
|
|
if ( scheduledParent )
|
|
{
|
|
wxPropertyCategoryClass* pc;
|
|
|
|
if ( scheduledParent->GetParentingType() >= PT_CAPTION )
|
|
pc = (wxPropertyCategoryClass*)scheduledParent;
|
|
else
|
|
// This conditional compile is necessary to
|
|
// bypass some compiler bug.
|
|
pc = wxPropertyGrid::_GetPropertyCategory(scheduledParent);
|
|
|
|
if ( pc )
|
|
greyDepth = pc->GetDepth();
|
|
else
|
|
greyDepth = scheduledParent->m_depthBgCol;
|
|
}
|
|
|
|
property->m_depthBgCol = greyDepth;
|
|
|
|
// Add children to propertywithchildren.
|
|
if ( parenting < PT_NONE )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)property;
|
|
|
|
pwc->m_parentState = this;
|
|
|
|
pwc->m_expanded = 0; // Properties with children are not expanded by default.
|
|
if ( propGrid && propGrid->GetWindowStyleFlag() & wxPG_HIDE_MARGIN )
|
|
pwc->m_expanded = 1; // ...unless it cannot not be expanded.
|
|
|
|
if ( pwc->GetCount() )
|
|
{
|
|
pwc->PrepareSubProperties();
|
|
}
|
|
|
|
//
|
|
// If children were added prior to append, then this is considered
|
|
// a "fixed" parent (otherwise the PT_CUSTOMPROPERTY is set, see below,
|
|
// to mark it as customizable).
|
|
/*if ( pwc->GetCount() )
|
|
{
|
|
pwc->PrepareSubProperties();
|
|
}
|
|
else
|
|
{
|
|
pwc->m_parentingType = PT_CUSTOMPROPERTY;
|
|
}*/
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is a category.
|
|
|
|
// depth
|
|
unsigned char depth = 1;
|
|
if ( scheduledParent )
|
|
{
|
|
depth = scheduledParent->m_depth + 1;
|
|
}
|
|
property->m_depth = depth;
|
|
property->m_depthBgCol = depth;
|
|
|
|
m_currentCategory = (wxPropertyCategoryClass*)property;
|
|
|
|
wxPropertyCategoryClass* pc = (wxPropertyCategoryClass*)property;
|
|
pc->m_parentState = this;
|
|
|
|
// Calculate text extent for caption item.
|
|
pc->CalculateTextExtent(propGrid,propGrid->GetCaptionFont());
|
|
}
|
|
|
|
return parenting;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyContainerMethods::BeginAddChildren( wxPGId id )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*) wxPGIdToPtr(id);
|
|
wxCHECK_RET( pwc, wxT("NULL property") );
|
|
wxCHECK_RET( pwc->GetParentingType() == PT_FIXEDCHILDREN, wxT("only call on properties with fixed children") );
|
|
pwc->m_parentingType = PT_CUSTOMPROPERTY;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyContainerMethods::EndAddChildren( wxPGId id )
|
|
{
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*) wxPGIdToPtr(id);
|
|
wxCHECK_RET( pwc, wxT("NULL property") );
|
|
wxCHECK_RET( pwc->GetParentingType() == PT_CUSTOMPROPERTY, wxT("only call on properties for which BeginAddChildren was called prior") );
|
|
pwc->m_parentingType = PT_FIXEDCHILDREN;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridState::Append( wxPGProperty* property )
|
|
{
|
|
wxPropertyCategoryClass* cur_cat = m_currentCategory;
|
|
if ( property->GetParentingType() > 0 )
|
|
cur_cat = (wxPropertyCategoryClass*) NULL;
|
|
|
|
return DoInsert( cur_cat, -1, property );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridState::DoInsert( wxPGPropertyWithChildren* parent, int index, wxPGProperty* property )
|
|
{
|
|
if ( !parent )
|
|
parent = m_properties;
|
|
|
|
wxPropertyGrid* propGrid = m_pPropGrid;
|
|
|
|
wxCHECK_MSG( parent->GetParentingType() != PT_NONE,
|
|
wxNullProperty,
|
|
wxT("this parent cannot accomodate children") );
|
|
|
|
wxCHECK_MSG( parent->GetParentingType() != PT_FIXEDCHILDREN,
|
|
wxNullProperty,
|
|
wxT("when adding properties to fixed parents, use BeginAddChildren and EndAddChildren.") );
|
|
|
|
int parenting = PrepareToAddItem( property, (wxPropertyCategoryClass*)parent );
|
|
|
|
// This type of invalid parenting value indicates we should exit now, returning
|
|
// id of most recent category.
|
|
if ( parenting > PT_CAPTION )
|
|
return wxPGIdGen(m_currentCategory);
|
|
|
|
// Note that item must be added into current mode later.
|
|
|
|
// If parent is wxParentProperty, just stick it in...
|
|
// If parent is root (m_properties), then...
|
|
// In categoric mode: Add as last item in m_abcArray (if not category).
|
|
// Add to given index in m_regularArray.
|
|
// In non-cat mode: Add as last item in m_regularArray.
|
|
// Add to given index in m_abcArray.
|
|
// If parent is category, then...
|
|
// 1) Add to given category in given index.
|
|
// 2) Add as last item in m_abcArray.
|
|
|
|
int parents_parenting = parent->GetParentingType();
|
|
if ( parents_parenting < 0 )
|
|
{
|
|
// Parent is wxParentingProperty: Just stick it in...
|
|
parent->AddChild2( property, index );
|
|
}
|
|
else
|
|
{
|
|
// Parent is Category or Root.
|
|
|
|
if ( m_properties == &m_regularArray )
|
|
{
|
|
// Categorized mode
|
|
|
|
// Only add non-categories to m_abcArray.
|
|
if ( m_abcArray && parenting <= 0 )
|
|
m_abcArray->AddChild2( property, -1, false );
|
|
|
|
// Add to current mode.
|
|
parent->AddChild2( property, index );
|
|
|
|
}
|
|
else
|
|
{
|
|
// Non-categorized mode.
|
|
|
|
if ( parent != m_properties )
|
|
// Parent is category.
|
|
parent->AddChild2( property, index, false );
|
|
else
|
|
// Parent is root.
|
|
m_regularArray.AddChild2( property, -1, false );
|
|
|
|
// Add to current mode (no categories).
|
|
if ( parenting <= 0 )
|
|
m_abcArray->AddChild2( property, index );
|
|
}
|
|
}
|
|
|
|
// category stuff
|
|
if ( parenting > PT_NONE )
|
|
{
|
|
// This is a category caption item.
|
|
|
|
// Last caption is not the bottom one (this info required by append)
|
|
m_lastCaptionBottomnest = 0;
|
|
}
|
|
|
|
// Only add name to hashmap if parent is root or category
|
|
if ( parent->GetParentingType() >= PT_CAPTION && property->m_name.length() )
|
|
m_dictName[property->m_name] = (void*) property;
|
|
|
|
m_itemsAdded = 1;
|
|
|
|
if ( propGrid )
|
|
propGrid->m_bottomy = 0; // this signals y recalculation
|
|
|
|
return wxPGIdGen(property);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridState::AppendIn( wxPGPropertyWithChildren* pwc,
|
|
const wxString& label,
|
|
const wxString& propname,
|
|
wxVariant& value )
|
|
{
|
|
wxPGProperty* p = wxPropertyContainerMethods::
|
|
CreatePropertyByType(value.GetType(),label,propname);
|
|
|
|
if ( p )
|
|
{
|
|
p->GetValueTypePtr()->SetValueFromVariant(p,value);
|
|
return DoInsert(pwc,-1,p);
|
|
}
|
|
return wxPGIdGen((wxPGProperty*)NULL);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::DoDelete( wxPGProperty* item )
|
|
{
|
|
wxCHECK_RET( item != &m_regularArray && item != m_abcArray,
|
|
wxT("wxPropertyGrid: Do not attempt to remove the root item.") );
|
|
|
|
size_t i;
|
|
int parenting = item->GetParentingType();
|
|
unsigned int indinparent = item->GetIndexInParent();
|
|
|
|
wxPGPropertyWithChildren* pwc = (wxPGPropertyWithChildren*)item;
|
|
|
|
wxCHECK_RET( item->GetParent()->GetParentingType() != -1,
|
|
wxT("wxPropertyGrid: Do not attempt to remove sub-properties.") );
|
|
|
|
if ( parenting > 0 )
|
|
{
|
|
// deleting a category
|
|
|
|
// erase category entries from the hash table
|
|
for ( i=0; i<pwc->GetCount(); i++ )
|
|
{
|
|
wxPGProperty* sp = pwc->Item( i );
|
|
if ( sp->GetName().Len() ) m_dictName.erase( wxPGNameConv(sp->GetName()) );
|
|
}
|
|
|
|
if ( pwc == m_currentCategory )
|
|
m_currentCategory = (wxPropertyCategoryClass*) NULL;
|
|
|
|
if ( m_abcArray )
|
|
{
|
|
// Remove children from non-categorized array.
|
|
for ( i=0; i<pwc->GetCount(); i++ )
|
|
{
|
|
wxPGProperty * p = pwc->Item( i );
|
|
wxASSERT( p != NULL );
|
|
if ( p->GetParentingType() <= PT_NONE )
|
|
m_abcArray->m_children.Remove( (void*)p );
|
|
}
|
|
|
|
if ( IsInNonCatMode() )
|
|
m_abcArray->FixIndexesOfChildren();
|
|
}
|
|
}
|
|
|
|
if ( !IsInNonCatMode() )
|
|
{
|
|
// categorized mode - non-categorized array
|
|
|
|
// Remove from non-cat array, but only if parent is in it
|
|
if ( parenting <= 0 && item->GetParent()->GetParentingType() == PT_CAPTION )
|
|
{
|
|
if ( m_abcArray )
|
|
{
|
|
m_abcArray->m_children.Remove( item );
|
|
}
|
|
}
|
|
|
|
// categorized mode - categorized array
|
|
item->m_parent->m_children.RemoveAt(indinparent);
|
|
item->m_parent->FixIndexesOfChildren(/*indinparent*/);
|
|
}
|
|
else
|
|
{
|
|
// non-categorized mode - categorized array
|
|
|
|
// We need to find location of item.
|
|
wxPGPropertyWithChildren* cat_parent = &m_regularArray;
|
|
int cat_index = m_regularArray.GetCount();
|
|
size_t i;
|
|
for ( i = 0; i < m_regularArray.GetCount(); i++ )
|
|
{
|
|
wxPGProperty* p = m_regularArray.Item(i);
|
|
if ( p == item ) { cat_index = i; break; }
|
|
if ( p->GetParentingType() > 0 )
|
|
{
|
|
int subind = ((wxPGPropertyWithChildren*)p)->Index(item);
|
|
if ( subind != wxNOT_FOUND )
|
|
{
|
|
cat_parent = ((wxPGPropertyWithChildren*)p);
|
|
cat_index = subind;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
cat_parent->m_children.RemoveAt(cat_index);
|
|
|
|
// non-categorized mode - non-categorized array
|
|
if ( parenting <= 0 )
|
|
{
|
|
wxASSERT( item->m_parent == m_abcArray );
|
|
item->m_parent->m_children.RemoveAt(indinparent);
|
|
item->m_parent->FixIndexesOfChildren(indinparent);
|
|
}
|
|
}
|
|
|
|
if ( item->GetName().Len() ) m_dictName.erase( wxPGNameConv(item->GetName()) );
|
|
|
|
#ifdef __WXPYTHON__
|
|
// For some reason, Py_DECREF always crashes, even though we make
|
|
// matching Py_INCREF call in propgrid_cbacks.cpp. Maybe refcount is decremented
|
|
// somewhere automatically? Unlikely though...
|
|
//if ( item->m_scriptObject )
|
|
// Py_DECREF( item->m_scriptObject );
|
|
#endif
|
|
|
|
// We can actually delete it now
|
|
delete item;
|
|
|
|
m_itemsAdded = 1; // Not a logical assignment (but required nonetheless).
|
|
|
|
if ( this == m_pPropGrid->GetState() )
|
|
{
|
|
//m_pPropGrid->m_clearThisMany = 1;
|
|
m_pPropGrid->m_bottomy = 0; // this signals y recalculation
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridState init etc.
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::InitNonCatMode()
|
|
{
|
|
ITEM_ITERATION_DCAE_VARIABLES
|
|
|
|
if ( !m_abcArray )
|
|
{
|
|
m_abcArray = new wxPGRootPropertyClass();
|
|
m_abcArray->SetParentState(this);
|
|
m_abcArray->m_expanded = wxPG_EXP_OF_COPYARRAY;
|
|
}
|
|
|
|
// Must be called when FROM_STATE(m_properties) still points to regularArray.
|
|
wxPGPropertyWithChildren* oldProperties = m_properties;
|
|
|
|
// Must use temp value in FROM_STATE(m_properties) for item iteration loop
|
|
// to run as expected.
|
|
m_properties = &m_regularArray;
|
|
|
|
// Copy items.
|
|
ITEM_ITERATION_INIT_FROM_THE_TOP
|
|
|
|
ITEM_ITERATION_DCAE_ISP_LOOP_BEGIN
|
|
|
|
if ( parenting < 1 &&
|
|
( parent == m_properties || parent->GetParentingType() > 0 ) )
|
|
{
|
|
|
|
m_abcArray->AddChild2 ( p );
|
|
p->m_parent = &FROM_STATE(m_regularArray);
|
|
}
|
|
//else wxLogDebug("OUT: %s",p->m_label.c_str());
|
|
|
|
ITEM_ITERATION_DCAE_ISP_LOOP_END
|
|
|
|
m_properties = oldProperties;
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridState::Clear()
|
|
{
|
|
m_regularArray.Empty();
|
|
if ( m_abcArray )
|
|
m_abcArray->Empty();
|
|
|
|
m_dictName.clear();
|
|
|
|
m_currentCategory = (wxPropertyCategoryClass*) NULL;
|
|
m_lastCaptionBottomnest = 1;
|
|
m_itemsAdded = 0;
|
|
|
|
m_selected = (wxPGProperty*) NULL;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGridState::wxPropertyGridState()
|
|
{
|
|
m_pPropGrid = (wxPropertyGrid*) NULL;
|
|
m_regularArray.SetParentState(this);
|
|
m_properties = &m_regularArray;
|
|
m_abcArray = (wxPGRootPropertyClass*) NULL;
|
|
m_currentCategory = (wxPropertyCategoryClass*) NULL;
|
|
m_selected = (wxPGProperty*) NULL;
|
|
m_lastCaptionBottomnest = 1;
|
|
m_itemsAdded = 0;
|
|
m_anyModified = 0;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGridState::~wxPropertyGridState()
|
|
{
|
|
delete m_abcArray;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// wxPropertyGridPopulator
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPopulator::Init( wxPropertyGrid* pg, wxPGId popRoot )
|
|
{
|
|
WX_PG_GLOBALS_LOCKER()
|
|
|
|
m_propGrid = pg;
|
|
m_popRoot = popRoot;
|
|
wxPGGlobalVars->m_offline++;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPropertyGridPopulator::~wxPropertyGridPopulator()
|
|
{
|
|
|
|
//
|
|
// Free unused sets of choices
|
|
wxPGHashMapP2P::iterator it;
|
|
|
|
for( it = m_dictIdChoices.begin(); it != m_dictIdChoices.end(); ++it )
|
|
{
|
|
wxPGChoicesData* data = (wxPGChoicesData*) it->second;
|
|
data->m_refCount--;
|
|
if ( data->m_refCount < 1 )
|
|
delete data;
|
|
}
|
|
|
|
wxPGGlobalVars->m_offline--;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPopulator::HasChoices( wxPGChoicesId id ) const
|
|
{
|
|
wxPGHashMapP2P::const_iterator it = m_dictIdChoices.find(id);
|
|
return ( it != m_dictIdChoices.end() );
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool wxPropertyGridPopulator::BeginChildren()
|
|
{
|
|
if ( wxPGIdIsOk(m_lastProperty) &&
|
|
wxPGIdToPtr(m_lastProperty)->CanHaveExtraChildren() )
|
|
{
|
|
wxLogDebug(wxT("New Parent: %s"),wxPGIdToPtr(m_lastProperty)->GetLabel().c_str());
|
|
m_curParent = m_lastProperty;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
void wxPropertyGridPopulator::AddChoices(wxPGChoicesId choicesId,
|
|
const wxArrayString& choiceLabels,
|
|
const wxArrayInt& choiceValues)
|
|
{
|
|
#ifdef __WXDEBUG__
|
|
// Make sure the id is not used yet
|
|
wxPGHashMapP2P::iterator it = m_dictIdChoices.find(choicesId);
|
|
wxCHECK_RET( it == m_dictIdChoices.end(),
|
|
wxT("added set of choices to same id twice (use HasChoices if necessary)") );
|
|
#endif
|
|
|
|
wxCHECK_RET( choicesId != (wxPGChoicesId)0,
|
|
wxT("choicesId must not be 0/NULL"));
|
|
|
|
wxPGChoices chs(choiceLabels,choiceValues);
|
|
wxPGChoicesData* data = chs.ExtractData();
|
|
m_dictIdChoices[choicesId] = (void*) data;
|
|
|
|
// Artifically reduce refcount to 0 (since nothing uses it yet)
|
|
//data->m_refCount = 0;
|
|
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridPopulator::DoAppend(wxPGProperty* p,
|
|
const wxString& value,
|
|
const wxString& attributes,
|
|
wxPGChoicesId choicesId,
|
|
const wxArrayString& choiceLabels,
|
|
const wxArrayInt& choiceValues)
|
|
{
|
|
wxASSERT( m_propGrid );
|
|
|
|
// Make sure m_curParent is ok
|
|
if ( !wxPGIdIsOk(m_curParent) )
|
|
{
|
|
if ( !wxPGIdIsOk(m_popRoot) )
|
|
m_popRoot = m_propGrid->GetRoot();
|
|
m_curParent = m_popRoot;
|
|
}
|
|
|
|
if ( p )
|
|
{
|
|
|
|
// Set choices
|
|
if ( choicesId )
|
|
{
|
|
wxPGHashMapP2P::iterator it = m_dictIdChoices.find(choicesId);
|
|
|
|
wxPGChoices chs;
|
|
|
|
if ( it != m_dictIdChoices.end() )
|
|
{
|
|
// Already found
|
|
wxPGChoicesData* foundData = (wxPGChoicesData*) it->second;
|
|
chs.AssignData(foundData);
|
|
}
|
|
else
|
|
{
|
|
chs.Set(choiceLabels,choiceValues);
|
|
m_dictIdChoices[choicesId] = (void*) chs.GetData();
|
|
}
|
|
|
|
p->SetChoices(chs);
|
|
|
|
}
|
|
|
|
// Value setter must be before append
|
|
if ( value.length() )
|
|
{
|
|
p->SetValueFromString(value,wxPG_FULL_VALUE);
|
|
}
|
|
|
|
// Set attributes
|
|
if ( attributes.length() )
|
|
wxPropertyGrid::SetPropertyAttributes(p,attributes);
|
|
|
|
// Append to grid
|
|
m_propGrid->AppendIn(m_curParent,p);
|
|
|
|
m_lastProperty = p;
|
|
}
|
|
return wxPGIdGen(p);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridPopulator::AppendByClass(const wxString& classname,
|
|
const wxString& label,
|
|
const wxString& name,
|
|
const wxString& value,
|
|
const wxString& attributes,
|
|
wxPGChoicesId choicesId,
|
|
const wxArrayString& choiceLabels,
|
|
const wxArrayInt& choiceValues)
|
|
{
|
|
wxPGProperty* p = m_propGrid->CreatePropertyByClass(classname,label,name);
|
|
return DoAppend(p,value,attributes,choicesId,choiceLabels,choiceValues);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
wxPGId wxPropertyGridPopulator::AppendByType(const wxString& valuetype,
|
|
const wxString& label,
|
|
const wxString& name,
|
|
const wxString& value,
|
|
const wxString& attributes,
|
|
wxPGChoicesId choicesId,
|
|
const wxArrayString& choiceLabels,
|
|
const wxArrayInt& choiceValues)
|
|
{
|
|
wxPGProperty* p = m_propGrid->CreatePropertyByType(valuetype,label,name);
|
|
return DoAppend(p,value,attributes,choicesId,choiceLabels,choiceValues);
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|