// Author: Jaakko Salli
// Modified by:
// Created: 2004-09-25
-// RCS-ID: $Id:
+// RCS-ID: $Id$
// Copyright: (c) Jaakko Salli
-// Licence: wxWindows license
+// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// For compilers that support precompilation, includes "wx/wx.h".
#pragma hdrstop
#endif
+#if wxUSE_PROPGRID
+
#ifndef WX_PRECOMP
#include "wx/defs.h"
#include "wx/object.h"
#include "wx/stattext.h"
#include "wx/scrolwin.h"
#include "wx/dirdlg.h"
- #include "wx/layout.h"
#include "wx/sizer.h"
#include "wx/textdlg.h"
#include "wx/filedlg.h"
// This define is necessary to prevent macro clearing
#define __wxPG_SOURCE_FILE__
-#include <wx/propgrid/propgrid.h>
-#include <wx/propgrid/editors.h>
+#include "wx/propgrid/propgrid.h"
+#include "wx/propgrid/editors.h"
#if wxPG_USE_RENDERER_NATIVE
- #include <wx/renderer.h>
+ #include "wx/renderer.h"
#endif
-#include <wx/odcombo.h>
+#include "wx/odcombo.h"
#include "wx/timer.h"
#include "wx/dcbuffer.h"
-#include <wx/clipbrd.h>
-#include <wx/dataobj.h>
-
-#ifdef __WXMSW__
- #include <wx/msw/private.h>
-#endif
-
-#include <typeinfo>
+#include "wx/scopeguard.h"
// Two pics for the expand / collapse buttons.
// Files are not supplied with this project (since it is
//#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_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_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_OPTIMAL_WIDTH 200 // Arbitrary
-#define wxPG_MIN_SCROLLBAR_WIDTH 10 // Smallest scrollbar width on any platform
+//#define wxPG_MIN_SCROLLBAR_WIDTH 10 // Smallest scrollbar width on any platform
// Must be larger than largest control border
// width * 2.
//#define wxPG_NAT_CHOICE_BORDER_ANY 0
-#define wxPG_HIDER_BUTTON_HEIGHT 25
+//#define wxPG_HIDER_BUTTON_HEIGHT 25
#define wxPG_PIXELS_PER_UNIT m_lineHeight
#define m_iconHeight m_iconWidth
#endif
-#define wxPG_TOOLTIP_DELAY 1000
+//#define wxPG_TOOLTIP_DELAY 1000
+
+// This is the number of pixels the expander button inside
+// property cells (i.e. not in the grey margin area are
+// adjusted.
+#define IN_CELL_EXPANDER_BUTTON_X_ADJUST 2
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
-const wxChar *wxPropertyGridNameStr = wxT("wxPropertyGrid");
+const char wxPropertyGridNameStr[] = "wxPropertyGrid";
// -----------------------------------------------------------------------
// Statics in one class for easy destruction.
-// NB: We prefer to use wxModule, as it offers more consistent behavior
-// 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>
+#include "wx/module.h"
class wxPGGlobalVarsClassManager : public wxModule
{
public:
wxPGGlobalVarsClassManager() {}
virtual bool OnInit() { wxPGGlobalVars = new wxPGGlobalVarsClass(); return true; }
- virtual void OnExit() { delete wxPGGlobalVars; wxPGGlobalVars = NULL; }
+ virtual void OnExit() { wxDELETE(wxPGGlobalVars); }
};
IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager, wxModule)
-#else // !wxPG_USE_WXMODULE
-class wxPGGlobalVarsClassManager
+// When wxPG is loaded dynamically after the application is already running
+// then the built-in module system won't pick this one up. Add it manually.
+void wxPGInitResourceModule()
{
-public:
- wxPGGlobalVarsClassManager() {}
- ~wxPGGlobalVarsClassManager() { delete wxPGGlobalVars; }
-};
-
-static wxPGGlobalVarsClassManager gs_pgGlobalVarsClassManager;
-
-#endif
-
+ wxModule* module = new wxPGGlobalVarsClassManager;
+ module->Init();
+ wxModule::RegisterModule(module);
+}
-wxPGGlobalVarsClass* wxPGGlobalVars = (wxPGGlobalVarsClass*) NULL;
+wxPGGlobalVarsClass* wxPGGlobalVars = NULL;
wxPGGlobalVarsClass::wxPGGlobalVarsClass()
m_boolChoices.Add(_("False"));
m_boolChoices.Add(_("True"));
- m_fontFamilyChoices = (wxPGChoices*) NULL;
+ m_fontFamilyChoices = NULL;
m_defaultRenderer = new wxPGDefaultRenderer();
wxVariant v;
- v = (long)0;
- wxVariantClassInfo_long = wxPGVariantDataGetClassInfo(v.GetData());
-
- v = wxString();
- wxVariantClassInfo_string = wxPGVariantDataGetClassInfo(v.GetData());
-
- v = (double)0.0;
- wxVariantClassInfo_double = wxPGVariantDataGetClassInfo(v.GetData());
-
- v = (bool)false;
- wxVariantClassInfo_bool = wxPGVariantDataGetClassInfo(v.GetData());
-
- v = wxArrayString();
- wxVariantClassInfo_arrstring = wxPGVariantDataGetClassInfo(v.GetData());
-
- wxColour col;
- wxVariant v2((wxObject*)&col);
- wxVariantClassInfo_wxobject = wxPGVariantDataGetClassInfo(v2.GetData());
-
- wxVariantList list;
- v = wxVariant(list);
- wxVariantClassInfo_list = wxPGVariantDataGetClassInfo(v.GetData());
-
- v << *wxRED;
- wxVariantClassInfo_wxColour = wxPGVariantDataGetClassInfo(v.GetData());
-
-#if wxUSE_DATETIME
- v = wxVariant(wxDateTime::Now());
- wxVariantClassInfo_datetime = wxPGVariantDataGetClassInfo(v.GetData());
-#endif
-
- // Prepare some shared variants
+ // Prepare some shared variants
m_vEmptyString = wxString();
m_vZero = (long) 0;
m_vMinusOne = (long) -1;
m_vFalse = false;
// Prepare cached string constants
+ m_strstring = wxS("string");
+ m_strlong = wxS("long");
+ m_strbool = wxS("bool");
+ m_strlist = wxS("list");
+ m_strDefaultValue = wxS("DefaultValue");
m_strMin = wxS("Min");
m_strMax = wxS("Max");
m_strUnits = wxS("Units");
+ m_strHint = wxS("Hint");
+#if wxPG_COMPATIBILITY_1_4
m_strInlineHelp = wxS("InlineHelp");
+#endif
-#ifdef __WXDEBUG__
m_warnings = 0;
-#endif
}
delete ((wxPGEditor*)vt_it->second);
}
- delete wxPGProperty::sm_wxPG_LABEL;
-}
-
-// -----------------------------------------------------------------------
-// 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;
-}
-
+ // Make sure the global pointers have been reset
+ wxASSERT(wxPG_EDITOR(TextCtrl) == NULL);
+ wxASSERT(wxPG_EDITOR(ChoiceAndButton) == NULL);
-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());
+ delete wxPGProperty::sm_wxPG_LABEL;
}
-
-// -----------------------------------------------------------------------
-// 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()
-
-// -----------------------------------------------------------------------
-// wxPGCanvas
-// -----------------------------------------------------------------------
-
-//
-// wxPGCanvas acts as a graphics sub-window of the
-// wxScrolledWindow that wxPropertyGrid is.
-//
-class wxPGCanvas : public wxPanel
+void wxPropertyGridInitGlobalsIfNeeded()
{
-public:
- wxPGCanvas() : wxPanel()
- {
- }
- virtual ~wxPGCanvas() { }
-
-protected:
- void OnMouseMove( wxMouseEvent &event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnMouseMove( event );
- }
-
- void OnMouseClick( wxMouseEvent &event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnMouseClick( event );
- }
-
- void OnMouseUp( wxMouseEvent &event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnMouseUp( event );
- }
-
- void OnMouseRightClick( wxMouseEvent &event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnMouseRightClick( event );
- }
-
- void OnMouseDoubleClick( wxMouseEvent &event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnMouseDoubleClick( event );
- }
-
- void OnKey( wxKeyEvent& event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnKey( event );
- }
-
- void OnKeyUp( wxKeyEvent& event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnKeyUp( event );
- }
-
- void OnNavigationKey( wxNavigationKeyEvent& event )
- {
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- pg->OnNavigationKey( event );
- }
-
- void OnPaint( wxPaintEvent& event );
-
-private:
- DECLARE_EVENT_TABLE()
-};
-
-
-BEGIN_EVENT_TABLE(wxPGCanvas, wxPanel)
- EVT_MOTION(wxPGCanvas::OnMouseMove)
- EVT_PAINT(wxPGCanvas::OnPaint)
- EVT_LEFT_DOWN(wxPGCanvas::OnMouseClick)
- EVT_LEFT_UP(wxPGCanvas::OnMouseUp)
- EVT_RIGHT_UP(wxPGCanvas::OnMouseRightClick)
- EVT_LEFT_DCLICK(wxPGCanvas::OnMouseDoubleClick)
- EVT_KEY_DOWN(wxPGCanvas::OnKey)
- EVT_KEY_UP(wxPGCanvas::OnKeyUp)
- EVT_CHAR(wxPGCanvas::OnKey)
- EVT_NAVIGATION_KEY(wxPGCanvas::OnNavigationKey)
-END_EVENT_TABLE()
-
-
-void wxPGCanvas::OnPaint( wxPaintEvent& WXUNUSED(event) )
-{
- wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
- wxASSERT( pg->IsKindOf(CLASSINFO(wxPropertyGrid)) );
-
- wxPaintDC dc(this);
-
- // Don't paint after destruction has begun
- if ( !(pg->GetInternalFlags() & wxPG_FL_INITIALIZED) )
- return;
-
- // Update everything inside the box
- wxRect r = GetUpdateRegion().GetBox();
-
- // Repaint this rectangle
- pg->DrawItems( dc, r.y, r.y + r.height, &r );
-
- // We assume that the size set when grid is shown
- // is what is desired.
- pg->SetInternalFlag(wxPG_FL_GOOD_SIZE_SET);
}
// -----------------------------------------------------------------------
// wxPropertyGrid
// -----------------------------------------------------------------------
-IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid, wxScrolledWindow)
+IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid, wxControl)
-BEGIN_EVENT_TABLE(wxPropertyGrid, wxScrolledWindow)
+BEGIN_EVENT_TABLE(wxPropertyGrid, wxControl)
EVT_IDLE(wxPropertyGrid::OnIdle)
- EVT_MOTION(wxPropertyGrid::OnMouseMoveBottom)
EVT_PAINT(wxPropertyGrid::OnPaint)
EVT_SIZE(wxPropertyGrid::OnResize)
EVT_ENTER_WINDOW(wxPropertyGrid::OnMouseEntry)
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)
+ EVT_MOTION(wxPropertyGrid::OnMouseMove)
+ EVT_LEFT_DOWN(wxPropertyGrid::OnMouseClick)
+ EVT_LEFT_UP(wxPropertyGrid::OnMouseUp)
+ EVT_RIGHT_UP(wxPropertyGrid::OnMouseRightClick)
+ EVT_LEFT_DCLICK(wxPropertyGrid::OnMouseDoubleClick)
+ EVT_KEY_DOWN(wxPropertyGrid::OnKey)
END_EVENT_TABLE()
-
// -----------------------------------------------------------------------
wxPropertyGrid::wxPropertyGrid()
- : wxScrolledWindow()
+ : wxControl(), wxScrollHelper(this)
{
Init1();
}
const wxPoint& pos,
const wxSize& size,
long style,
- const wxChar* name )
- : wxScrolledWindow()
+ const wxString& name )
+ : wxControl(), wxScrollHelper(this)
{
Init1();
Create(parent,id,pos,size,style,name);
const wxPoint& pos,
const wxSize& size,
long style,
- const wxChar* name )
+ const wxString& name )
{
- if ( !(style&wxBORDER_MASK) )
- style |= wxSIMPLE_BORDER;
+ if (!(style&wxBORDER_MASK))
+ {
+ style |= wxBORDER_THEME;
+ }
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
+ // Filter out wxTAB_TRAVERSAL - we will handle TABs manually
+ style &= ~(wxTAB_TRAVERSAL);
+ style |= wxWANTS_CHARS;
- wxScrolledWindow::Create(parent,id,pos,size,style,name);
+ wxControl::Create(parent, id, pos, size,
+ style | wxScrolledWindowStyle,
+ wxDefaultValidator,
+ name);
Init2();
//
void wxPropertyGrid::Init1()
{
-#if !wxPG_USE_WXMODULE
- if ( !wxPGGlobalVars )
- wxPGGlobalVars = new wxPGGlobalVarsClass();
-#endif
-
// Register editor classes, if necessary.
if ( wxPGGlobalVars->m_mapEditorClasses.empty() )
- RegisterDefaultEditors();
+ wxPropertyGrid::RegisterDefaultEditors();
+ m_validatingEditor = 0;
m_iFlags = 0;
- m_pState = (wxPropertyGridPageState*) NULL;
- m_wndEditor = m_wndEditor2 = (wxWindow*) NULL;
- m_selected = (wxPGProperty*) NULL;
- m_selColumn = -1;
- m_propHover = (wxPGProperty*) NULL;
+ m_pState = NULL;
+ m_wndEditor = m_wndEditor2 = NULL;
+ m_selColumn = 1;
+ m_colHover = 1;
+ m_propHover = NULL;
+ m_labelEditor = NULL;
+ m_labelEditorProperty = NULL;
m_eventObject = this;
- m_curFocused = (wxWindow*) NULL;
- m_tlwHandler = NULL;
- m_inDoPropertyChanged = 0;
- m_inCommitChangesFromEditor = 0;
- m_inDoSelectProperty = 0;
+ m_curFocused = NULL;
+ m_processedEvent = NULL;
+ m_tlp = NULL;
+ m_sortFunction = NULL;
+ m_inDoPropertyChanged = false;
+ m_inCommitChangesFromEditor = false;
+ m_inDoSelectProperty = false;
+ m_inOnValidationFailure = false;
m_permanentValidationFailureBehavior = wxPG_VFB_DEFAULT;
m_dragStatus = 0;
m_mouseSide = 16;
m_editorFocused = 0;
+ // Set up default unspecified value 'colour'
+ m_unspecifiedAppearance.SetFgCol(*wxLIGHT_GREY);
+
// Set default keys
AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RIGHT );
AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_DOWN );
AddActionTrigger( wxPG_ACTION_EXPAND_PROPERTY, WXK_RIGHT);
AddActionTrigger( wxPG_ACTION_COLLAPSE_PROPERTY, WXK_LEFT);
AddActionTrigger( wxPG_ACTION_CANCEL_EDIT, WXK_ESCAPE );
- AddActionTrigger( wxPG_ACTION_CUT, 'X', wxMOD_CONTROL );
- AddActionTrigger( wxPG_ACTION_CUT, WXK_DELETE, wxMOD_SHIFT );
- AddActionTrigger( wxPG_ACTION_COPY, 'C', wxMOD_CONTROL);
- AddActionTrigger( wxPG_ACTION_COPY, WXK_INSERT, wxMOD_CONTROL );
- AddActionTrigger( wxPG_ACTION_PASTE, 'V', wxMOD_CONTROL );
- AddActionTrigger( wxPG_ACTION_PASTE, WXK_INSERT, wxMOD_SHIFT );
+ AddActionTrigger( wxPG_ACTION_PRESS_BUTTON, WXK_DOWN, wxMOD_ALT );
+ AddActionTrigger( wxPG_ACTION_PRESS_BUTTON, WXK_F4 );
m_coloursCustomized = 0;
m_frozen = 0;
- m_canvas = NULL;
-
-#if wxPG_DOUBLE_BUFFER
- m_doubleBuffer = (wxBitmap*) NULL;
-#endif
-
- m_windowsToDelete = NULL;
+ m_doubleBuffer = NULL;
#ifndef wxPG_ICON_WIDTH
- m_expandbmp = NULL;
- m_collbmp = NULL;
- m_iconWidth = 11;
- m_iconHeight = 11;
+ m_expandbmp = NULL;
+ m_collbmp = NULL;
+ m_iconWidth = 11;
+ m_iconHeight = 11;
#else
m_iconWidth = wxPG_ICON_WIDTH;
#endif
m_width = m_height = 0;
- SetButtonShortcut(0);
-
- m_keyComboConsumed = 0;
-
m_commonValues.push_back(new wxPGCommonValue(_("Unspecified"), wxPGGlobalVars->m_defaultRenderer) );
m_cvUnspecified = 0;
#ifdef __WXMAC__
// Smaller controls on Mac
SetWindowVariant(wxWINDOW_VARIANT_SMALL);
-#endif
+#endif
// Now create state, if one didn't exist already
// (wxPropertyGridManager might have created it for us).
}
if ( !(m_windowStyle & wxPG_SPLITTER_AUTO_CENTER) )
- m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
+ m_pState->m_dontCenterSplitter = true;
if ( m_windowStyle & wxPG_HIDE_CATEGORIES )
{
#ifndef wxPG_ICON_WIDTH
// create two bitmap nodes for drawing
- m_expandbmp = new wxBitmap(expand_xpm);
- m_collbmp = new wxBitmap(collapse_xpm);
+ m_expandbmp = new wxBitmap(expand_xpm);
+ m_collbmp = new wxBitmap(collapse_xpm);
- // calculate average font height for bitmap centering
+ // calculate average font height for bitmap centering
- m_iconWidth = m_expandbmp->GetWidth();
- m_iconHeight = m_expandbmp->GetHeight();
+ 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
+ // 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());
+ CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING );
- // Add base colour items
- m_arrFgCols.Add((void*)new wxPGColour());
- m_arrFgCols.Add((void*)new wxPGColour());
+ // Allocate cell datas
+ m_propertyDefaultCell.SetEmptyData();
+ m_categoryDefaultCell.SetEmptyData();
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);
+ // Hook the top-level parent
+ m_tlpClosed = NULL;
+ m_tlpClosedTime = 0;
- // set virtual size to this window size
+ // set virtual size to this window size
wxSize wndsize = GetSize();
- SetVirtualSize(wndsize.GetWidth(), wndsize.GetWidth());
+ SetVirtualSize(wndsize.GetWidth(), wndsize.GetWidth());
m_timeCreated = ::wxGetLocalTimeMillis();
- m_canvas = new wxPGCanvas();
- m_canvas->Create(this, 1, wxPoint(0, 0), GetClientSize(),
- (GetWindowStyle() & wxTAB_TRAVERSAL) | wxWANTS_CHARS | wxCLIP_CHILDREN);
- m_canvas->SetBackgroundStyle( wxBG_STYLE_CUSTOM );
-
m_iFlags |= wxPG_FL_INITIALIZED;
m_ncWidth = wndsize.GetWidth();
{
size_t i;
- DoSelectProperty(NULL);
+#if wxUSE_THREADS
+ wxCriticalSectionLocker(wxPGGlobalVars->m_critSect);
+#endif
+
+ //
+ // Remove grid and property pointers from live wxPropertyGridEvents.
+ for ( i=0; i<m_liveEvents.size(); i++ )
+ {
+ wxPropertyGridEvent* evt = m_liveEvents[i];
+ evt->SetPropertyGrid(NULL);
+ evt->SetProperty(NULL);
+ }
+ m_liveEvents.clear();
+
+ if ( m_processedEvent )
+ {
+ // All right... we are being deleted while wxPropertyGrid event
+ // is being sent. Make sure that event propagates as little
+ // as possible (although usually this is not enough to prevent
+ // a crash).
+ m_processedEvent->Skip(false);
+ m_processedEvent->StopPropagation();
+
+ // Let's use wxMessageBox to make the message appear more
+ // reliably (and *before* the crash can happen).
+ ::wxMessageBox("wxPropertyGrid was being destroyed in an event "
+ "generated by it. This usually leads to a crash "
+ "so it is recommended to destroy the control "
+ "at idle time instead.");
+ }
+
+ DoSelectProperty(NULL, wxPG_SEL_NOVALIDATE|wxPG_SEL_DONT_SEND_EVENT);
// This should do prevent things from going too badly wrong
m_iFlags &= ~(wxPG_FL_INITIALIZED);
if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
- m_canvas->ReleaseMouse();
+ ReleaseMouse();
- wxPGTLWHandler* handler = (wxPGTLWHandler*) m_tlwHandler;
- m_tlp->RemoveEventHandler(handler);
- delete handler;
+ // Call with NULL to disconnect event handling
+ if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING )
+ {
+ OnTLPChanging(NULL);
-#ifdef __WXDEBUG__
- if ( IsEditorsValueModified() )
- ::wxMessageBox(wxS("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).)"),
- wxS("wxPropertyGrid Debug Warning") );
-#endif
+ wxASSERT_MSG( !IsEditorsValueModified(),
+ wxS("Most recent change in property editor was ")
+ wxS("lost!!! (if you don't want this to happen, ")
+ wxS("close your frames and dialogs using ")
+ wxS("Close(false).)") );
+ }
-#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;
+ delete m_expandbmp;
+ delete m_collbmp;
#endif
- // Delete cached text colours.
- for ( i=0; i<m_arrFgCols.size(); i++ )
- {
- delete (wxPGColour*)m_arrFgCols.Item(i);
- }
-
- // Delete cached brushes.
- for ( i=0; i<m_arrBgBrushes.size(); i++ )
- {
- delete (wxPGBrush*)m_arrBgBrushes.Item(i);
- }
-
// Delete common value records
for ( i=0; i<m_commonValues.size(); i++ )
{
- delete GetCommonValue(i);
+ // Use temporary variable to work around possible strange VC6 (asserts because m_size is zero)
+ wxPGCommonValue* value = m_commonValues[i];
+ delete value;
}
}
bool wxPropertyGrid::Destroy()
{
if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
- m_canvas->ReleaseMouse();
+ ReleaseMouse();
- return wxScrolledWindow::Destroy();
+ return wxControl::Destroy();
}
// -----------------------------------------------------------------------
//
// Tooltips disabled
//
- m_canvas->SetToolTip( (wxToolTip*) NULL );
+ SetToolTip( NULL );
}
#endif
}
- wxScrolledWindow::SetWindowStyleFlag ( style );
+ wxControl::SetWindowStyleFlag ( style );
if ( m_iFlags & wxPG_FL_INITIALIZED )
{
// -----------------------------------------------------------------------
-void wxPropertyGrid::Freeze()
+void wxPropertyGrid::Freeze()
+{
+ if ( !m_frozen )
+ {
+ wxControl::Freeze();
+ }
+ m_frozen++;
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::Thaw()
+{
+ m_frozen--;
+
+ if ( !m_frozen )
+ {
+ wxControl::Thaw();
+ RecalculateVirtualSize();
+ #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
+ Refresh();
+ #endif
+
+ // Force property re-selection
+ // NB: We must copy the selection.
+ wxArrayPGProperty selection = m_pState->m_selection;
+ DoSetSelection(selection, wxPG_SEL_FORCE | wxPG_SEL_NONVISIBLE);
+ }
+}
+
+// -----------------------------------------------------------------------
+
+bool wxPropertyGrid::DoAddToSelection( wxPGProperty* prop, int selFlags )
+{
+ wxCHECK( prop, false );
+
+ if ( !(GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION) )
+ return DoSelectProperty(prop, selFlags);
+
+ wxArrayPGProperty& selection = m_pState->m_selection;
+
+ if ( !selection.size() )
+ {
+ return DoSelectProperty(prop, selFlags);
+ }
+ else
+ {
+ // For categories, only one can be selected at a time
+ if ( prop->IsCategory() || selection[0]->IsCategory() )
+ return true;
+
+ selection.push_back(prop);
+
+ if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) )
+ {
+ SendEvent( wxEVT_PG_SELECTED, prop, NULL );
+ }
+
+ DrawItem(prop);
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty* prop, int selFlags )
+{
+ wxCHECK( prop, false );
+ bool res;
+
+ wxArrayPGProperty& selection = m_pState->m_selection;
+ if ( selection.size() <= 1 )
+ {
+ res = DoSelectProperty(NULL, selFlags);
+ }
+ else
+ {
+ m_pState->DoRemoveFromSelection(prop);
+ DrawItem(prop);
+ res = true;
+ }
+
+ return res;
+}
+
+// -----------------------------------------------------------------------
+
+bool wxPropertyGrid::DoSelectAndEdit( wxPGProperty* prop,
+ unsigned int colIndex,
+ unsigned int selFlags )
+{
+ //
+ // NB: Enable following if label editor background colour is
+ // ever changed to any other than m_colSelBack.
+ //
+ // We use this workaround to prevent visible flicker when editing
+ // a cell. Atleast on wxMSW, there is a difficult to find
+ // (and perhaps prevent) redraw somewhere between making property
+ // selected and enabling label editing.
+ //
+ //wxColour prevColSelBack = m_colSelBack;
+ //m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
+
+ bool res;
+
+ if ( colIndex == 1 )
+ {
+ res = DoSelectProperty(prop, selFlags);
+ }
+ else
+ {
+ // send event
+ DoClearSelection(false, wxPG_SEL_NO_REFRESH);
+
+ if ( m_pState->m_editableColumns.Index(colIndex) == wxNOT_FOUND )
+ {
+ res = DoAddToSelection(prop, selFlags);
+ }
+ else
+ {
+ res = DoAddToSelection(prop, selFlags|wxPG_SEL_NO_REFRESH);
+
+ DoBeginLabelEdit(colIndex, selFlags);
+ }
+ }
+
+ //m_colSelBack = prevColSelBack;
+ return res;
+}
+
+// -----------------------------------------------------------------------
+
+bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop,
+ unsigned int colIndex,
+ wxMouseEvent* mouseEvent,
+ int selFlags )
+{
+ const wxArrayPGProperty& selection = GetSelectedProperties();
+ bool alreadySelected = m_pState->DoIsPropertySelected(prop);
+ bool res = true;
+
+ // Set to 2 if also add all items in between
+ int addToExistingSelection = 0;
+
+ if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION )
+ {
+ if ( mouseEvent )
+ {
+ if ( mouseEvent->GetEventType() == wxEVT_RIGHT_DOWN ||
+ mouseEvent->GetEventType() == wxEVT_RIGHT_UP )
+ {
+ // Allow right-click for context menu without
+ // disturbing the selection.
+ if ( GetSelectedProperties().size() <= 1 ||
+ !alreadySelected )
+ return DoSelectAndEdit(prop, colIndex, selFlags);
+ return true;
+ }
+ else
+ {
+ if ( mouseEvent->ControlDown() )
+ {
+ addToExistingSelection = 1;
+ }
+ else if ( mouseEvent->ShiftDown() )
+ {
+ if ( selection.size() > 0 && !prop->IsCategory() )
+ addToExistingSelection = 2;
+ else
+ addToExistingSelection = 1;
+ }
+ }
+ }
+ }
+
+ if ( addToExistingSelection == 1 )
+ {
+ // Add/remove one
+ if ( !alreadySelected )
+ {
+ res = DoAddToSelection(prop, selFlags);
+ }
+ else if ( GetSelectedProperties().size() > 1 )
+ {
+ res = DoRemoveFromSelection(prop, selFlags);
+ }
+ }
+ else if ( addToExistingSelection == 2 )
+ {
+ // Add this, and all in between
+
+ // Find top selected property
+ wxPGProperty* topSelProp = selection[0];
+ int topSelPropY = topSelProp->GetY();
+ for ( unsigned int i=1; i<selection.size(); i++ )
+ {
+ wxPGProperty* p = selection[i];
+ int y = p->GetY();
+ if ( y < topSelPropY )
+ {
+ topSelProp = p;
+ topSelPropY = y;
+ }
+ }
+
+ wxPGProperty* startFrom;
+ wxPGProperty* stopAt;
+
+ if ( prop->GetY() <= topSelPropY )
+ {
+ // Property is above selection (or same)
+ startFrom = prop;
+ stopAt = topSelProp;
+ }
+ else
+ {
+ // Property is below selection
+ startFrom = topSelProp;
+ stopAt = prop;
+ }
+
+ // Iterate through properties in-between, and select them
+ wxPropertyGridIterator it;
+
+ for ( it = GetIterator(wxPG_ITERATE_VISIBLE, startFrom);
+ !it.AtEnd();
+ it++ )
+ {
+ wxPGProperty* p = *it;
+
+ if ( !p->IsCategory() &&
+ !m_pState->DoIsPropertySelected(p) )
+ {
+ DoAddToSelection(p, selFlags);
+ }
+
+ if ( p == stopAt )
+ break;
+ }
+ }
+ else
+ {
+ res = DoSelectAndEdit(prop, colIndex, selFlags);
+ }
+
+ return res;
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::DoSetSelection( const wxArrayPGProperty& newSelection,
+ int selFlags )
+{
+ if ( newSelection.size() > 0 )
+ {
+ if ( !DoSelectProperty(newSelection[0], selFlags) )
+ return;
+ }
+ else
+ {
+ DoClearSelection(false, selFlags);
+ }
+
+ for ( unsigned int i = 1; i < newSelection.size(); i++ )
+ {
+ DoAddToSelection(newSelection[i], selFlags);
+ }
+
+ Refresh();
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::MakeColumnEditable( unsigned int column,
+ bool editable )
+{
+ wxASSERT( column != 1 );
+
+ wxArrayInt& cols = m_pState->m_editableColumns;
+
+ if ( editable )
+ {
+ cols.push_back(column);
+ }
+ else
+ {
+ for ( int i = cols.size() - 1; i > 0; i-- )
+ {
+ if ( cols[i] == (int)column )
+ cols.erase( cols.begin() + i );
+ }
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::DoBeginLabelEdit( unsigned int colIndex,
+ int selFlags )
+{
+ wxPGProperty* selected = GetSelection();
+ wxCHECK_RET(selected, wxT("No property selected"));
+ wxCHECK_RET(colIndex != 1, wxT("Do not use this for column 1"));
+
+ if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) )
+ {
+ if ( SendEvent( wxEVT_PG_LABEL_EDIT_BEGIN,
+ selected, NULL, 0,
+ colIndex ) )
+ return;
+ }
+
+ wxString text;
+ const wxPGCell* cell = NULL;
+ if ( selected->HasCell(colIndex) )
+ {
+ cell = &selected->GetCell(colIndex);
+ if ( !cell->HasText() && colIndex == 0 )
+ text = selected->GetLabel();
+ }
+
+ if ( !cell )
+ {
+ if ( colIndex == 0 )
+ text = selected->GetLabel();
+ else
+ cell = &selected->GetOrCreateCell(colIndex);
+ }
+
+ if ( cell && cell->HasText() )
+ text = cell->GetText();
+
+ DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE); // send event
+
+ m_selColumn = colIndex;
+
+ wxRect r = GetEditorWidgetRect(selected, m_selColumn);
+
+ wxWindow* tc = GenerateEditorTextCtrl(r.GetPosition(),
+ r.GetSize(),
+ text,
+ NULL,
+ wxTE_PROCESS_ENTER,
+ 0,
+ colIndex);
+
+ wxWindowID id = tc->GetId();
+ tc->Connect(id, wxEVT_COMMAND_TEXT_ENTER,
+ wxCommandEventHandler(wxPropertyGrid::OnLabelEditorEnterPress),
+ NULL, this);
+ tc->Connect(id, wxEVT_KEY_DOWN,
+ wxKeyEventHandler(wxPropertyGrid::OnLabelEditorKeyPress),
+ NULL, this);
+
+ tc->SetFocus();
+
+ m_labelEditor = wxStaticCast(tc, wxTextCtrl);
+ m_labelEditorProperty = selected;
+}
+
+// -----------------------------------------------------------------------
+
+void
+wxPropertyGrid::OnLabelEditorEnterPress( wxCommandEvent& WXUNUSED(event) )
+{
+ DoEndLabelEdit(true);
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::OnLabelEditorKeyPress( wxKeyEvent& event )
{
- if ( !m_frozen )
+ int keycode = event.GetKeyCode();
+
+ if ( keycode == WXK_ESCAPE )
{
- wxScrolledWindow::Freeze();
+ DoEndLabelEdit(false);
+ }
+ else
+ {
+ HandleKeyEvent(event, true);
}
- m_frozen++;
}
// -----------------------------------------------------------------------
-void wxPropertyGrid::Thaw()
+void wxPropertyGrid::DoEndLabelEdit( bool commit, int selFlags )
{
- m_frozen--;
+ if ( !m_labelEditor )
+ return;
- if ( !m_frozen )
+ wxPGProperty* prop = m_labelEditorProperty;
+ wxASSERT(prop);
+
+ if ( commit )
{
- wxScrolledWindow::Thaw();
- RecalculateVirtualSize();
- #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- m_canvas->Refresh();
- #endif
+ if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) )
+ {
+ // wxPG_SEL_NOVALIDATE is passed correctly in selFlags
+ if ( SendEvent( wxEVT_PG_LABEL_EDIT_ENDING,
+ prop, NULL, selFlags,
+ m_selColumn ) )
+ return;
+ }
- // Force property re-selection
- if ( m_selected )
- DoSelectProperty(m_selected, wxPG_SEL_FORCE);
+ wxString text = m_labelEditor->GetValue();
+ wxPGCell* cell = NULL;
+ if ( prop->HasCell(m_selColumn) )
+ {
+ cell = &prop->GetCell(m_selColumn);
+ }
+ else
+ {
+ if ( m_selColumn == 0 )
+ prop->SetLabel(text);
+ else
+ cell = &prop->GetOrCreateCell(m_selColumn);
+ }
+
+ if ( cell )
+ cell->SetText(text);
}
+
+ m_selColumn = 1;
+ int wasFocused = m_iFlags & wxPG_FL_FOCUSED;
+
+ DestroyEditorWnd(m_labelEditor);
+
+ m_labelEditor = NULL;
+ m_labelEditorProperty = NULL;
+
+ // Fix focus (needed at least on wxGTK)
+ if ( wasFocused )
+ SetFocusOnCanvas();
+
+ DrawItem(prop);
}
// -----------------------------------------------------------------------
void wxPropertyGrid::SetExtraStyle( long exStyle )
{
+ if ( exStyle & wxPG_EX_ENABLE_TLP_TRACKING )
+ OnTLPChanging(::wxGetTopLevelParent(this));
+ else
+ OnTLPChanging(NULL);
+
if ( exStyle & wxPG_EX_NATIVE_DOUBLE_BUFFERING )
{
#if defined(__WXMSW__)
}
else
{
- #if wxPG_DOUBLE_BUFFER
- delete m_doubleBuffer;
- m_doubleBuffer = NULL;
- #endif
+ wxDELETE(m_doubleBuffer);
}
}
- wxScrolledWindow::SetExtraStyle( exStyle );
+ wxControl::SetExtraStyle( exStyle );
if ( exStyle & wxPG_EX_INIT_NOCAT )
m_pState->InitNonCatMode();
// 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 );
+ int lineHeight = wxMax(15, m_lineHeight);
+
+ // don't make the grid too tall (limit height to 10 items) but don't
+ // make it too small neither
+ int numLines = wxMin
+ (
+ wxMax(m_pState->m_properties->GetChildCount(), 3),
+ 10
+ );
+
+ wxClientDC dc(const_cast<wxPropertyGrid *>(this));
+ int width = m_marginWidth;
+ for ( unsigned int i = 0; i < m_pState->m_colWidths.size(); i++ )
+ {
+ width += m_pState->GetColumnFitWidth(dc, m_pState->DoGetRoot(), i, true);
+ }
+
+ const wxSize sz = wxSize(width, lineHeight*numLines + 40);
CacheBestSize(sz);
return sz;
}
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::OnTLPChanging( wxWindow* newTLP )
+{
+ if ( newTLP == m_tlp )
+ return;
+
+ wxLongLong currentTime = ::wxGetLocalTimeMillis();
+
+ //
+ // Parent changed so let's redetermine and re-hook the
+ // correct top-level window.
+ if ( m_tlp )
+ {
+ m_tlp->Disconnect( wxEVT_CLOSE_WINDOW,
+ wxCloseEventHandler(wxPropertyGrid::OnTLPClose),
+ NULL, this );
+ m_tlpClosed = m_tlp;
+ m_tlpClosedTime = currentTime;
+ }
+
+ if ( newTLP )
+ {
+ // Only accept new tlp if same one was not just dismissed.
+ if ( newTLP != m_tlpClosed ||
+ m_tlpClosedTime+250 < currentTime )
+ {
+ newTLP->Connect( wxEVT_CLOSE_WINDOW,
+ wxCloseEventHandler(wxPropertyGrid::OnTLPClose),
+ NULL, this );
+ m_tlpClosed = NULL;
+ }
+ else
+ {
+ newTLP = NULL;
+ }
+ }
+
+ m_tlp = newTLP;
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::OnTLPClose( wxCloseEvent& event )
+{
+ // ClearSelection forces value validation/commit.
+ if ( event.CanVeto() && !DoClearSelection() )
+ {
+ event.Veto();
+ return;
+ }
+
+ // Ok, it can close, set tlp pointer to NULL. Some other event
+ // handler can of course veto the close, but our OnIdle() should
+ // then be able to regain the tlp pointer.
+ OnTLPChanging(NULL);
+
+ event.Skip();
+}
+
+// -----------------------------------------------------------------------
+
+bool wxPropertyGrid::Reparent( wxWindowBase *newParent )
+{
+ OnTLPChanging((wxWindow*)newParent);
+
+ bool res = wxControl::Reparent(newParent);
+
+ return res;
+}
+
// -----------------------------------------------------------------------
// wxPropertyGrid Font and Colour Methods
// -----------------------------------------------------------------------
void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing )
{
- int x = 0, y = 0;
+ int x = 0, y = 0;
- m_captionFont = wxScrolledWindow::GetFont();
+ m_captionFont = wxControl::GetFont();
- GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont);
+ GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont);
m_subgroup_extramargin = x + (x/2);
- m_fontHeight = y;
+ m_fontHeight = y;
#if wxPG_USE_RENDERER_NATIVE
m_iconWidth = wxPG_ICON_WIDTH;
m_marginWidth = m_gutterWidth*2 + m_iconWidth;
m_captionFont.SetWeight(wxBOLD);
- GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont);
+ GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont);
m_lineHeight = m_fontHeight+(2*m_spacingy)+1;
void wxPropertyGrid::RegainColours()
{
- wxColour def_bgcol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
-
if ( !(m_coloursCustomized & 0x0002) )
{
wxColour col = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
m_colCapBack = wxPGAdjustColour(col,-colDec);
else
m_colCapBack = col;
+ m_categoryDefaultCell.GetData()->SetBgCol(m_colCapBack);
}
if ( !(m_coloursCustomized & 0x0001) )
#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);
+ m_categoryDefaultCell.GetData()->SetFgCol(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);
+ m_propertyDefaultCell.GetData()->SetBgCol(bgCol);
+ if ( !m_unspecifiedAppearance.GetBgCol().IsOk() )
+ m_unspecifiedAppearance.SetBgCol(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);
+ m_propertyDefaultCell.GetData()->SetFgCol(fgCol);
+ if ( !m_unspecifiedAppearance.GetFgCol().IsOk() )
+ m_unspecifiedAppearance.SetFgCol(fgCol);
}
if ( !(m_coloursCustomized & 0x0020) )
bool wxPropertyGrid::SetFont( const wxFont& font )
{
// 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)") );
- }
+ DoClearSelection();
- // 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 )
+ bool res = wxControl::SetFont( font );
+ if ( res && GetParent()) // may not have been Create()ed yet if SetFont called from SetWindowVariant
{
CalculateFontAndBitmapStuff( m_vspacing );
-
- if ( m_pState )
- m_pState->CalculateFontAndBitmapStuff(m_vspacing);
-
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
}
// -----------------------------------------------------------------------
m_colPropBack = col;
m_coloursCustomized |= 0x08;
- // Set the cached brush as well.
- ((wxPGBrush*)m_arrBgBrushes.Item(0))->SetColour2(col);
+ m_propertyDefaultCell.GetData()->SetBgCol(col);
+ m_unspecifiedAppearance.SetBgCol(col);
Refresh();
}
m_colPropFore = col;
m_coloursCustomized |= 0x10;
- // Set the cached colour as well.
- ((wxPGColour*)m_arrFgCols.Item(0))->SetColour2(col);
+ m_propertyDefaultCell.GetData()->SetFgCol(col);
+ m_unspecifiedAppearance.SetFgCol(col);
Refresh();
}
{
m_colCapBack = col;
m_coloursCustomized |= 0x02;
+
+ m_categoryDefaultCell.GetData()->SetBgCol(col);
+
Refresh();
}
m_colCapFore = col;
m_coloursCustomized |= 0x04;
- // Set the cached colour as well.
- ((wxPGColour*)m_arrFgCols.Item(1))->SetColour2(col);
+ m_categoryDefaultCell.GetData()->SetFgCol(col);
Refresh();
}
-// -----------------------------------------------------------------------
-
-void wxPropertyGrid::SetBackgroundColourIndex( wxPGProperty* p, int index )
-{
- unsigned char ind = index;
-
- p->m_bgColIndex = ind;
-
- unsigned int i;
- for ( i=0; i<p->GetChildCount(); i++ )
- SetBackgroundColourIndex(p->Item(i),index);
-}
-
-// -----------------------------------------------------------------------
-
-void wxPropertyGrid::SetPropertyBackgroundColour( wxPGPropArg id, const wxColour& colour )
-{
- wxPG_PROP_ARG_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.size()-1); i>0; i-- )
- {
- if ( ((wxPGBrush*)m_arrBgBrushes.Item(i))->GetColourAsLong() == colAsLong )
- {
- colInd = i;
- break;
- }
- }
-
- if ( colInd < 0 )
- {
- colInd = m_arrBgBrushes.size();
- 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);
-
- // If this was on a visible grid, then draw it.
- DrawItemAndChildren(p);
-}
-
-// -----------------------------------------------------------------------
-
-wxColour wxPropertyGrid::GetPropertyBackgroundColour( wxPGPropArg id ) const
-{
- wxPG_PROP_ARG_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;
-
- p->m_fgColIndex = ind;
-
- if ( p->GetChildCount() && (flags & wxPG_RECURSE) )
- {
- unsigned int i;
- for ( i=0; i<p->GetChildCount(); i++ )
- SetTextColourIndex( p->Item(i), index, flags );
- }
-}
-
-// -----------------------------------------------------------------------
-
-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.size()-1); i>0; i-- )
- {
- if ( ((wxPGColour*)m_arrFgCols.Item(i))->GetColourAsLong() == colAsLong )
- {
- colInd = i;
- break;
- }
- }
-
- if ( colInd < 0 )
- {
- colInd = m_arrFgCols.size();
- 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( wxPGPropArg id, const wxColour& colour,
- bool recursively )
-{
- wxPG_PROP_ARG_CALL_PROLOG()
-
- if ( p->IsCategory() )
- {
- wxPropertyCategory* cat = (wxPropertyCategory*) p;
- cat->SetTextColIndex(CacheColour(colour));
- }
-
- // Set indexes
- int flags = 0;
- if ( recursively )
- flags |= wxPG_RECURSE;
- SetTextColourIndex(p, CacheColour(colour), flags);
-
- DrawItemAndChildren(p);
-}
-
-// -----------------------------------------------------------------------
-
-wxColour wxPropertyGrid::GetPropertyTextColour( wxPGPropArg id ) const
-{
- wxPG_PROP_ARG_CALL_PROLOG_RETVAL(wxColour())
-
- return wxColour(*((wxPGColour*)m_arrFgCols.Item(p->m_fgColIndex)));
-}
-
-void wxPropertyGrid::SetPropertyColoursToDefault( wxPGPropArg id )
-{
- wxPG_PROP_ARG_CALL_PROLOG()
-
- SetBackgroundColourIndex( p, 0 );
- SetTextColourIndex( p, 0, wxPG_RECURSE );
-
- if ( p->IsCategory() )
- {
- wxPropertyCategory* cat = (wxPropertyCategory*) p;
- cat->SetTextColIndex(1);
- }
-}
-
// -----------------------------------------------------------------------
// wxPropertyGrid property adding and removal
// -----------------------------------------------------------------------
m_pState->m_itemsAdded = 0;
if ( m_windowStyle & wxPG_AUTO_SORT )
- Sort();
+ Sort(wxPG_SORT_TOP_LEVEL_ONLY);
RecalculateVirtualSize();
-}
-
-// -----------------------------------------------------------------------
-// wxPropertyGrid property value setting and getting
-// -----------------------------------------------------------------------
-
-void wxPropertyGrid::DoSetPropertyValueUnspecified( wxPGProperty* p )
-{
- m_pState->DoSetPropertyValueUnspecified(p);
- DrawItemAndChildren(p);
- wxPGProperty* parent = p->GetParent();
- while ( (parent->GetFlags() & wxPG_PROP_PARENTAL_FLAGS) == wxPG_PROP_MISC_PARENT )
- {
- DrawItem(parent);
- parent = parent->GetParent();
- }
+ // Fix editor position
+ CorrectEditorWidgetPosY();
}
// -----------------------------------------------------------------------
// wxPropertyGrid property operations
// -----------------------------------------------------------------------
-void wxPropertyGrid::DoSetPropertyName( wxPGProperty* p, const wxString& newname )
-{
- wxCHECK_RET( p, wxT("invalid property id") );
-
- if ( p->GetBaseName().Len() ) m_pState->m_dictName.erase( p->GetBaseName() );
- if ( newname.Len() ) m_pState->m_dictName[newname] = (void*) p;
-
- p->DoSetName(newname);
-}
-
-// -----------------------------------------------------------------------
-
bool wxPropertyGrid::EnsureVisible( wxPGPropArg id )
{
wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
// Control font changer helper.
void wxPropertyGrid::SetCurControlBoldFont()
{
- wxASSERT( m_wndEditor );
- m_wndEditor->SetFont( m_captionFont );
+ wxWindow* editor = GetEditorControl();
+ editor->SetFont( m_captionFont );
}
// -----------------------------------------------------------------------
wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& src_str )
{
- if ( src_str.length() == 0 )
+ if ( src_str.empty() )
{
dst_str = src_str;
return src_str;
dst_str.clear();
- for ( ; i != src_str.end(); i++ )
+ for ( ; i != src_str.end(); ++i )
{
wxUniChar a = *i;
wxString& wxPropertyGrid::CreateEscapeSequences( wxString& dst_str, wxString& src_str )
{
- if ( src_str.length() == 0 )
+ if ( src_str.empty() )
{
dst_str = src_str;
return src_str;
dst_str.clear();
- for ( ; i != src_str.end(); i++ )
+ for ( ; i != src_str.end(); ++i )
{
wxChar a = *i;
{
// Outside?
if ( y < 0 )
- return (wxPGProperty*) NULL;
+ return NULL;
unsigned int a = 0;
return m_pState->m_properties->GetItemAtY(y, m_lineHeight, &a);
void wxPropertyGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
{
wxPaintDC dc(this);
+ PrepareDC(dc);
+
+ // Don't paint after destruction has begun
+ if ( !HasInternalFlag(wxPG_FL_INITIALIZED) )
+ return;
+
+ // 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();
- dc.SetPen(m_colEmptySpace);
- dc.SetBrush(m_colEmptySpace);
- dc.DrawRectangle(r);
+ r.y += vy;
+
+ // FIXME: This is just a workaround for a bug that causes splitters not
+ // to paint when other windows are being dragged over the grid.
+ r.x = 0;
+ r.width = GetClientSize().x;
+
+ r.y = vy;
+ r.height = GetClientSize().y;
+
+ // Repaint this rectangle
+ DrawItems( dc, r.y, r.y + r.height, &r );
+
+ // We assume that the size set when grid is shown
+ // is what is desired.
+ SetInternalFlag(wxPG_FL_GOOD_SIZE_SET);
}
// -----------------------------------------------------------------------
// topy and bottomy are already unscrolled (ie. physical)
//
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;
+ unsigned int topItemY,
+ unsigned int bottomItemY,
+ const wxRect* itemsRect )
+{
+ if ( m_frozen ||
+ m_height < 1 ||
+ bottomItemY < topItemY ||
+ !m_pState )
+ return;
m_pState->EnsureVirtualHeight();
- wxRect tempClipRect;
- if ( !clipRect )
+ wxRect tempItemsRect;
+ if ( !itemsRect )
{
- tempClipRect = wxRect(0,topy,m_pState->m_width,bottomy);
- clipRect = &tempClipRect;
+ tempItemsRect = wxRect(0, topItemY,
+ m_pState->m_width,
+ bottomItemY);
+ itemsRect = &tempItemsRect;
}
+ int vx, vy;
+ GetViewStart(&vx, &vy);
+ vx *= wxPG_PIXELS_PER_UNIT;
+ vy *= wxPG_PIXELS_PER_UNIT;
+
+ // itemRect is in virtual grid space
+ wxRect drawRect(itemsRect->x - vx,
+ itemsRect->y - vy,
+ itemsRect->width,
+ itemsRect->height);
+
// items added check
if ( m_pState->m_itemsAdded ) PrepareAfterItemsAdded();
wxDC* dcPtr = &dc;
bool isBuffered = false;
- #if wxPG_DOUBLE_BUFFER
wxMemoryDC* bufferDC = NULL;
if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
{
if ( !m_doubleBuffer )
{
- paintFinishY = clipRect->y;
+ paintFinishY = itemsRect->y;
dcPtr = NULL;
}
else
isBuffered = true;
}
}
- #endif
if ( dcPtr )
{
- dc.SetClippingRegion( *clipRect );
- paintFinishY = DoDrawItems( *dcPtr, NULL, NULL, clipRect, isBuffered );
+ // paintFinishY and drawBottomY are in buffer/physical space
+ paintFinishY = DoDrawItems( *dcPtr, itemsRect, isBuffered );
+ int drawBottomY = itemsRect->y + itemsRect->height - vy;
+
+ // Clear area beyond last painted property
+ if ( paintFinishY < drawBottomY )
+ {
+ dcPtr->SetPen(m_colEmptySpace);
+ dcPtr->SetBrush(m_colEmptySpace);
+ dcPtr->DrawRectangle(0, paintFinishY,
+ m_width,
+ drawBottomY );
+ }
}
- #if wxPG_DOUBLE_BUFFER
if ( bufferDC )
{
- dc.Blit( clipRect->x, clipRect->y, clipRect->width, clipRect->height,
- bufferDC, 0, 0, wxCOPY );
- dc.DestroyClippingRegion(); // Is this really necessary?
+ dc.Blit( drawRect.x, drawRect.y, drawRect.width,
+ drawRect.height,
+ bufferDC, 0, 0, wxCOPY );
delete bufferDC;
}
- #endif
}
-
- // Clear area beyond bottomY?
- if ( paintFinishY < (clipRect->y+clipRect->height) )
+ else
{
+ // Just clear the area
dc.SetPen(m_colEmptySpace);
dc.SetBrush(m_colEmptySpace);
- dc.DrawRectangle( 0, paintFinishY, m_width, (clipRect->y+clipRect->height) );
+ dc.DrawRectangle(drawRect);
}
}
// -----------------------------------------------------------------------
int wxPropertyGrid::DoDrawItems( wxDC& dc,
- const wxPGProperty* firstItem,
- const wxPGProperty* lastItem,
- const wxRect* clipRect,
+ const wxRect* itemsRect,
bool isBuffered ) const
{
- // TODO: This should somehow be eliminated.
- wxRect tempClipRect;
- if ( !clipRect )
- {
- wxASSERT(firstItem);
- wxASSERT(lastItem);
- tempClipRect = GetPropertyRect(firstItem, lastItem);
- clipRect = &tempClipRect;
- }
+ const wxPGProperty* firstItem;
+ const wxPGProperty* lastItem;
- if ( !firstItem )
- firstItem = DoGetItemAtY(clipRect->y);
+ firstItem = DoGetItemAtY(itemsRect->y);
+ lastItem = DoGetItemAtY(itemsRect->y+itemsRect->height-1);
if ( !lastItem )
- {
- lastItem = DoGetItemAtY(clipRect->y+clipRect->height-1);
- if ( !lastItem )
- lastItem = GetLastItem( wxPG_ITERATE_VISIBLE );
- }
+ lastItem = GetLastItem( wxPG_ITERATE_VISIBLE );
if ( m_frozen || m_height < 1 || firstItem == NULL )
- return clipRect->y;
+ return itemsRect->y;
- wxCHECK_MSG( !m_pState->m_itemsAdded, clipRect->y, wxT("no items added") );
+ wxCHECK_MSG( !m_pState->m_itemsAdded, itemsRect->y,
+ "no items added" );
wxASSERT( m_pState->m_properties->GetChildCount() );
int lh = m_lineHeight;
int firstItemTopY;
int lastItemBottomY;
- firstItemTopY = clipRect->y;
- lastItemBottomY = clipRect->y + clipRect->height;
+ firstItemTopY = itemsRect->y;
+ lastItemBottomY = itemsRect->y + itemsRect->height;
// Align y coordinates to item boundaries
firstItemTopY -= firstItemTopY % lh;
lastItemBottomY -= 1;
// Entire range outside scrolled, visible area?
- if ( firstItemTopY >= (int)m_pState->GetVirtualHeight() || lastItemBottomY <= 0 )
- return clipRect->y;
-
- wxCHECK_MSG( firstItemTopY < lastItemBottomY, clipRect->y, wxT("invalid y values") );
+ if ( firstItemTopY >= (int)m_pState->GetVirtualHeight() ||
+ lastItemBottomY <= 0 )
+ return itemsRect->y;
+ wxCHECK_MSG( firstItemTopY < lastItemBottomY,
+ itemsRect->y,
+ "invalid y values" );
/*
- wxLogDebug(wxT(" -> DoDrawItems ( \"%s\" -> \"%s\", height=%i (ch=%i), clipRect = 0x%lX )"),
+ wxLogDebug(" -> DoDrawItems ( \"%s\" -> \"%s\"
+ "height=%i (ch=%i), itemsRect = 0x%lX )",
firstItem->GetLabel().c_str(),
lastItem->GetLabel().c_str(),
(int)(lastItemBottomY - firstItemTopY),
(int)m_height,
- (unsigned long)clipRect );
+ (unsigned long)&itemsRect );
*/
wxRect r;
long windowStyle = m_windowStyle;
int xRelMod = 0;
- int yRelMod = 0;
//
- // With wxPG_DOUBLE_BUFFER, do double buffering
- // - buffer's y = 0, so align cliprect and coordinates to that
+ // For now, do some manual calculation for double buffering
+ // - buffer's y = 0, so align itemsRect and coordinates to that
+ //
+ // TODO: In future use wxAutoBufferedPaintDC (for example)
//
-#if wxPG_DOUBLE_BUFFER
+ int yRelMod = 0;
wxRect cr2;
if ( isBuffered )
{
- xRelMod = clipRect->x;
- yRelMod = clipRect->y;
+ xRelMod = itemsRect->x;
+ yRelMod = itemsRect->y;
//
- // clipRect conversion
- if ( clipRect )
- {
- cr2 = *clipRect;
- cr2.x -= xRelMod;
- cr2.y -= yRelMod;
- clipRect = &cr2;
- }
+ // itemsRect conversion
+ cr2 = *itemsRect;
+ cr2.x -= xRelMod;
+ cr2.y -= yRelMod;
+ itemsRect = &cr2;
firstItemTopY -= yRelMod;
lastItemBottomY -= yRelMod;
}
-#else
- wxUnusedVar(isBuffered);
-#endif
int x = m_marginWidth - xRelMod;
- const wxFont& normalfont = m_font;
+ wxFont normalFont = GetFont();
- bool reallyFocused = (m_iFlags & wxPG_FL_FOCUSED) ? true : false;
+ bool reallyFocused = (m_iFlags & wxPG_FL_FOCUSED) != 0;
- bool isEnabled = IsEnabled();
+ bool isPgEnabled = IsEnabled();
//
// Prepare some pens and brushes that are often changed to.
wxBrush capbgbrush(m_colCapBack,wxSOLID);
wxPen linepen(m_colLine,1,wxSOLID);
+ wxColour selBackCol;
+ if ( isPgEnabled )
+ selBackCol = m_colSelBack;
+ else
+ selBackCol = m_colMargin;
+
// pen that has same colour as text
wxPen outlinepen(m_colPropFore,1,wxSOLID);
dc.DrawRectangle(-1-xRelMod,firstItemTopY-1,x+2,lastItemBottomY-firstItemTopY+2);
}
- const wxPGProperty* selected = m_selected;
+ const wxPGProperty* firstSelected = GetSelection();
const wxPropertyGridPageState* state = m_pState;
+ const wxArrayInt& colWidths = state->m_colWidths;
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
bool wasSelectedPainted = false;
// TODO: Only render columns that are within clipping region.
- dc.SetFont(normalfont);
+ dc.SetFont(normalFont);
wxPropertyGridConstIterator it( state, wxPG_ITERATE_VISIBLE, firstItem );
int endScanBottomY = lastItemBottomY + lh;
int rowHeight = m_fontHeight+(m_spacingy*2)+1;
int textMarginHere = x;
- int renderFlags = wxPGCellRenderer::Control;
+ int renderFlags = 0;
int greyDepth = m_marginWidth;
if ( !(windowStyle & wxPG_HIDE_CATEGORIES) )
int y2 = y + lh;
+#ifdef __WXMSW__
// Margin Edge
- dc.DrawLine( greyDepthX, y, greyDepthX, y2 );
+ // Modified by JACS to not draw a margin if wxPG_HIDE_MARGIN is specified, since it
+ // looks better, at least under Windows when we have a themed border (the themed-window-specific
+ // whitespace between the real border and the propgrid margin exacerbates the double-border look).
+
+ // Is this or its parent themed?
+ bool suppressMarginEdge = (GetWindowStyle() & wxPG_HIDE_MARGIN) &&
+ (((GetWindowStyle() & wxBORDER_MASK) == wxBORDER_THEME) ||
+ (((GetWindowStyle() & wxBORDER_MASK) == wxBORDER_NONE) && ((GetParent()->GetWindowStyle() & wxBORDER_MASK) == wxBORDER_THEME)));
+#else
+ bool suppressMarginEdge = false;
+#endif
+ if (!suppressMarginEdge)
+ dc.DrawLine( greyDepthX, y, greyDepthX, y2 );
+ else
+ {
+ // Blank out the margin edge
+ dc.SetPen(wxPen(GetBackgroundColour()));
+ dc.DrawLine( greyDepthX, y, greyDepthX, y2 );
+ dc.SetPen( linepen );
+ }
// Splitters
unsigned int si;
int sx = x;
- for ( si=0; si<state->m_colWidths.size(); si++ )
+ for ( si=0; si<colWidths.size(); si++ )
{
- sx += state->m_colWidths[si];
+ sx += colWidths[si];
dc.DrawLine( sx, y, sx, y2 );
}
dc.DrawLine( greyDepthX, y2-1, gridWidth-xRelMod, y2-1 );
- if ( p == selected )
- {
- renderFlags |= wxPGCellRenderer::Selected;
-#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- wasSelectedPainted = true;
-#endif
- }
-
- wxColour rowBgCol;
+ //
+ // Need to override row colours?
wxColour rowFgCol;
- wxBrush rowBgBrush;
+ wxColour rowBgCol;
- if ( p->IsCategory() )
- {
- if ( p->m_fgColIndex == 0 )
- rowFgCol = m_colCapFore;
- else
- rowFgCol = *(wxPGColour*)m_arrFgCols[p->m_fgColIndex];
- rowBgBrush = wxBrush(m_colCapBack);
- }
- else if ( p != selected )
+ bool isSelected = state->DoIsPropertySelected(p);
+
+ if ( !isSelected )
{
// Disabled may get different colour.
if ( !p->IsEnabled() )
+ {
+ renderFlags |= wxPGCellRenderer::Disabled |
+ wxPGCellRenderer::DontUseCellFgCol;
rowFgCol = m_colDisPropFore;
- else
- rowFgCol = *(wxPGColour*)m_arrFgCols[p->m_fgColIndex];
-
- rowBgBrush = *(wxPGBrush*)m_arrBgBrushes[p->m_bgColIndex];
+ }
}
else
{
- // Selected gets different colour.
- if ( reallyFocused )
- {
- rowFgCol = m_colSelFore;
- rowBgBrush = wxBrush(m_colSelBack);
- }
- else if ( isEnabled )
+#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
+ if ( p == firstSelected )
+ wasSelectedPainted = true;
+#endif
+
+ renderFlags |= wxPGCellRenderer::Selected;
+
+ if ( !p->IsCategory() )
{
- rowFgCol = *(wxPGColour*)m_arrFgCols[p->m_fgColIndex];
- rowBgBrush = marginBrush;
+ renderFlags |= wxPGCellRenderer::DontUseCellFgCol |
+ wxPGCellRenderer::DontUseCellBgCol;
+
+ if ( reallyFocused && p == firstSelected )
+ {
+ rowFgCol = m_colSelFore;
+ rowBgCol = selBackCol;
+ }
+ else if ( isPgEnabled )
+ {
+ rowFgCol = m_colPropFore;
+ if ( p == firstSelected )
+ rowBgCol = m_colMargin;
+ else
+ rowBgCol = selBackCol;
+ }
+ else
+ {
+ rowFgCol = m_colDisPropFore;
+ rowBgCol = selBackCol;
+ }
}
- else
+ }
+
+ wxBrush rowBgBrush;
+
+ if ( rowBgCol.IsOk() )
+ rowBgBrush = wxBrush(rowBgCol);
+
+ if ( HasInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL) )
+ renderFlags = renderFlags & ~wxPGCellRenderer::DontUseCellColours;
+
+ //
+ // Fill additional margin area with background colour of first cell
+ if ( greyDepthX < textMarginHere )
+ {
+ if ( !(renderFlags & wxPGCellRenderer::DontUseCellBgCol) )
{
- rowFgCol = m_colDisPropFore;
- rowBgBrush = wxBrush(m_colSelBack);
+ wxPGCell& cell = p->GetCell(0);
+ rowBgCol = cell.GetBgCol();
+ rowBgBrush = wxBrush(rowBgCol);
}
+ dc.SetBrush(rowBgBrush);
+ dc.SetPen(rowBgCol);
+ dc.DrawRectangle(greyDepthX+1, y,
+ textMarginHere-greyDepthX, lh-1);
}
bool fontChanged = false;
+ // Expander button rectangle
wxRect butRect( ((p->m_depth - 1) * m_subgroup_extramargin) - xRelMod,
y,
m_marginWidth,
lh );
- if ( p->IsCategory() )
+ // Default cell rect fill the entire row
+ wxRect cellRect(greyDepthX, y,
+ gridWidth - greyDepth + 2, rowHeight-1 );
+
+ bool isCategory = p->IsCategory();
+
+ if ( isCategory )
{
- // Captions are all cells merged as one
dc.SetFont(m_captionFont);
fontChanged = true;
- wxRect cellRect(greyDepthX, y, gridWidth - greyDepth + 2, rowHeight-1 );
-
- dc.SetBrush(rowBgBrush);
- dc.SetPen(rowBgBrush.GetColour());
- dc.SetTextForeground(rowFgCol);
- dc.DrawRectangle(cellRect);
-
- // Foreground
- wxPGCellRenderer* renderer = p->GetCellRenderer(0);
- renderer->Render( dc, cellRect, this, p, 0, -1, renderFlags );
+ if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol )
+ {
+ dc.SetBrush(rowBgBrush);
+ dc.SetPen(rowBgCol);
+ }
- // Tree Item Button
- if ( !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() )
- DrawExpanderButton( dc, butRect, p );
+ if ( renderFlags & wxPGCellRenderer::DontUseCellFgCol )
+ {
+ dc.SetTextForeground(rowFgCol);
+ }
}
else
{
- if ( p->m_flags & wxPG_PROP_MODIFIED && (windowStyle & wxPG_BOLD_MODIFIED) )
+ // Fine tune button rectangle to actually fit the cell
+ if ( butRect.x > 0 )
+ butRect.x += IN_CELL_EXPANDER_BUTTON_X_ADJUST;
+
+ if ( p->m_flags & wxPG_PROP_MODIFIED &&
+ (windowStyle & wxPG_BOLD_MODIFIED) )
{
dc.SetFont(m_captionFont);
fontChanged = true;
}
- unsigned int ci;
- int cellX = x + 1;
- int nextCellWidth = state->m_colWidths[0];
- wxRect cellRect(greyDepthX+1, y, 0, rowHeight-1);
- int textXAdd = textMarginHere - greyDepthX;
+ // Magic fine-tuning for non-category rows
+ cellRect.x += 1;
+ }
+
+ int firstCellWidth = colWidths[0] - (greyDepthX - m_marginWidth);
+ int firstCellX = cellRect.x;
+
+ // Calculate cellRect.x for the last cell
+ unsigned int ci = 0;
+ int cellX = x + 1;
+ for ( ci=0; ci<colWidths.size(); ci++ )
+ cellX += colWidths[ci];
+ cellRect.x = cellX;
+
+ // Draw cells from back to front so that we can easily tell if the
+ // cell on the right was empty from text
+ bool prevFilled = true;
+ ci = colWidths.size();
+ do
+ {
+ ci--;
+
+ int textXAdd = 0;
- for ( ci=0; ci<state->m_colWidths.size(); ci++ )
+ if ( ci == 0 )
{
- cellRect.width = nextCellWidth - 1;
+ textXAdd = textMarginHere - greyDepthX;
+ cellRect.width = firstCellWidth;
+ cellRect.x = firstCellX;
+ }
+ else
+ {
+ int colWidth = colWidths[ci];
+ cellRect.width = colWidth;
+ cellRect.x -= colWidth;
+ }
+
+ // Merge with column to the right?
+ if ( !prevFilled && isCategory )
+ {
+ cellRect.width += colWidths[ci+1];
+ }
+
+ if ( !isCategory )
+ cellRect.width -= 1;
- bool ctrlCell = false;
+ wxWindow* cellEditor = NULL;
+ int cellRenderFlags = renderFlags;
- // Background
- if ( p == selected && m_wndEditor && ci == 1 )
+ // Tree Item Button (must be drawn before clipping is set up)
+ if ( ci == 0 && !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() )
+ DrawExpanderButton( dc, butRect, p );
+
+ // Background
+ if ( isSelected && (ci == 1 || ci == m_selColumn) )
+ {
+ if ( p == firstSelected )
+ {
+ if ( ci == 1 && m_wndEditor )
+ cellEditor = m_wndEditor;
+ else if ( ci == m_selColumn && m_labelEditor )
+ cellEditor = m_labelEditor;
+ }
+
+ if ( cellEditor )
{
- wxColour editorBgCol = GetEditorControl()->GetBackgroundColour();
+ wxColour editorBgCol =
+ cellEditor->GetBackgroundColour();
dc.SetBrush(editorBgCol);
dc.SetPen(editorBgCol);
dc.SetTextForeground(m_colPropFore);
+ dc.DrawRectangle(cellRect);
- if ( m_dragStatus == 0 && !(m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) )
- ctrlCell = true;
+ if ( m_dragStatus != 0 ||
+ (m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) )
+ cellEditor = NULL;
}
else
+ {
+ dc.SetBrush(m_colPropBack);
+ dc.SetPen(m_colPropBack);
+ if ( p->IsEnabled() )
+ dc.SetTextForeground(m_colPropFore);
+ else
+ dc.SetTextForeground(m_colDisPropFore);
+ }
+ }
+ else
+ {
+ if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol )
{
dc.SetBrush(rowBgBrush);
- dc.SetPen(rowBgBrush.GetColour());
- dc.SetTextForeground(rowFgCol);
+ dc.SetPen(rowBgCol);
}
- dc.DrawRectangle(cellRect);
-
- // Tree Item Button
- if ( ci == 0 && !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() )
- DrawExpanderButton( dc, butRect, p );
+ if ( renderFlags & wxPGCellRenderer::DontUseCellFgCol )
+ {
+ dc.SetTextForeground(rowFgCol);
+ }
+ }
- dc.SetClippingRegion(cellRect);
+ dc.SetClippingRegion(cellRect);
- cellRect.x += textXAdd;
- cellRect.width -= textXAdd;
+ cellRect.x += textXAdd;
+ cellRect.width -= textXAdd;
- // Foreground
- if ( !ctrlCell )
+ // Foreground
+ if ( !cellEditor )
+ {
+ wxPGCellRenderer* renderer;
+ int cmnVal = p->GetCommonValue();
+ if ( cmnVal == -1 || ci != 1 )
{
- wxPGCellRenderer* renderer;
- int cmnVal = p->GetCommonValue();
- if ( cmnVal == -1 || ci != 1 )
- {
- renderer = p->GetCellRenderer(ci);
- renderer->Render( dc, cellRect, this, p, ci, -1, renderFlags );
- }
- else
- {
- renderer = GetCommonValue(cmnVal)->GetRenderer();
- renderer->Render( dc, cellRect, this, p, ci, -1, renderFlags );
- }
+ renderer = p->GetCellRenderer(ci);
+ prevFilled = renderer->Render(dc, cellRect, this,
+ p, ci, -1,
+ cellRenderFlags );
}
-
- cellX += state->m_colWidths[ci];
- if ( ci < (state->m_colWidths.size()-1) )
- nextCellWidth = state->m_colWidths[ci+1];
- cellRect.x = cellX;
- dc.DestroyClippingRegion(); // Is this really necessary?
- textXAdd = 0;
+ else
+ {
+ renderer = GetCommonValue(cmnVal)->GetRenderer();
+ prevFilled = renderer->Render(dc, cellRect, this,
+ p, ci, -1,
+ cellRenderFlags );
+ }
+ }
+ else
+ {
+ prevFilled = true;
}
+
+ dc.DestroyClippingRegion(); // Is this really necessary?
}
+ while ( ci > 0 );
if ( fontChanged )
- dc.SetFont(normalfont);
+ dc.SetFont(normalFont);
y += rowHeight;
}
}
#endif
- return y + yRelMod;
+ return y;
}
// -----------------------------------------------------------------------
if ( m_width < 10 || m_height < 10 ||
!m_pState->m_properties->GetChildCount() ||
- p1 == (wxPGProperty*) NULL )
+ p1 == NULL )
return wxRect(0,0,0,0);
int vy = 0;
//
// Return rect which encloses the given property range
+ // (in logical grid coordinates)
+ //
int visTop = p1->GetY();
int visBottom;
// If seleced property is inside the range, we'll extend the range to include
// control's size.
- wxPGProperty* selected = m_selected;
+ wxPGProperty* selected = GetSelection();
if ( selected )
{
int selectedY = selected->GetY();
wxRect r = GetPropertyRect(p1, p2);
if ( r.width > 0 )
{
- m_canvas->RefreshRect(r);
+ // Convert rectangle from logical grid coordinates to physical ones
+ int vx, vy;
+ GetViewStart(&vx, &vy);
+ vx *= wxPG_PIXELS_PER_UNIT;
+ vy *= wxPG_PIXELS_PER_UNIT;
+ r.x -= vx;
+ r.y -= vy;
+ RefreshRect(r);
}
}
void wxPropertyGrid::RefreshProperty( wxPGProperty* p )
{
- if ( p == m_selected )
- DoSelectProperty(p, wxPG_SEL_FORCE);
+ if ( m_pState->DoIsPropertySelected(p) || p->IsChildSelected(true) )
+ {
+ // NB: We must copy the selection.
+ wxArrayPGProperty selection = m_pState->m_selection;
+ DoSetSelection(selection, wxPG_SEL_FORCE);
+ }
DrawItemAndChildren(p);
}
if ( m_pState->m_itemsAdded || m_frozen )
return;
- wxWindow* wndPrimary = GetEditorControl();
-
// Update child control.
- if ( m_selected && m_selected->GetParent() == p )
- m_selected->UpdateControl(wndPrimary);
+ wxPGProperty* selected = GetSelection();
+ if ( selected && selected->GetParent() == p )
+ RefreshEditor();
const wxPGProperty* lastDrawn = p->GetLastVisibleSubItem();
{
PrepareAfterItemsAdded();
- wxWindow::Refresh(false);
- if ( m_canvas )
- // TODO: Coordinate translation
- m_canvas->Refresh(false, rect);
+ wxWindow::Refresh(false, rect);
#if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
// I think this really helps only GTK+1.2
void wxPropertyGrid::Clear()
{
- if ( m_selected )
- {
- bool selRes = DoSelectProperty(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)") );
- }
-
m_pState->DoClear();
m_propHover = NULL;
bool wxPropertyGrid::EnableCategories( bool enable )
{
- if ( !ClearSelection() )
- return false;
+ DoClearSelection();
if ( enable )
{
if ( pNewState == m_pState )
return;
- wxPGProperty* oldSelection = m_selected;
+ wxArrayPGProperty oldSelection = m_pState->m_selection;
- // Deselect
- if ( m_selected )
- {
- bool selRes = ClearSelection();
- wxPG_CHECK_RET_DBG( selRes,
- wxT("failed to deselect a property (editor probably had invalid value)") );
- }
+ // Call ClearSelection() instead of DoClearSelection()
+ // so that selection clear events are not sent.
+ ClearSelection();
- m_pState->m_selected = oldSelection;
+ m_pState->m_selection = oldSelection;
bool orig_mode = m_pState->IsInNonCatMode();
bool new_state_mode = pNewState->IsInNonCatMode();
{
//
// Just in case, fully re-center splitter
- if ( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) )
- pNewState->m_fSplitterX = -1.0;
+ //if ( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) )
+ // pNewState->m_fSplitterX = -1.0;
- pNewState->OnClientWidthChange( pgWidth, pgWidth - pNewState->m_width );
+ pNewState->OnClientWidthChange(pgWidth,
+ pgWidth - pNewState->m_width);
}
- m_propHover = (wxPGProperty*) NULL;
+ m_propHover = NULL;
// If necessary, convert state to correct mode.
if ( orig_mode != new_state_mode )
else if ( !m_frozen )
{
// Refresh, if not frozen.
- if ( m_pState->m_itemsAdded )
- PrepareAfterItemsAdded();
+ m_pState->PrepareAfterItemsAdded();
- // Reselect
- if ( m_pState->m_selected )
- DoSelectProperty( m_pState->m_selected );
+ // Reselect (Use SetSelection() instead of Do-variant so that
+ // events won't be sent).
+ SetSelection(m_pState->m_selection);
RecalculateVirtualSize(0);
Refresh();
// -----------------------------------------------------------------------
-void wxPropertyGrid::SortChildren( wxPGPropArg id )
-{
- wxPG_PROP_ARG_CALL_PROLOG()
-
- m_pState->SortChildren( p );
-}
-
-// -----------------------------------------------------------------------
-
-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();
-}
-
-// -----------------------------------------------------------------------
-
// Call to SetSplitterPosition will always disable splitter auto-centering
// if parent window is shown.
-void wxPropertyGrid::DoSetSplitterPosition_( int newxpos, bool refresh, int splitterIndex, bool allPages )
+void wxPropertyGrid::DoSetSplitterPosition( int newxpos,
+ int splitterIndex,
+ int flags )
{
if ( ( newxpos < wxPG_DRAG_MARGIN ) )
return;
wxPropertyGridPageState* state = m_pState;
- state->DoSetSplitterPosition( newxpos, splitterIndex, allPages );
+ if ( flags & wxPG_SPLITTER_FROM_EVENT )
+ state->m_dontCenterSplitter = true;
+
+ state->DoSetSplitterPosition(newxpos, splitterIndex, flags);
- if ( refresh )
+ if ( flags & wxPG_SPLITTER_REFRESH )
{
- if ( m_selected )
+ if ( GetSelection() )
CorrectEditorWidgetSizeX();
Refresh();
}
+
+ return;
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::ResetColumnSizes( bool enableAutoResizing )
+{
+ wxPropertyGridPageState* state = m_pState;
+ if ( state )
+ state->ResetColumnSizes(0);
+
+ if ( enableAutoResizing && HasFlag(wxPG_SPLITTER_AUTO_CENTER) )
+ m_pState->m_dontCenterSplitter = false;
}
// -----------------------------------------------------------------------
-void wxPropertyGrid::CenterSplitter( bool enableAutoCentering )
+void wxPropertyGrid::CenterSplitter( bool enableAutoResizing )
{
- SetSplitterPosition( m_width/2, true );
- if ( enableAutoCentering && ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER ) )
- m_iFlags &= ~(wxPG_FL_DONT_CENTER_SPLITTER);
+ SetSplitterPosition( m_width/2 );
+ if ( enableAutoResizing && HasFlag(wxPG_SPLITTER_AUTO_CENTER) )
+ m_pState->m_dontCenterSplitter = false;
}
// -----------------------------------------------------------------------
}
-// -----------------------------------------------------------------------
-
-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
// -----------------------------------------------------------------------
// Don't do this if already processing editor event. It might
// induce recursive dialogs and crap like that.
- if ( m_iFlags & wxPG_FL_IN_ONCUSTOMEDITOREVENT )
+ if ( m_iFlags & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT )
{
if ( m_inDoPropertyChanged )
return true;
return false;
}
+ wxPGProperty* selected = GetSelection();
+
if ( m_wndEditor &&
IsEditorsValueModified() &&
(m_iFlags & wxPG_FL_INITIALIZED) &&
- m_selected )
+ selected )
{
- m_inCommitChangesFromEditor = 1;
+ m_inCommitChangesFromEditor = true;
- wxVariant variant(m_selected->GetValueRef());
+ wxVariant variant(selected->GetValueRef());
bool valueIsPending = false;
// JACS - necessary to avoid new focus being found spuriously within OnIdle
m_chgInfo_changedProperty = NULL;
// If truly modified, schedule value as pending.
- if ( m_selected->GetEditorClass()->GetValueFromControl( variant, m_selected, GetEditorControl() ) )
+ if ( selected->GetEditorClass()->
+ GetValueFromControl( variant,
+ selected,
+ GetEditorControl() ) )
{
if ( DoEditorValidate() &&
- PerformValidation(m_selected, variant) )
+ PerformValidation(selected, variant) )
{
valueIsPending = true;
}
EditorsValueWasNotModified();
}
- bool res = true;
+ m_inCommitChangesFromEditor = false;
- m_inCommitChangesFromEditor = 0;
+ bool res = true;
if ( validationFailure && !forceSuccess )
{
m_curFocused = oldFocus;
}
- res = OnValidationFailure(m_selected, variant);
+ res = OnValidationFailure(selected, variant);
// Now prevent further validation failure messages
if ( res )
{
EditorsValueWasNotModified();
- OnValidationFailureReset(m_selected);
+ OnValidationFailureReset(selected);
}
}
else if ( valueIsPending )
{
- DoPropertyChanged( m_selected, flags );
+ DoPropertyChanged( selected, flags );
EditorsValueWasNotModified();
}
// -----------------------------------------------------------------------
-bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue )
+bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue,
+ int flags )
{
//
// Runs all validation functionality.
//
m_validationInfo.m_failureBehavior = m_permanentValidationFailureBehavior;
+ m_validationInfo.m_isFailing = true;
- if ( !wxPGIsVariantType(pendingValue, list) )
+ //
+ // Variant list a special value that cannot be validated
+ // by normal means.
+ if ( pendingValue.GetType() != wxPG_VARIANT_TYPE_LIST )
{
if ( !p->ValidateValue(pendingValue, m_validationInfo) )
return false;
wxVariant bcpPendingList;
listValue = pendingValue;
- listValue.SetName(p->GetLabel());
+ listValue.SetName(p->GetBaseName());
while ( pwc &&
(pwc->HasFlag(wxPG_PROP_AGGREGATE) || pwc->HasFlag(wxPG_PROP_COMPOSED_VALUE)) )
{
wxVariantList tempList;
- wxVariant lv(tempList, pwc->GetLabel());
+ wxVariant lv(tempList, pwc->GetBaseName());
lv.Append(listValue);
listValue = lv;
pPendingValue = &listValue;
wxVariant value;
wxPGProperty* evtChangingProperty = changedProperty;
- if ( !wxPGIsVariantType(*pPendingValue, list) )
+ if ( pPendingValue->GetType() != wxPG_VARIANT_TYPE_LIST )
{
value = *pPendingValue;
}
wxVariant evtChangingValue = value;
- // FIXME: After proper ValueToString()s added, remove
- // this. It is just a temporary fix, as evt_changing
- // will simply not work for wxPG_PROP_COMPOSED_VALUE
- // (unless it is selected, and textctrl editor is open).
- if ( changedProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) )
+ if ( flags & SendEvtChanging )
{
- evtChangingProperty = baseChangedProperty;
- if ( evtChangingProperty != p )
- {
- evtChangingProperty->AdaptListToValue( bcpPendingList, &evtChangingValue );
- }
- else
+ // FIXME: After proper ValueToString()s added, remove
+ // this. It is just a temporary fix, as evt_changing
+ // will simply not work for wxPG_PROP_COMPOSED_VALUE
+ // (unless it is selected, and textctrl editor is open).
+ if ( changedProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) )
{
- evtChangingValue = pendingValue;
+ evtChangingProperty = baseChangedProperty;
+ if ( evtChangingProperty != p )
+ {
+ evtChangingProperty->AdaptListToValue( bcpPendingList, &evtChangingValue );
+ }
+ else
+ {
+ evtChangingValue = pendingValue;
+ }
}
- }
- if ( evtChangingProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) )
- {
- if ( changedProperty == m_selected )
- {
- wxASSERT( m_wndEditor->IsKindOf(CLASSINFO(wxTextCtrl)) );
- evtChangingValue = ((wxTextCtrl*)m_wndEditor)->GetValue();
- }
- else
+ if ( evtChangingProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) )
{
- wxLogDebug(wxT("WARNING: wxEVT_PG_CHANGING is about to happen with old value."));
+ if ( changedProperty == GetSelection() )
+ {
+ wxWindow* editor = GetEditorControl();
+ wxASSERT( editor->IsKindOf(CLASSINFO(wxTextCtrl)) );
+ evtChangingValue = wxStaticCast(editor, wxTextCtrl)->GetValue();
+ }
+ else
+ {
+ wxLogDebug(wxT("WARNING: wxEVT_PG_CHANGING is about to happen with old value."));
+ }
}
}
// If changedProperty is not property which value was edited,
// then call wxPGProperty::ValidateValue() for that as well.
- if ( p != changedProperty && !wxPGIsVariantType(value, list) )
+ if ( p != changedProperty && value.GetType() != wxPG_VARIANT_TYPE_LIST )
{
if ( !changedProperty->ValidateValue(value, m_validationInfo) )
return false;
}
- // SendEvent returns true if event was vetoed
- if ( SendEvent( wxEVT_PG_CHANGING, evtChangingProperty, &evtChangingValue, 0 ) )
- return false;
+ if ( flags & SendEvtChanging )
+ {
+ // SendEvent returns true if event was vetoed
+ if ( SendEvent( wxEVT_PG_CHANGING, evtChangingProperty,
+ &evtChangingValue ) )
+ return false;
+ }
+
+ if ( flags & IsStandaloneValidation )
+ {
+ // If called in 'generic' context, we need to reset
+ // m_chgInfo_changedProperty and write back translated value.
+ m_chgInfo_changedProperty = NULL;
+ pendingValue = value;
+ }
+
+ m_validationInfo.m_isFailing = false;
return true;
}
// -----------------------------------------------------------------------
+#if wxUSE_STATUSBAR
+wxStatusBar* wxPropertyGrid::GetStatusBar()
+{
+ wxWindow* topWnd = ::wxGetTopLevelParent(this);
+ if ( topWnd && topWnd->IsKindOf(CLASSINFO(wxFrame)) )
+ {
+ wxFrame* pFrame = wxStaticCast(topWnd, wxFrame);
+ if ( pFrame )
+ return pFrame->GetStatusBar();
+ }
+ return NULL;
+}
+#endif
+
+// -----------------------------------------------------------------------
+
void wxPropertyGrid::DoShowPropertyError( wxPGProperty* WXUNUSED(property), const wxString& msg )
{
- if ( !msg.length() )
+ if ( msg.empty() )
return;
#if wxUSE_STATUSBAR
if ( !wxPGGlobalVars->m_offline )
{
- wxWindow* topWnd = ::wxGetTopLevelParent(this);
- if ( topWnd )
+ wxStatusBar* pStatusBar = GetStatusBar();
+ if ( pStatusBar )
{
- wxFrame* pFrame = wxDynamicCast(topWnd, wxFrame);
- if ( pFrame )
- {
- wxStatusBar* pStatusBar = pFrame->GetStatusBar();
- if ( pStatusBar )
- {
- pStatusBar->SetStatusText(msg);
- return;
- }
- }
+ pStatusBar->SetStatusText(msg);
+ return;
}
}
#endif
- ::wxMessageBox(msg, _T("Property Error"));
+ ::wxMessageBox(msg, _("Property Error"));
+}
+
+// -----------------------------------------------------------------------
+
+void wxPropertyGrid::DoHidePropertyError( wxPGProperty* WXUNUSED(property) )
+{
+#if wxUSE_STATUSBAR
+ if ( !wxPGGlobalVars->m_offline )
+ {
+ wxStatusBar* pStatusBar = GetStatusBar();
+ if ( pStatusBar )
+ {
+ pStatusBar->SetStatusText(wxEmptyString);
+ return;
+ }
+ }
+#endif
}
// -----------------------------------------------------------------------
+bool wxPropertyGrid::OnValidationFailure( wxPGProperty* property,
+ wxVariant& invalidValue )
+{
+ if ( m_inOnValidationFailure )
+ return true;
+
+ m_inOnValidationFailure = true;
+ wxON_BLOCK_EXIT_SET(m_inOnValidationFailure, false);
+
+ wxWindow* editor = GetEditorControl();
+ int vfb = m_validationInfo.m_failureBehavior;
+
+ if ( m_inDoSelectProperty )
+ {
+ // When property selection is being changed, do not display any
+ // messages, if some were already shown for this property.
+ if ( property->HasFlag(wxPG_PROP_INVALID_VALUE) )
+ {
+ m_validationInfo.m_failureBehavior =
+ vfb & ~(wxPG_VFB_SHOW_MESSAGE |
+ wxPG_VFB_SHOW_MESSAGEBOX |
+ wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR);
+ }
+ }
+
+ // First call property's handler
+ property->OnValidationFailure(invalidValue);
+
+ bool res = DoOnValidationFailure(property, invalidValue);
+
+ //
+ // For non-wxTextCtrl editors, we do need to revert the value
+ if ( !editor->IsKindOf(CLASSINFO(wxTextCtrl)) &&
+ property == GetSelection() )
+ {
+ property->GetEditorClass()->UpdateControl(property, editor);
+ }
+
+ property->SetFlag(wxPG_PROP_INVALID_VALUE);
+
+ return res;
+}
+
bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty* property, wxVariant& WXUNUSED(invalidValue) )
{
int vfb = m_validationInfo.m_failureBehavior;
if ( (vfb & wxPG_VFB_MARK_CELL) &&
!property->HasFlag(wxPG_PROP_INVALID_VALUE) )
{
- wxASSERT_MSG( !property->GetCell(0) && !property->GetCell(1),
- wxT("Currently wxPG_VFB_MARK_CELL only works with properties with standard first two cells") );
+ unsigned int colCount = m_pState->GetColumnCount();
+
+ // We need backup marked property's cells
+ m_propCellsBackup = property->m_cells;
+
+ wxColour vfbFg = *wxWHITE;
+ wxColour vfbBg = *wxRED;
- if ( !property->GetCell(0) && !property->GetCell(1) )
+ property->EnsureCells(colCount);
+
+ for ( unsigned int i=0; i<colCount; i++ )
{
- wxColour vfbFg = *wxWHITE;
- wxColour vfbBg = *wxRED;
- property->SetCell(0, new wxPGCell(property->GetLabel(), wxNullBitmap, vfbFg, vfbBg));
- property->SetCell(1, new wxPGCell(property->GetDisplayedString(), wxNullBitmap, vfbFg, vfbBg));
+ wxPGCell& cell = property->m_cells[i];
+ cell.SetFgCol(vfbFg);
+ cell.SetBgCol(vfbBg);
+ }
- DrawItemAndChildren(property);
+ DrawItemAndChildren(property);
- if ( property == m_selected )
- {
- SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL);
+ if ( property == GetSelection() )
+ {
+ SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL);
- wxWindow* editor = GetEditorControl();
- if ( editor )
- {
- editor->SetForegroundColour(vfbFg);
- editor->SetBackgroundColour(vfbBg);
- }
+ wxWindow* editor = GetEditorControl();
+ if ( editor )
+ {
+ editor->SetForegroundColour(vfbFg);
+ editor->SetBackgroundColour(vfbBg);
}
}
}
- if ( vfb & wxPG_VFB_SHOW_MESSAGE )
+ if ( vfb & (wxPG_VFB_SHOW_MESSAGE |
+ wxPG_VFB_SHOW_MESSAGEBOX |
+ wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR) )
{
wxString msg = m_validationInfo.m_failureMessage;
- if ( !msg.length() )
- msg = _T("You have entered invalid value. Press ESC to cancel editing.");
+ if ( msg.empty() )
+ msg = _("You have entered invalid value. Press ESC to cancel editing.");
+
+ #if wxUSE_STATUSBAR
+ if ( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR )
+ {
+ if ( !wxPGGlobalVars->m_offline )
+ {
+ wxStatusBar* pStatusBar = GetStatusBar();
+ if ( pStatusBar )
+ pStatusBar->SetStatusText(msg);
+ }
+ }
+ #endif
+
+ if ( vfb & wxPG_VFB_SHOW_MESSAGE )
+ DoShowPropertyError(property, msg);
- DoShowPropertyError(property, msg);
+ if ( vfb & wxPG_VFB_SHOW_MESSAGEBOX )
+ ::wxMessageBox(msg, _("Property Error"));
}
return (vfb & wxPG_VFB_STAY_IN_PROPERTY) ? false : true;
if ( vfb & wxPG_VFB_MARK_CELL )
{
- property->SetCell(0, NULL);
- property->SetCell(1, NULL);
+ // Revert cells
+ property->m_cells = m_propCellsBackup;
ClearInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL);
- if ( property == m_selected && GetEditorControl() )
+ if ( property == GetSelection() && GetEditorControl() )
{
// Calling this will recreate the control, thus resetting its colour
RefreshProperty(property);
DrawItemAndChildren(property);
}
}
+
+#if wxUSE_STATUSBAR
+ if ( vfb & wxPG_VFB_SHOW_MESSAGE_ON_STATUSBAR )
+ {
+ if ( !wxPGGlobalVars->m_offline )
+ {
+ wxStatusBar* pStatusBar = GetStatusBar();
+ if ( pStatusBar )
+ pStatusBar->SetStatusText(wxEmptyString);
+ }
+ }
+#endif
+
+ if ( vfb & wxPG_VFB_SHOW_MESSAGE )
+ {
+ DoHidePropertyError(property);
+ }
+
+ m_validationInfo.m_isFailing = false;
}
// -----------------------------------------------------------------------
if ( m_inDoPropertyChanged )
return true;
+ m_inDoPropertyChanged = true;
+ wxON_BLOCK_EXIT_SET(m_inDoPropertyChanged, false);
+
+ wxPGProperty* selected = GetSelection();
+
m_pState->m_anyModified = 1;
- m_inDoPropertyChanged = 1;
+ // If property's value is being changed, assume it is valid
+ OnValidationFailureReset(selected);
// Maybe need to update control
wxASSERT( m_chgInfo_changedProperty != NULL );
topPaintedProperty = topPaintedProperty->GetParent();
}
- changedProperty->SetValue(value, &m_chgInfo_valueList);
+ changedProperty->SetValue(value, &m_chgInfo_valueList, wxPG_SETVAL_BY_USER);
+
+ // NB: Call GetEditorControl() as late as possible, because OnSetValue()
+ // and perhaps other user-defined virtual functions may change it.
+ wxWindow* editor = GetEditorControl();
// 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 ( p == selected && (m_windowStyle & wxPG_BOLD_MODIFIED) )
{
- if ( m_wndEditor )
+ if ( editor )
SetCurControlBoldFont();
}
}
{
pwc->m_flags |= wxPG_PROP_MODIFIED;
- if ( pwc == m_selected && (m_windowStyle & wxPG_BOLD_MODIFIED) )
+ if ( pwc == selected && (m_windowStyle & wxPG_BOLD_MODIFIED) )
{
- if ( m_wndEditor )
+ if ( editor )
SetCurControlBoldFont();
}
// control.
if ( selFlags & wxPG_SEL_DIALOGVAL )
{
- if ( m_wndEditor )
- p->GetEditorClass()->UpdateControl(p, m_wndEditor);
+ RefreshEditor();
}
else
{
while ( pwc != changedProperty )
{
- SendEvent( wxEVT_PG_CHANGED, pwc, NULL, selFlags );
+ SendEvent( wxEVT_PG_CHANGED, pwc, NULL );
pwc = pwc->GetParent();
}
}
- SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL, selFlags );
-
- m_inDoPropertyChanged = 0;
+ SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL );
return true;
}
// -----------------------------------------------------------------------
-// Runs wxValidator for the selected property
-bool wxPropertyGrid::DoEditorValidate()
+wxVariant wxPropertyGrid::GetUncommittedPropertyValue()
{
-#if wxUSE_VALIDATORS
- // With traditional validator style, we dont need to more
- if ( !(GetExtraStyle() & wxPG_EX_LEGACY_VALIDATORS) )
- return true;
+ wxPGProperty* prop = GetSelectedProperty();
- if ( m_iFlags & wxPG_FL_VALIDATION_FAILED )
- {
- return false;
- }
+ if ( !prop )
+ return wxNullVariant;
- wxWindow* wnd = GetEditorControl();
+ wxTextCtrl* tc = GetEditorTextCtrl();
+ wxVariant value = prop->GetValue();
- wxValidator* validator = m_selected->GetValidator();
- if ( validator && wnd )
- {
- // Use TextCtrl of ODComboBox instead
- if ( wnd->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)) )
- {
- wnd = ((wxOwnerDrawnComboBox*)wnd)->GetTextCtrl();
+ if ( !tc || !IsEditorsValueModified() )
+ return value;
- if ( !wnd )
- return true;
- }
+ if ( !prop->StringToValue(value, tc->GetValue()) )
+ return value;
- validator->SetWindow(wnd);
+ if ( !PerformValidation(prop, value, IsStandaloneValidation) )
+ return prop->GetValue();
- // 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;
+ return value;
}
// -----------------------------------------------------------------------
-bool wxPropertyGrid::ProcessEvent(wxEvent& event)
+// Runs wxValidator for the selected property
+bool wxPropertyGrid::DoEditorValidate()
{
- wxWindow* wnd = (wxWindow*) event.GetEventObject();
- if ( wnd && wnd->IsKindOf(CLASSINFO(wxWindow)) )
+#if wxUSE_VALIDATORS
+ wxRecursionGuard guard(m_validatingEditor);
+ if ( guard.IsInside() )
+ return false;
+
+ wxPGProperty* selected = GetSelection();
+ if ( selected )
{
- wxWindow* parent = wnd->GetParent();
+ wxWindow* wnd = GetEditorControl();
- if ( parent &&
- (parent == m_canvas ||
- parent->GetParent() == m_canvas) )
+ wxValidator* validator = selected->GetValidator();
+ if ( validator && wnd )
{
- OnCustomEditorEvent((wxCommandEvent&)event);
- return true;
+ validator->SetWindow(wnd);
+ if ( !validator->Validate(this) )
+ return false;
}
}
- return wxPanel::ProcessEvent(event);
+#endif
+ return true;
}
// -----------------------------------------------------------------------
-// NB: It may really not be wxCommandEvent - must check if necessary
-// (usually not).
-void wxPropertyGrid::OnCustomEditorEvent( wxCommandEvent &event )
+bool wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event )
{
- wxPGProperty* selected = m_selected;
-
//
+ // NB: We should return true if the event was recognized as
+ // a dedicated wxPropertyGrid event, and as such was
+ // either properly handled or ignored.
+ //
+
+ // It is possible that this handler receives event even before
+ // the control has been properly initialized. Let's skip the
+ // event handling in that case.
+ if ( !m_pState )
+ return false;
+
+ // Don't care about the event if it originated from the
+ // 'label editor'. In this function we only care about the
+ // property value editor.
+ if ( m_labelEditor && event.GetId() == m_labelEditor->GetId() )
+ {
+ event.Skip();
+ return true;
+ }
+
+ wxPGProperty* selected = GetSelection();
+
// Somehow, event is handled after property has been deselected.
// Possibly, but very rare.
- if ( !selected )
- return;
+ if ( !selected ||
+ selected->HasFlag(wxPG_PROP_BEING_DELETED) ||
+ m_inOnValidationFailure ||
+ // Also don't handle editor event if wxEVT_PG_CHANGED or
+ // similar is currently doing something (showing a
+ // message box, for instance).
+ m_processedEvent )
+ return true;
- if ( m_iFlags & wxPG_FL_IN_ONCUSTOMEDITOREVENT )
- return;
+ if ( m_iFlags & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT )
+ return true;
wxVariant pendingValue(selected->GetValueRef());
wxWindow* wnd = GetEditorControl();
+ wxWindow* editorWnd = wxDynamicCast(event.GetEventObject(), wxWindow);
int selFlags = 0;
bool wasUnspecified = selected->IsValueUnspecified();
int usesAutoUnspecified = selected->UsesAutoUnspecified();
-
bool valueIsPending = false;
m_chgInfo_changedProperty = NULL;
- m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED|wxPG_FL_VALUE_CHANGE_IN_EVENT);
+ m_iFlags &= ~wxPG_FL_VALUE_CHANGE_IN_EVENT;
//
// Filter out excess wxTextCtrl modified events
- if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED &&
- wnd &&
- wnd->IsKindOf(CLASSINFO(wxTextCtrl)) )
+ if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED && wnd )
{
- wxTextCtrl* tc = (wxTextCtrl*) wnd;
+ if ( wnd->IsKindOf(CLASSINFO(wxTextCtrl)) )
+ {
+ wxTextCtrl* tc = (wxTextCtrl*) wnd;
- wxString newTcValue = tc->GetValue();
- if ( m_prevTcValue == newTcValue )
- return;
+ wxString newTcValue = tc->GetValue();
+ if ( m_prevTcValue == newTcValue )
+ return true;
+ m_prevTcValue = newTcValue;
+ }
+ else if ( wnd->IsKindOf(CLASSINFO(wxComboCtrl)) )
+ {
+ // In some cases we might stumble unintentionally on
+ // wxComboCtrl's embedded wxTextCtrl's events. Let's
+ // avoid them.
+ if ( editorWnd->IsKindOf(CLASSINFO(wxTextCtrl)) )
+ return false;
+
+ wxComboCtrl* cc = (wxComboCtrl*) wnd;
- m_prevTcValue = newTcValue;
+ wxString newTcValue = cc->GetTextCtrl()->GetValue();
+ if ( m_prevTcValue == newTcValue )
+ return true;
+ m_prevTcValue = newTcValue;
+ }
}
- SetInternalFlag(wxPG_FL_IN_ONCUSTOMEDITOREVENT);
+ SetInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT);
bool validationFailure = false;
bool buttonWasHandled = false;
+ bool result = false;
//
// Try common button handling
if ( !buttonWasHandled )
{
- // First call editor class' event handler.
- const wxPGEditor* editor = selected->GetEditorClass();
-
- if ( editor->OnEvent( this, selected, wnd, event ) )
+ if ( wnd || m_wndEditor2 )
{
- // If changes, validate them
- if ( DoEditorValidate() )
- {
- if ( editor->GetValueFromControl( pendingValue, m_selected, wnd ) )
- valueIsPending = true;
- }
- else
+ // First call editor class' event handler.
+ const wxPGEditor* editor = selected->GetEditorClass();
+
+ if ( editor->OnEvent( this, selected, editorWnd, event ) )
{
- validationFailure = true;
+ result = true;
+
+ // If changes, validate them
+ if ( DoEditorValidate() )
+ {
+ if ( editor->GetValueFromControl( pendingValue,
+ selected,
+ wnd ) )
+ valueIsPending = true;
+
+ // Mark value always as pending if validation is currently
+ // failing and value was not unspecified
+ if ( !valueIsPending &&
+ !pendingValue.IsNull() &&
+ m_validationInfo.m_isFailing )
+ valueIsPending = true;
+ }
+ else
+ {
+ validationFailure = true;
+ }
}
}
// Then the property's custom handler (must be always called, unless
// validation failed).
if ( !validationFailure )
- buttonWasHandled = selected->OnEvent( this, wnd, event );
+ buttonWasHandled = selected->OnEvent( this, editorWnd, event );
}
// SetValueInEvent(), as called in one of the functions referred above
}
if ( !validationFailure && valueIsPending )
- if ( !PerformValidation(m_selected, pendingValue) )
+ if ( !PerformValidation(selected, pendingValue) )
validationFailure = true;
- if ( validationFailure )
+ if ( validationFailure)
{
OnValidationFailure(selected, pendingValue);
}
DoPropertyChanged(selected, selFlags);
EditorsValueWasNotModified();
+
+ // Regardless of editor type, unfocus editor on
+ // text-editing related enter press.
+ if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER )
+ {
+ SetFocusOnCanvas();
+ }
}
else
{
// No value after all
+ // Regardless of editor type, unfocus editor on
+ // text-editing related enter press.
+ if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER )
+ {
+ SetFocusOnCanvas();
+ }
+
// Let unhandled button click events go to the parent
if ( !buttonWasHandled && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
{
+ result = true;
wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,GetId());
GetEventHandler()->AddPendingEvent(evt);
}
}
- ClearInternalFlag(wxPG_FL_IN_ONCUSTOMEDITOREVENT);
+ ClearInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT);
+
+ return result;
}
// -----------------------------------------------------------------------
wxRect wxPropertyGrid::GetEditorWidgetRect( wxPGProperty* p, int column ) const
{
int itemy = p->GetY2(m_lineHeight);
- int vy = 0;
- int cust_img_space = 0;
int splitterX = m_pState->DoGetSplitterPosition(column-1);
int colEnd = splitterX + m_pState->m_colWidths[column];
+ int imageOffset = 0;
- // 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*/ )
+ int vx, vy; // Top left corner of client
+ GetViewStart(&vx, &vy);
+ vy *= wxPG_PIXELS_PER_UNIT;
+
+ if ( column == 1 )
+ {
+ // TODO: If custom image detection changes from current, change this.
+ if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE )
+ {
+ //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
+ int iw = p->OnMeasureImage().x;
+ if ( iw < 1 )
+ iw = wxPG_CUSTOM_IMAGE_WIDTH;
+ imageOffset = p->GetImageOffset(iw);
+ }
+ }
+ else if ( column == 0 )
{
- //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
- int imwid = p->OnMeasureImage().x;
- if ( imwid < 1 ) imwid = wxPG_CUSTOM_IMAGE_WIDTH;
- cust_img_space = imwid + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2;
+ splitterX += (p->m_depth - 1) * m_subgroup_extramargin;
}
return wxRect
(
- splitterX+cust_img_space+wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1,
+ splitterX+imageOffset+wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1,
itemy-vy,
- colEnd-splitterX-wxPG_XBEFOREWIDGET-wxPG_CONTROL_MARGIN-cust_img_space-1,
+ colEnd-splitterX-wxPG_XBEFOREWIDGET-wxPG_CONTROL_MARGIN-imageOffset-1,
m_lineHeight-1
);
}
wxSize cis = p->OnMeasureImage(item);
- int choiceCount = p->GetChoiceCount();
+ int choiceCount = p->m_choices.GetCount();
int comVals = p->GetDisplayedCommonValueCount();
if ( item >= choiceCount && comVals > 0 )
{
if ( type == wxCURSOR_SIZEWE )
cursor = m_cursorSizeWE;
- m_canvas->SetCursor( *cursor );
+ SetCursor( *cursor );
m_curcursor = type;
}
-// -----------------------------------------------------------------------
-// wxPropertyGrid property selection
// -----------------------------------------------------------------------
-#define CONNECT_CHILD(EVT,FUNCTYPE,FUNC) \
- wnd->Connect(id, EVT, \
- (wxObjectEventFunction) (wxEventFunction) \
- FUNCTYPE (&wxPropertyGrid::FUNC), \
- NULL, this );
+wxString
+wxPropertyGrid::GetUnspecifiedValueText( int argFlags ) const
+{
+ const wxPGCell& ua = GetUnspecifiedValueAppearance();
-// Setups event handling for child control
-void wxPropertyGrid::SetupEventHandling( wxWindow* argWnd, int id )
+ if ( ua.HasText() &&
+ !(argFlags & wxPG_FULL_VALUE) &&
+ !(argFlags & wxPG_EDITABLE_VALUE) )
+ return ua.GetText();
+
+ return wxEmptyString;
+}
+
+// -----------------------------------------------------------------------
+// wxPropertyGrid property selection, editor creation
+// -----------------------------------------------------------------------
+
+//
+// This class forwards events from property editor controls to wxPropertyGrid.
+class wxPropertyGridEditorEventForwarder : public wxEvtHandler
{
- wxWindow* wnd = argWnd;
+public:
+ wxPropertyGridEditorEventForwarder( wxPropertyGrid* propGrid )
+ : wxEvtHandler(), m_propGrid(propGrid)
+ {
+ }
- if ( argWnd == m_wndEditor )
+ virtual ~wxPropertyGridEditorEventForwarder()
{
- CONNECT_CHILD(wxEVT_MOTION,(wxMouseEventFunction),OnMouseMoveChild)
- CONNECT_CHILD(wxEVT_LEFT_UP,(wxMouseEventFunction),OnMouseUpChild)
- CONNECT_CHILD(wxEVT_LEFT_DOWN,(wxMouseEventFunction),OnMouseClickChild)
- CONNECT_CHILD(wxEVT_RIGHT_UP,(wxMouseEventFunction),OnMouseRightClickChild)
- CONNECT_CHILD(wxEVT_ENTER_WINDOW,(wxMouseEventFunction),OnMouseEntry)
- CONNECT_CHILD(wxEVT_LEAVE_WINDOW,(wxMouseEventFunction),OnMouseEntry)
}
- else
+
+private:
+ bool ProcessEvent( wxEvent& event )
{
- CONNECT_CHILD(wxEVT_NAVIGATION_KEY,(wxNavigationKeyEventFunction),OnNavigationKey)
+ // Always skip
+ event.Skip();
+
+ m_propGrid->HandleCustomEditorEvent(event);
+
+ //
+ // NB: We should return true if the event was recognized as
+ // a dedicated wxPropertyGrid event, and as such was
+ // either properly handled or ignored.
+ //
+ if ( m_propGrid->IsMainButtonEvent(event) )
+ return true;
+
+ //
+ // NB: On wxMSW, a wxTextCtrl with wxTE_PROCESS_ENTER
+ // may beep annoyingly if that event is skipped
+ // and passed to parent event handler.
+ if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER )
+ return true;
+
+ return wxEvtHandler::ProcessEvent(event);
}
- CONNECT_CHILD(wxEVT_KEY_DOWN,(wxCharEventFunction),OnChildKeyDown)
- CONNECT_CHILD(wxEVT_KEY_UP,(wxCharEventFunction),OnChildKeyUp)
- CONNECT_CHILD(wxEVT_KILL_FOCUS,(wxFocusEventFunction),OnFocusEvent)
+
+ wxPropertyGrid* m_propGrid;
+};
+
+// Setups event handling for child control
+void wxPropertyGrid::SetupChildEventHandling( wxWindow* argWnd )
+{
+ wxWindowID id = argWnd->GetId();
+
+ if ( argWnd == m_wndEditor )
+ {
+ argWnd->Connect(id, wxEVT_MOTION,
+ wxMouseEventHandler(wxPropertyGrid::OnMouseMoveChild),
+ NULL, this);
+ argWnd->Connect(id, wxEVT_LEFT_UP,
+ wxMouseEventHandler(wxPropertyGrid::OnMouseUpChild),
+ NULL, this);
+ argWnd->Connect(id, wxEVT_LEFT_DOWN,
+ wxMouseEventHandler(wxPropertyGrid::OnMouseClickChild),
+ NULL, this);
+ argWnd->Connect(id, wxEVT_RIGHT_UP,
+ wxMouseEventHandler(wxPropertyGrid::OnMouseRightClickChild),
+ NULL, this);
+ argWnd->Connect(id, wxEVT_ENTER_WINDOW,
+ wxMouseEventHandler(wxPropertyGrid::OnMouseEntry),
+ NULL, this);
+ argWnd->Connect(id, wxEVT_LEAVE_WINDOW,
+ wxMouseEventHandler(wxPropertyGrid::OnMouseEntry),
+ NULL, this);
+ }
+
+ wxPropertyGridEditorEventForwarder* forwarder;
+ forwarder = new wxPropertyGridEditorEventForwarder(this);
+ argWnd->PushEventHandler(forwarder);
+
+ argWnd->Connect(id, wxEVT_KEY_DOWN,
+ wxCharEventHandler(wxPropertyGrid::OnChildKeyDown),
+ NULL, this);
+}
+
+void wxPropertyGrid::DestroyEditorWnd( wxWindow* wnd )
+{
+ if ( !wnd )
+ return;
+
+ wnd->Hide();
+
+ // Do not free editors immediately (for sake of processing events)
+ wxPendingDelete.Append(wnd);
}
void wxPropertyGrid::FreeEditors()
{
- // Do not free editors immediately if processing events
- if ( !m_windowsToDelete )
- m_windowsToDelete = new wxArrayPtrVoid;
+ //
+ // Return focus back to canvas from children (this is required at least for
+ // GTK+, which, unlike Windows, clears focus when control is destroyed
+ // instead of moving it to closest parent).
+ SetFocusOnCanvas();
+ // Do not free editors immediately if processing events
if ( m_wndEditor2 )
{
- m_windowsToDelete->push_back(m_wndEditor2);
+ wxEvtHandler* handler = m_wndEditor2->PopEventHandler(false);
m_wndEditor2->Hide();
- m_wndEditor2 = (wxWindow*) NULL;
+ wxPendingDelete.Append( handler );
+ DestroyEditorWnd(m_wndEditor2);
+ m_wndEditor2 = NULL;
}
if ( m_wndEditor )
{
- m_windowsToDelete->push_back(m_wndEditor);
+ wxEvtHandler* handler = m_wndEditor->PopEventHandler(false);
m_wndEditor->Hide();
- m_wndEditor = (wxWindow*) NULL;
+ wxPendingDelete.Append( handler );
+ DestroyEditorWnd(m_wndEditor);
+ m_wndEditor = NULL;
}
}
{
/*
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 )"));
+ }
*/
if ( m_inDoSelectProperty )
return true;
- m_inDoSelectProperty = 1;
+ m_inDoSelectProperty = true;
+ wxON_BLOCK_EXIT_SET(m_inDoSelectProperty, false);
- wxPGProperty* prev = m_selected;
+ if ( !m_pState )
+ return false;
- //
- // Delete windows pending for deletion
- if ( m_windowsToDelete && !m_inDoPropertyChanged && m_windowsToDelete->size() )
- {
- unsigned int i;
+ wxArrayPGProperty prevSelection = m_pState->m_selection;
+ wxPGProperty* prevFirstSel;
- for ( i=0; i<m_windowsToDelete->size(); i++ )
- delete ((wxWindow*)((*m_windowsToDelete)[i]));
+ if ( prevSelection.size() > 0 )
+ prevFirstSel = prevSelection[0];
+ else
+ prevFirstSel = NULL;
- m_windowsToDelete->clear();
- }
+ if ( prevFirstSel && prevFirstSel->HasFlag(wxPG_PROP_BEING_DELETED) )
+ prevFirstSel = NULL;
- if ( !m_pState )
- {
- m_inDoSelectProperty = 0;
- return false;
- }
+ // Always send event, as this is indirect call
+ DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE);
+
+/*
+ if ( prevFirstSel )
+ wxPrintf( "Selected %s\n", prevFirstSel->GetClassInfo()->GetClassName() );
+ else
+ wxPrintf( "None selected\n" );
+
+ if (p)
+ wxPrintf( "P = %s\n", p->GetClassInfo()->GetClassName() );
+ else
+ wxPrintf( "P = NULL\n" );
+*/
+
+ wxWindow* primaryCtrl = NULL;
- //
// If we are frozen, then just set the values.
if ( m_frozen )
{
m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR);
m_editorFocused = 0;
- m_selected = p;
- m_selColumn = 1;
- m_pState->m_selected = p;
+ m_pState->DoSetSelection(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;
+ p = NULL;
}
else
{
// Is it the same?
- if ( m_selected == p && !(flags & wxPG_SEL_FORCE) )
+ if ( prevFirstSel == p &&
+ prevSelection.size() <= 1 &&
+ !(flags & wxPG_SEL_FORCE) )
{
// Only set focus if not deselecting
if ( p )
}
else
{
- wxScrolledWindow::SetFocus();
- m_editorFocused = 0;
+ SetFocusOnCanvas();
}
}
- m_inDoSelectProperty = 0;
return true;
}
//
// First, deactivate previous
- if ( m_selected )
+ if ( prevFirstSel )
{
-
- OnValidationFailureReset(m_selected);
-
// Must double-check if this is an selected in case of forceswitch
- if ( p != prev )
+ if ( p != prevFirstSel )
{
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);
- m_inDoSelectProperty = 0;
return false;
}
}
- FreeEditors();
- m_selColumn = -1;
-
- m_selected = (wxPGProperty*) NULL;
- m_pState->m_selected = (wxPGProperty*) NULL;
+ // This should be called after CommitChangesFromEditor(), so that
+ // OnValidationFailure() still has information on property's
+ // validation state.
+ OnValidationFailureReset(prevFirstSel);
- // We need to always fully refresh the grid here
- Refresh(false);
+ FreeEditors();
m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR);
EditorsValueWasNotModified();
SetInternalFlag(wxPG_FL_IN_SELECT_PROPERTY);
+ m_pState->DoSetSelection(p);
+
+ // Redraw unselected
+ for ( unsigned int i=0; i<prevSelection.size(); i++ )
+ {
+ DrawItem(prevSelection[i]);
+ }
+
//
// Then, activate the one given.
if ( p )
int splitterX = GetSplitterPosition();
m_editorFocused = 0;
- m_selected = p;
- m_pState->m_selected = p;
m_iFlags |= wxPG_FL_PRIMARY_FILLS_ENTIRE;
- if ( p != prev )
- m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
- wxASSERT( m_wndEditor == (wxWindow*) NULL );
-
- // Do we need OnMeasureCalls?
- wxSize imsz = p->OnMeasureImage();
+ wxASSERT( m_wndEditor == NULL );
//
// Only create editor for non-disabled non-caption
wxRect grect = GetEditorWidgetRect(p, m_selColumn);
wxPoint goodPos = grect.GetPosition();
- #if wxPG_CREATE_CONTROLS_HIDDEN
- int coord_adjust = m_height - goodPos.y;
- goodPos.y += coord_adjust;
- #endif
+
+ // Editor appearance can now be considered clear
+ m_editorAppearance.SetEmptyData();
const wxPGEditor* editor = p->GetEditorClass();
wxCHECK_MSG(editor, false,
m_iFlags &= ~wxPG_FL_FIXED_WIDTH_EDITOR;
- wxPGWindowList wndList = editor->CreateControls(this,
- p,
- goodPos,
- grect.GetSize());
+ wxPGWindowList wndList =
+ editor->CreateControls(this,
+ p,
+ goodPos,
+ grect.GetSize());
m_wndEditor = wndList.m_primary;
m_wndEditor2 = wndList.m_secondary;
+ primaryCtrl = GetEditorControl();
+
+ //
+ // Essentially, primaryCtrl == m_wndEditor
+ //
- // NOTE: It is allowed for m_wndEditor to be NULL - in this case
- // value is drawn as normal, and m_wndEditor2 is assumed
- // to be a right-aligned button that triggers a separate editor
- // window.
+ // NOTE: It is allowed for m_wndEditor to be NULL - in this
+ // case value is drawn as normal, and m_wndEditor2 is
+ // assumed to be a right-aligned button that triggers
+ // a separate editorCtrl window.
if ( m_wndEditor )
{
- wxASSERT_MSG( m_wndEditor->GetParent() == m_canvas,
- wxT("CreateControls must use result of wxPropertyGrid::GetPanel() as parent of controls.") );
+ wxASSERT_MSG( m_wndEditor->GetParent() == GetPanel(),
+ "CreateControls must use result of "
+ "wxPropertyGrid::GetPanel() as parent "
+ "of controls." );
// Set validator, if any
#if wxUSE_VALIDATORS
- if ( !(GetExtraStyle() & wxPG_EX_LEGACY_VALIDATORS) )
- {
- wxValidator* validator = p->GetValidator();
- if ( validator )
- m_wndEditor->SetValidator(*validator);
- }
+ wxValidator* validator = p->GetValidator();
+ if ( validator )
+ primaryCtrl->SetValidator(*validator);
#endif
if ( m_wndEditor->GetSize().y > (m_lineHeight+6) )
// 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) )
+ if ( (p->m_flags & wxPG_PROP_MODIFIED) &&
+ (m_windowStyle & wxPG_BOLD_MODIFIED) )
SetCurControlBoldFont();
- //
- // Fix TextCtrl indentation
- #if defined(__WXMSW__) && !defined(__WXWINCE__)
- wxTextCtrl* tc = NULL;
- if ( m_wndEditor->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)) )
- tc = ((wxOwnerDrawnComboBox*)m_wndEditor)->GetTextCtrl();
- else
- tc = wxDynamicCast(m_wndEditor, 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_wndEditor->GetPosition().x - splitterX;
m_wndEditor->SetSizeHints(3, 3);
- #if wxPG_CREATE_CONTROLS_HIDDEN
- m_wndEditor->Show(false);
- m_wndEditor->Freeze();
-
- goodPos = m_wndEditor->GetPosition();
- goodPos.y -= coord_adjust;
- m_wndEditor->Move( goodPos );
- #endif
-
- wxWindow* primaryCtrl = GetEditorControl();
- SetupEventHandling(primaryCtrl, wxPG_SUBID1);
+ SetupChildEventHandling(primaryCtrl);
// Focus and select all (wxTextCtrl, wxComboBox etc)
if ( flags & wxPG_SEL_FOCUS )
p->GetEditorClass()->OnFocus(p, primaryCtrl);
}
+ else
+ {
+ if ( p->IsValueUnspecified() )
+ SetEditorAppearance(m_unspecifiedAppearance,
+ true);
+ }
}
if ( m_wndEditor2 )
{
- wxASSERT_MSG( m_wndEditor2->GetParent() == m_canvas,
- wxT("CreateControls must use result of wxPropertyGrid::GetPanel() as parent of controls.") );
+ wxASSERT_MSG( m_wndEditor2->GetParent() == GetPanel(),
+ "CreateControls must use result of "
+ "wxPropertyGrid::GetPanel() as parent "
+ "of controls." );
// Get proper id for wndSecondary
m_wndSecId = m_wndEditor2->GetId();
m_wndEditor2->SetSizeHints(3,3);
- #if wxPG_CREATE_CONTROLS_HIDDEN
- wxRect sec_rect = m_wndEditor2->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_wndEditor2->SetSize( sec_rect );
- #endif
m_wndEditor2->Show();
- SetupEventHandling(m_wndEditor2,wxPG_SUBID2);
+ SetupChildEventHandling(m_wndEditor2);
// If no primary editor, focus to button to allow
// it to interprete ENTER etc.
}
else
{
- // wxGTK atleast seems to need this (wxMSW not)
- SetFocus();
+ // Make sure focus is in grid canvas (important for wxGTK,
+ // at least)
+ SetFocusOnCanvas();
}
EditorsValueWasNotModified();
if ( m_wndEditor )
{
- #if wxPG_CREATE_CONTROLS_HIDDEN
- m_wndEditor->Thaw();
- #endif
m_wndEditor->Show(true);
}
- DrawItems(p, p);
+ if ( !(flags & wxPG_SEL_NO_REFRESH) )
+ DrawItem(p);
+ }
+ else
+ {
+ // Make sure focus is in grid canvas
+ SetFocusOnCanvas();
}
ClearInternalFlag(wxPG_FL_IN_SELECT_PROPERTY);
}
-#if wxUSE_STATUSBAR
+ const wxString* pHelpString = NULL;
- //
- // 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 ( p )
+ pHelpString = &p->GetHelpString();
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 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).
+ //
+ wxStatusBar* statusbar = GetStatusBar();
if ( statusbar )
{
- const wxString* pHelpString = (const wxString*) NULL;
-
- if ( p )
+ if ( pHelpString && !pHelpString->empty() )
{
- pHelpString = &p->GetHelpString();
- if ( pHelpString->length() )
- {
- // Set help box text.
- statusbar->SetStatusText( *pHelpString );
- m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR;
- }
+ // 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) )
+ else if ( m_iFlags & wxPG_FL_STRING_IN_STATUSBAR )
{
// Clear help box - but only if it was written
// by us at previous time.
m_iFlags &= ~(wxPG_FL_STRING_IN_STATUSBAR);
}
}
+#endif
}
+ else
+ {
+#if wxPG_SUPPORT_TOOLTIPS
+ //
+ // Show help as a tool tip on the editor control.
+ //
+ if ( pHelpString && !pHelpString->empty() &&
+ primaryCtrl )
+ {
+ primaryCtrl->SetToolTip(*pHelpString);
+ }
#endif
-
- m_inDoSelectProperty = 0;
+ }
// call wx event handler (here so that it also occurs on deselection)
- SendEvent( wxEVT_PG_SELECTED, m_selected, NULL, flags );
+ if ( !(flags & wxPG_SEL_DONT_SEND_EVENT) )
+ SendEvent( wxEVT_PG_SELECTED, p, NULL );
return true;
}
bool wxPropertyGrid::UnfocusEditor()
{
- if ( !m_selected || !m_wndEditor || m_frozen )
+ wxPGProperty* selected = GetSelection();
+
+ if ( !selected || !m_wndEditor || m_frozen )
return true;
if ( !CommitChangesFromEditor(0) )
return false;
- m_canvas->SetFocusIgnoringChildren();
- DrawItem(m_selected);
+ SetFocusOnCanvas();
+ DrawItem(selected);
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::DoClearSelection()
+void wxPropertyGrid::RefreshEditor()
+{
+ wxPGProperty* p = GetSelection();
+ if ( !p )
+ return;
+
+ wxWindow* wnd = GetEditorControl();
+ if ( !wnd )
+ return;
+
+ // Set editor font boldness - must do this before
+ // calling UpdateControl().
+ if ( HasFlag(wxPG_BOLD_MODIFIED) )
+ {
+ if ( p->HasFlag(wxPG_PROP_MODIFIED) )
+ wnd->SetFont(GetCaptionFont());
+ else
+ wnd->SetFont(GetFont());
+ }
+
+ const wxPGEditor* editorClass = p->GetEditorClass();
+
+ editorClass->UpdateControl(p, wnd);
+
+ if ( p->IsValueUnspecified() )
+ SetEditorAppearance(m_unspecifiedAppearance, true);
+}
+
+// -----------------------------------------------------------------------
+
+bool wxPropertyGrid::SelectProperty( wxPGPropArg id, bool focus )
{
- return DoSelectProperty((wxPGProperty*)NULL);
+ wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
+
+ int flags = wxPG_SEL_DONT_SEND_EVENT;
+ if ( focus )
+ flags |= wxPG_SEL_FOCUS;
+
+ return DoSelectProperty(p, flags);
}
// -----------------------------------------------------------------------
bool wxPropertyGrid::DoCollapse( wxPGProperty* p, bool sendEvents )
{
wxPGProperty* pwc = wxStaticCast(p, wxPGProperty);
+ wxPGProperty* selected = GetSelection();
// If active editor was inside collapsed section, then disable it
- if ( m_selected && m_selected->IsSomeParent (p) )
+ if ( selected && selected->IsSomeParent(p) )
{
- if ( !ClearSelection() )
- return false;
+ DoClearSelection();
}
// 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;
+ bool prevDontCenterSplitter = m_pState->m_dontCenterSplitter;
+ m_pState->m_dontCenterSplitter = true;
bool res = m_pState->DoCollapse(pwc);
SendEvent( wxEVT_PG_ITEM_COLLAPSED, p );
RecalculateVirtualSize();
-
- // Redraw etc. only if collapsed was visible.
- if (pwc->IsVisible() &&
- !m_frozen &&
- ( !pwc->IsCategory() || !(m_windowStyle & wxPG_HIDE_CATEGORIES) ) )
- {
- // When item is collapsed so that scrollbar would move,
- // graphics mess is about (unless we redraw everything).
- Refresh();
- }
+ Refresh();
}
- // Clear dont-center-splitter flag if it wasn't set
- m_iFlags = m_iFlags & ~(wxPG_FL_DONT_CENTER_SPLITTER) | old_flag;
+ m_pState->m_dontCenterSplitter = prevDontCenterSplitter;
return res;
}
wxPGProperty* pwc = (wxPGProperty*)p;
// 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;
+ bool prevDontCenterSplitter = m_pState->m_dontCenterSplitter;
+ m_pState->m_dontCenterSplitter = true;
bool res = m_pState->DoExpand(pwc);
SendEvent( wxEVT_PG_ITEM_EXPANDED, p );
RecalculateVirtualSize();
-
- // Redraw etc. only if expanded was visible.
- if ( pwc->IsVisible() && !m_frozen &&
- ( !pwc->IsCategory() || !(m_windowStyle & wxPG_HIDE_CATEGORIES) )
- )
- {
- // Redraw
- #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- Refresh();
- #else
- DrawItems(pwc, NULL);
- #endif
- }
+ Refresh();
}
- // Clear dont-center-splitter flag if it wasn't set
- m_iFlags = m_iFlags & ~(wxPG_FL_DONT_CENTER_SPLITTER) | old_flag;
+ m_pState->m_dontCenterSplitter = prevDontCenterSplitter;
return res;
}
if ( m_frozen )
return m_pState->DoHideProperty(p, hide, flags);
- if ( m_selected &&
- ( m_selected == p || m_selected->IsSomeParent(p) )
- )
+ wxArrayPGProperty selection = m_pState->m_selection; // Must use a copy
+ int selRemoveCount = 0;
+ for ( unsigned int i=0; i<selection.size(); i++ )
+ {
+ wxPGProperty* selected = selection[i];
+ if ( selected == p || selected->IsSomeParent(p) )
{
- if ( !ClearSelection() )
+ if ( !DoRemoveFromSelection(p, flags) )
return false;
+ selRemoveCount += 1;
}
+ }
m_pState->DoHideProperty(p, hide, flags);
void wxPropertyGrid::RecalculateVirtualSize( int forceXPos )
{
- if ( (m_iFlags & wxPG_FL_RECALCULATING_VIRTUAL_SIZE) || m_frozen )
+ // Don't check for !HasInternalFlag(wxPG_FL_INITIALIZED) here. Otherwise
+ // virtual size calculation may go wrong.
+ if ( HasInternalFlag(wxPG_FL_RECALCULATING_VIRTUAL_SIZE) ||
+ m_frozen ||
+ !m_pState )
return;
//
m_pState->EnsureVirtualHeight();
-#ifdef __WXDEBUG__
- int by1 = m_pState->GetVirtualHeight();
- int by2 = m_pState->GetActualVirtualHeight();
- if ( by1 != by2 )
- {
- wxString s = wxString::Format(wxT("VirtualHeight=%i, ActualVirtualHeight=%i, should match!"), by1, by2);
- wxASSERT_MSG( false,
- s.c_str() );
- wxLogDebug(s);
- }
-#endif
+ wxASSERT_LEVEL_2_MSG(
+ m_pState->GetVirtualHeight() == m_pState->GetActualVirtualHeight(),
+ "VirtualHeight and ActualVirtualHeight should match"
+ );
m_iFlags |= wxPG_FL_RECALCULATING_VIRTUAL_SIZE;
GetClientSize(&width,&height);
// Now adjust virtual size.
- SetVirtualSize(x, y);
+ SetVirtualSize(x, y);
int xAmount = 0;
int xPos = 0;
else if ( xPos > (xAmount-(width/wxPG_PIXELS_PER_UNIT)) )
xPos = 0;
- int yAmount = (y+wxPG_PIXELS_PER_UNIT+2)/wxPG_PIXELS_PER_UNIT;
+ int yAmount = y / wxPG_PIXELS_PER_UNIT;
int yPos = GetScrollPos( wxVERTICAL );
SetScrollbars( wxPG_PIXELS_PER_UNIT, wxPG_PIXELS_PER_UNIT,
xAmount, yAmount, xPos, yPos, true );
+ // This may be needed in addition to calling SetScrollbars()
+ // when class inherits from wxScrollHelper instead of
+ // actual wxScrolled<T>.
+ AdjustScrollbars();
+
// Must re-get size now
GetClientSize(&width,&height);
m_width = width;
m_height = height;
- m_canvas->SetSize( x, y );
-
m_pState->CheckColumnWidths();
- if ( m_selected )
+ if ( GetSelection() )
CorrectEditorWidgetSizeX();
m_iFlags &= ~wxPG_FL_RECALCULATING_VIRTUAL_SIZE;
return;
int width, height;
- GetClientSize(&width,&height);
+ GetClientSize(&width, &height);
m_width = width;
m_height = height;
-#if wxPG_DOUBLE_BUFFER
if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
{
int dblh = (m_lineHeight*2);
}
}
-#endif
-
m_pState->OnClientWidthChange( width, event.GetSize().x - m_ncWidth, true );
m_ncWidth = event.GetSize().x;
}
else
{
- // Enable virtual width
- SetInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH);
+ // Enable virtual width
+ SetInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH);
+ }
+ m_pState->SetVirtualWidth( width );
+}
+
+void wxPropertyGrid::SetFocusOnCanvas()
+{
+ // To prevent wxPropertyGrid from stealing focus from other controls,
+ // only move focus to the grid if it was already in one if its child
+ // controls.
+ wxWindow* focus = wxWindow::FindFocus();
+ if ( focus )
+ {
+ wxWindow* parent = focus->GetParent();
+ while ( parent )
+ {
+ if ( parent == this )
+ {
+ SetFocus();
+ break;
+ }
+ parent = parent->GetParent();
+ }
}
- m_pState->SetVirtualWidth( width );
+
+ m_editorFocused = 0;
}
// -----------------------------------------------------------------------
// selFlags uses same values DoSelectProperty's flags
// Returns true if event was vetoed.
-bool wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p, wxVariant* pValue, unsigned int WXUNUSED(selFlags) )
+bool wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p,
+ wxVariant* pValue,
+ unsigned int selFlags,
+ unsigned int column )
{
+ // selFlags should have wxPG_SEL_NOVALIDATE if event is not
+ // vetoable.
+
// Send property grid event of specific type and with specific property
wxPropertyGridEvent evt( eventType, m_eventObject->GetId() );
evt.SetPropertyGrid(this);
evt.SetEventObject(m_eventObject);
evt.SetProperty(p);
- if ( pValue )
+ evt.SetColumn(column);
+ if ( eventType == wxEVT_PG_CHANGING )
{
+ wxASSERT( pValue );
evt.SetCanVeto(true);
- evt.SetupValidationInfo();
m_validationInfo.m_pValue = pValue;
+ evt.SetupValidationInfo();
+ }
+ else
+ {
+ if ( p )
+ evt.SetPropertyValue(p->GetValue());
+
+ if ( !(selFlags & wxPG_SEL_NOVALIDATE) )
+ evt.SetCanVeto(true);
}
- wxEvtHandler* evtHandler = m_eventObject->GetEventHandler();
- evtHandler->ProcessEvent(evt);
+ wxPropertyGridEvent* prevProcessedEvent = m_processedEvent;
+ m_processedEvent = &evt;
+ m_eventObject->HandleWindowEvent(evt);
+ m_processedEvent = prevProcessedEvent;
return evt.WasVetoed();
}
// Need to set focus?
if ( !(m_iFlags & wxPG_FL_FOCUSED) )
{
- m_canvas->SetFocus();
+ SetFocusOnCanvas();
}
wxPropertyGridPageState* state = m_pState;
)
)
{
- if ( !DoSelectProperty( p ) )
+ if ( !AddToSelectionFromInputEvent( p,
+ columnHit,
+ &event ) )
return res;
// On double-click, expand/collapse.
m_iFlags |= wxPG_FL_ACTIVATION_BY_CLICK;
selFlag = wxPG_SEL_FOCUS;
}
- if ( !DoSelectProperty( p, selFlag ) )
+ if ( !AddToSelectionFromInputEvent( p,
+ columnHit,
+ &event,
+ selFlag ) )
return res;
m_iFlags &= ~(wxPG_FL_ACTIVATION_BY_CLICK);
else DoExpand( p, true );
}
- res = false;
+ // Do not Skip() the event after selection has been made.
+ // Otherwise default event handling behaviour kicks in
+ // and may revert focus back to the main canvas.
+ res = true;
}
else
{
if ( event.GetEventType() == wxEVT_LEFT_DCLICK )
{
// Double-clicking the splitter causes auto-centering
- CenterSplitter( true );
+ if ( m_pState->GetColumnCount() <= 2 )
+ {
+ ResetColumnSizes( true );
+
+ SendEvent(wxEVT_PG_COL_DRAGGING,
+ m_propHover,
+ NULL,
+ wxPG_SEL_NOVALIDATE,
+ (unsigned int)m_draggedSplitter);
+ }
}
else if ( m_dragStatus == 0 )
{
- //
- // Begin draggin the splitter
- //
- if ( m_wndEditor )
- {
- // Changes must be committed here or the
- // value won't be drawn correctly
- if ( !CommitChangesFromEditor() )
- return res;
+ //
+ // Begin draggin the splitter
+ //
- m_wndEditor->Show ( false );
- }
+ // send event
+ DoEndLabelEdit(true, wxPG_SEL_NOVALIDATE);
- if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) )
+ // Allow application to veto dragging
+ if ( !SendEvent(wxEVT_PG_COL_BEGIN_DRAG,
+ p, NULL, 0,
+ (unsigned int)splitterHit) )
{
- m_canvas->CaptureMouse();
- m_iFlags |= wxPG_FL_MOUSE_CAPTURED;
- }
+ if ( m_wndEditor )
+ {
+ // Changes must be committed here or the
+ // value won't be drawn correctly
+ if ( !CommitChangesFromEditor() )
+ return res;
+
+ m_wndEditor->Show ( false );
+ }
- m_dragStatus = 1;
- m_draggedSplitter = splitterHit;
- m_dragOffset = splitterHitOffset;
+ if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) )
+ {
+ CaptureMouse();
+ m_iFlags |= wxPG_FL_MOUSE_CAPTURED;
+ }
- wxClientDC dc(m_canvas);
+ m_dragStatus = 1;
+ m_draggedSplitter = splitterHit;
+ m_dragOffset = splitterHitOffset;
- #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
- // Fixes button disappearance bug
- if ( m_wndEditor2 )
- m_wndEditor2->Show ( false );
- #endif
+ #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
+ // Fixes button disappearance bug
+ if ( m_wndEditor2 )
+ m_wndEditor2->Show ( false );
+ #endif
- m_startingSplitterX = x - splitterHitOffset;
+ m_startingSplitterX = x - splitterHitOffset;
+ }
}
}
}
{
int nx = x + m_marginWidth - marginEnds; // Normalize x.
+ // Fine tune cell button x
+ if ( !p->IsCategory() )
+ nx -= IN_CELL_EXPANDER_BUTTON_X_ADJUST;
+
if ( (nx >= m_gutterWidth && nx < (m_gutterWidth+m_iconWidth)) )
{
int y2 = y % m_lineHeight;
// -----------------------------------------------------------------------
-bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x), unsigned int WXUNUSED(y),
- wxMouseEvent& WXUNUSED(event) )
+bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x),
+ unsigned int WXUNUSED(y),
+ wxMouseEvent& event )
{
if ( m_propHover )
{
// Select property here as well
wxPGProperty* p = m_propHover;
- if ( p != m_selected )
- DoSelectProperty( p );
+ AddToSelectionFromInputEvent(p, m_colHover, &event);
// Send right click event.
SendEvent( wxEVT_PG_RIGHT_CLICK, p );
// -----------------------------------------------------------------------
-bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x), unsigned int WXUNUSED(y),
- wxMouseEvent& WXUNUSED(event) )
+bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x),
+ unsigned int WXUNUSED(y),
+ wxMouseEvent& event )
{
if ( m_propHover )
{
// Select property here as well
wxPGProperty* p = m_propHover;
- if ( p != m_selected )
- DoSelectProperty( p );
+ AddToSelectionFromInputEvent(p, m_colHover, &event);
// Send double-click event.
SendEvent( wxEVT_PG_DOUBLE_CLICK, m_propHover );
// -----------------------------------------------------------------------
-#if wxPG_SUPPORT_TOOLTIPS
-
-void wxPropertyGrid::SetToolTip( const wxString& tipString )
-{
- if ( tipString.length() )
- {
- m_canvas->SetToolTip(tipString);
- }
- else
- {
- #if wxPG_ALLOW_EMPTY_TOOLTIPS
- m_canvas->SetToolTip( m_emptyString );
- #else
- m_canvas->SetToolTip( NULL );
- #endif
- }
-}
-
-#endif // #if wxPG_SUPPORT_TOOLTIPS
-
-// -----------------------------------------------------------------------
-
// Return false if should be skipped
-bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event )
+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() )
{
- HandleMouseUp(x,y,event);
+ HandleMouseUp(x, y, event);
}
wxPropertyGridPageState* state = m_pState;
int columnHit = state->HitTestH( x, &splitterHit, &splitterHitOffset );
int splitterX = x - splitterHitOffset;
+ m_colHover = columnHit;
+
if ( m_dragStatus > 0 )
{
if ( x > (m_marginWidth + wxPG_DRAG_MARGIN) &&
{
int newSplitterX = x - m_dragOffset;
- int splitterX = x - splitterHitOffset;
// Splitter redraw required?
if ( newSplitterX != splitterX )
{
// Move everything
- SetInternalFlag(wxPG_FL_DONT_CENTER_SPLITTER);
- state->DoSetSplitterPosition( newSplitterX, m_draggedSplitter, false );
- state->m_fSplitterX = (float) newSplitterX;
-
- if ( m_selected )
- CorrectEditorWidgetSizeX();
-
- Update();
- Refresh();
+ DoSetSplitterPosition(newSplitterX,
+ m_draggedSplitter,
+ wxPG_SPLITTER_REFRESH |
+ wxPG_SPLITTER_FROM_EVENT);
+
+ SendEvent(wxEVT_PG_COL_DRAGGING,
+ m_propHover,
+ NULL,
+ wxPG_SEL_NOVALIDATE,
+ (unsigned int)m_draggedSplitter);
}
m_dragStatus = 2;
int curPropHoverY = y - (y % ih);
// On which item it hovers
- if ( ( !m_propHover )
+ if ( !m_propHover
||
- ( m_propHover && ( sy < m_propHoverY || sy >= (m_propHoverY+ih) ) )
+ ( sy < m_propHoverY || sy >= (m_propHoverY+ih) )
)
{
// Mouse moves on another property
//
if ( m_windowStyle & wxPG_TOOLTIPS )
{
- wxToolTip* tooltip = m_canvas->GetToolTip();
-
if ( m_propHover != prevHover || prevSide != m_mouseSide )
{
if ( m_propHover && !m_propHover->IsCategory() )
space = m_width - splitterX;
if ( m_propHover->m_flags & wxPG_PROP_CUSTOMIMAGE )
- space -= wxPG_CUSTOM_IMAGE_WIDTH + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2;
+ 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 );
+ GetTextExtent( tipString, &tw, &th, 0, 0 );
if ( tw > space )
- {
SetToolTip( tipString );
- }
}
else
{
- if ( tooltip )
- {
- #if wxPG_ALLOW_EMPTY_TOOLTIPS
- m_canvas->SetToolTip( m_emptyString );
- #else
- m_canvas->SetToolTip( NULL );
- #endif
- }
+ SetToolTip( m_emptyString );
}
}
}
else
{
- if ( tooltip )
- {
- #if wxPG_ALLOW_EMPTY_TOOLTIPS
- m_canvas->SetToolTip( m_emptyString );
- #else
- m_canvas->SetToolTip( NULL );
- #endif
- }
+ SetToolTip( m_emptyString );
}
}
}
// (also not if we were dragging and its started
// outside the splitter region)
- if ( m_propHover &&
- !m_propHover->IsCategory() &&
+ if ( !m_propHover->IsCategory() &&
!event.Dragging() )
{
// hovering on splitter
- // NB: Condition disabled since MouseLeave event (from the editor control) cannot be
- // reliably detected.
+ // NB: Condition disabled since MouseLeave event (from the
+ // editor control) cannot be reliably detected.
//if ( m_curcursor != wxCURSOR_SIZEWE )
CustomSetCursor( wxCURSOR_SIZEWE, true );
CustomSetCursor( wxCURSOR_ARROW );
}
}
+
+ //
+ // Multi select by dragging
+ //
+ if ( (GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION) &&
+ event.LeftIsDown() &&
+ m_propHover &&
+ GetSelection() &&
+ columnHit != 1 &&
+ !state->DoIsPropertySelected(m_propHover) )
+ {
+ // Additional requirement is that the hovered property
+ // is adjacent to edges of selection.
+ const wxArrayPGProperty& selection = GetSelectedProperties();
+
+ // Since categories cannot be selected along with 'other'
+ // properties, exclude them from iterator flags.
+ int iterFlags = wxPG_ITERATE_VISIBLE & (~wxPG_PROP_CATEGORY);
+
+ for ( int i=(selection.size()-1); i>=0; i-- )
+ {
+ // TODO: This could be optimized by keeping track of
+ // which properties are at the edges of selection.
+ wxPGProperty* selProp = selection[i];
+ if ( state->ArePropertiesAdjacent(m_propHover, selProp,
+ iterFlags) )
+ {
+ DoAddToSelection(m_propHover);
+ break;
+ }
+ }
+ }
}
return true;
}
// (it is only here as a reminder to not to do it)
//splitterX = x;
- // Disable splitter auto-centering
- m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
+ SendEvent(wxEVT_PG_COL_END_DRAG,
+ m_propHover,
+ NULL,
+ wxPG_SEL_NOVALIDATE,
+ (unsigned int)m_draggedSplitter);
+
+ // Disable splitter auto-centering (but only if moved any -
+ // otherwise we end up disabling auto-center even after a
+ // recentering double-click).
+ int posDiff = abs(m_startingSplitterX -
+ GetSplitterPosition(m_draggedSplitter));
+
+ if ( posDiff > 1 )
+ state->m_dontCenterSplitter = true;
// This is necessary to return cursor
if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
{
- m_canvas->ReleaseMouse();
+ ReleaseMouse();
m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED);
}
m_dragStatus = 0;
// Control background needs to be cleared
- if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) && m_selected )
- DrawItem( m_selected );
+ wxPGProperty* selected = GetSelection();
+ if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) && selected )
+ DrawItem( selected );
if ( m_wndEditor )
{
{
int splitterX = GetSplitterPosition();
- //int ux, uy;
- //CalcUnscrolledPosition( event.m_x, event.m_y, &ux, &uy );
- int ux = event.m_x;
- int uy = event.m_y;
+ int ux, uy;
+ CalcUnscrolledPosition( event.m_x, event.m_y, &ux, &uy );
- wxWindow* wnd = m_wndEditor;
+ wxWindow* wnd = GetEditorControl();
// Hide popup on clicks
if ( event.GetEventType() != wxEVT_MOTION )
if ( wnd && wnd->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)) )
{
- ((wxOwnerDrawnComboBox*)m_wndEditor)->HidePopup();
+ ((wxOwnerDrawnComboBox*)wnd)->HidePopup();
}
wxRect r;
if ( wnd )
r = wnd->GetRect();
- if ( wnd == (wxWindow*) NULL || m_dragStatus ||
+ if ( wnd == NULL || m_dragStatus ||
(
ux <= (splitterX + wxPG_SPLITTERX_DETECTMARGIN2) ||
ux >= (r.x+r.width) ||
int x, y;
if ( OnMouseCommon( event, &x, &y ) )
{
- HandleMouseClick(x,y,event);
+ if ( !HandleMouseClick(x, y, event) )
+ event.Skip();
+ }
+ else
+ {
+ event.Skip();
}
- event.Skip();
}
// -----------------------------------------------------------------------
int x, y;
CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
HandleMouseDoubleClick(x,y,event);
- event.Skip();
+
+ // Do not Skip() event here - OnMouseClick() call above
+ // should have already taken care of it.
}
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
-void wxPropertyGrid::OnMouseMoveBottom( wxMouseEvent& WXUNUSED(event) )
-{
- // Called when mouse moves in the empty space below the properties.
- CustomSetCursor( wxCURSOR_ARROW );
-}
-
-// -----------------------------------------------------------------------
-
void wxPropertyGrid::OnMouseUp( wxMouseEvent &event )
{
int x, y;
if ( OnMouseCommon( event, &x, &y ) )
{
- HandleMouseUp(x,y,event);
+ if ( !HandleMouseUp(x, y, event) )
+ event.Skip();
+ }
+ else
+ {
+ event.Skip();
}
- event.Skip();
}
// -----------------------------------------------------------------------
else if ( event.Leaving() )
{
// Without this, wxSpinCtrl editor will sometimes have wrong cursor
- m_canvas->SetCursor( wxNullCursor );
+ SetCursor( wxNullCursor );
// Get real cursor position
wxPoint pt = ScreenToClient(::wxGetMousePosition());
int x, y;
event.GetPosition(&x,&y);
- AdjustPosForClipperWindow( topCtrlWnd, &x, &y );
-
int splitterX = GetSplitterPosition();
wxRect r = topCtrlWnd->GetRect();
// but that should not matter (right click is about item, not position).
wxPoint pt = m_wndEditor->GetPosition();
CalcUnscrolledPosition( event.m_x + pt.x, event.m_y + pt.y, &x, &y );
- wxASSERT( m_selected );
- m_propHover = m_selected;
+
+ // FIXME: Used to set m_propHover to selection here. Was it really
+ // necessary?
+
bool res = HandleMouseRightClick(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);
- m_canvas->GetEventHandler()->AddPendingEvent(evt);
-}
-
-
int wxPropertyGrid::KeyEventToActions(wxKeyEvent &event, int* pSecond) const
{
// Translates wxKeyEvent to wxPG_ACTION_XXX
void wxPropertyGrid::ClearActionTriggers( int action )
{
wxPGHashMapI2I::iterator it;
+ bool didSomething;
- for ( it = m_actionTriggers.begin(); it != m_actionTriggers.end(); it++ )
+ do
{
- if ( it->second == action )
+ didSomething = false;
+
+ for ( it = m_actionTriggers.begin();
+ it != m_actionTriggers.end();
+ it++ )
{
- m_actionTriggers.erase(it);
+ if ( it->second == action )
+ {
+ m_actionTriggers.erase(it);
+ didSomething = true;
+ break;
+ }
}
}
+ while ( didSomething );
}
-static void CopyTextToClipboard( const wxString& text )
-{
- if ( wxTheClipboard->Open() )
- {
- // This data objects are held by the clipboard,
- // so do not delete them in the app.
- wxTheClipboard->SetData( new wxTextDataObject(text) );
- wxTheClipboard->Close();
- }
-}
-
-void wxPropertyGrid::HandleKeyEvent(wxKeyEvent &event)
+void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild )
{
//
// Handles key event when editor control is not focused.
//
- wxASSERT( !m_frozen );
- if ( m_frozen )
- return;
+ wxCHECK2(!m_frozen, return);
// Travelsal between items, collapsing/expanding, etc.
+ wxPGProperty* selected = GetSelection();
int keycode = event.GetKeyCode();
+ bool editorFocused = IsEditorFocused();
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;
- }
-
- int secondAction;
- int action = KeyEventToActions(event, &secondAction);
-
- if ( m_selected )
- {
-
- // Show dialog?
- if ( ButtonTriggerKeyTest(event) )
- return;
+ wxWindow* mainControl;
- wxPGProperty* p = m_selected;
+ if ( HasInternalFlag(wxPG_FL_IN_MANAGER) )
+ mainControl = GetParent();
+ else
+ mainControl = this;
- if ( action == wxPG_ACTION_COPY )
+ if ( !event.ShiftDown() )
{
- CopyTextToClipboard(p->GetDisplayedString());
+ if ( !editorFocused && m_wndEditor )
+ {
+ DoSelectProperty( selected, wxPG_SEL_FOCUS );
+ }
+ else
+ {
+ // Tab traversal workaround for platforms on which
+ // wxWindow::Navigate() may navigate into first child
+ // instead of next sibling. Does not work perfectly
+ // in every scenario (for instance, when property grid
+ // is either first or last control).
+ #if defined(__WXGTK__)
+ wxWindow* sibling = mainControl->GetNextSibling();
+ if ( sibling )
+ sibling->SetFocusFromKbd();
+ #else
+ Navigate(wxNavigationKeyEvent::IsForward);
+ #endif
+ }
}
else
{
- // Travel and expand/collapse
- int selectDir = -2;
-
- if ( p->GetChildCount() &&
- !(p->m_flags & wxPG_PROP_DISABLED)
- )
- {
- if ( action == wxPG_ACTION_COLLAPSE_PROPERTY || secondAction == wxPG_ACTION_COLLAPSE_PROPERTY )
- {
- if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Collapse(p) )
- keycode = 0;
- }
- else if ( action == wxPG_ACTION_EXPAND_PROPERTY || secondAction == wxPG_ACTION_EXPAND_PROPERTY )
- {
- if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Expand(p) )
- keycode = 0;
- }
- }
-
- if ( keycode )
+ if ( editorFocused )
{
- if ( action == wxPG_ACTION_PREV_PROPERTY || secondAction == wxPG_ACTION_PREV_PROPERTY )
- {
- selectDir = -1;
- }
- else if ( action == wxPG_ACTION_NEXT_PROPERTY || secondAction == wxPG_ACTION_NEXT_PROPERTY )
- {
- selectDir = 1;
- }
- else
- {
- event.Skip();
- }
-
+ UnfocusEditor();
}
-
- if ( selectDir >= -1 )
+ else
{
- p = wxPropertyGridIterator::OneStep( m_pState, wxPG_ITERATE_VISIBLE, p, selectDir );
- if ( p )
- DoSelectProperty(p);
+ #if defined(__WXGTK__)
+ wxWindow* sibling = mainControl->GetPrevSibling();
+ if ( sibling )
+ sibling->SetFocusFromKbd();
+ #else
+ Navigate(wxNavigationKeyEvent::IsBackward);
+ #endif
}
}
- }
- else
- {
- // If nothing was selected, select the first item now
- // (or navigate out of tab).
- if ( action != wxPG_ACTION_CANCEL_EDIT && secondAction != wxPG_ACTION_CANCEL_EDIT )
- {
- wxPGProperty* p = wxPropertyGridInterface::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 res = true;
+ return;
+ }
- if ( !m_selected || !m_wndEditor )
+ // Ignore Alt and Control when they are down alone
+ if ( keycode == WXK_ALT ||
+ keycode == WXK_CONTROL )
{
- return true;
+ event.Skip();
+ return;
}
- int action = KeyEventToAction(event);
+ int secondAction;
+ int action = KeyEventToActions(event, &secondAction);
- // Unfocus?
- if ( action == wxPG_ACTION_CANCEL_EDIT )
+ if ( editorFocused && action == wxPG_ACTION_CANCEL_EDIT )
{
//
// Esc cancels any changes
EditorsValueWasNotModified();
// Update the control as well
- m_selected->GetEditorClass()->SetControlStringValue( m_selected,
- m_wndEditor,
- m_selected->GetDisplayedString() );
+ selected->GetEditorClass()->
+ SetControlStringValue( selected,
+ GetEditorControl(),
+ selected->GetDisplayedString() );
}
- OnValidationFailureReset(m_selected);
-
- res = false;
+ OnValidationFailureReset(selected);
UnfocusEditor();
+ return;
}
- else if ( action == wxPG_ACTION_COPY )
- {
- // NB: There is some problem with getting native cut-copy-paste keys to go through
- // for embedded editor wxTextCtrl. This is why we emulate.
- //
- wxTextCtrl* tc = GetEditorTextCtrl();
- if ( tc )
- {
- wxString sel = tc->GetStringSelection();
- if ( sel.length() )
- CopyTextToClipboard(sel);
- }
- else
- {
- CopyTextToClipboard(m_selected->GetDisplayedString());
- }
- }
- else if ( action == wxPG_ACTION_CUT )
- {
- wxTextCtrl* tc = GetEditorTextCtrl();
- if ( tc )
- {
- long from, to;
- tc->GetSelection(&from, &to);
- if ( from < to )
- {
- CopyTextToClipboard(tc->GetStringSelection());
- tc->Remove(from, to);
- }
- }
- }
- else if ( action == wxPG_ACTION_PASTE )
+
+ // Except for TAB, ESC, and any keys specifically dedicated to
+ // wxPropertyGrid itself, handle child control events in child control.
+ if ( fromChild &&
+ wxPGFindInVector(m_dedicatedKeys, keycode) == wxNOT_FOUND )
{
- wxTextCtrl* tc = GetEditorTextCtrl();
- if ( tc )
+ // Only propagate event if it had modifiers
+ if ( !event.HasModifiers() )
{
- if (wxTheClipboard->Open())
- {
- if (wxTheClipboard->IsSupported( wxDF_TEXT ))
- {
- wxTextDataObject data;
- wxTheClipboard->GetData( data );
- long from, to;
- tc->GetSelection(&from, &to);
- if ( from < to )
- {
- tc->Remove(from, to);
- tc->WriteText(data.GetText());
- }
- else
- {
- tc->WriteText(data.GetText());
- }
- }
- wxTheClipboard->Close();
- }
+ event.StopPropagation();
}
- }
-
- return res;
-}
-
-// -----------------------------------------------------------------------
-
-void wxPropertyGrid::OnKey( wxKeyEvent &event )
-{
-
- //
- // Events to editor controls should get relayed here.
- //
- wxWindow* focused = wxWindow::FindFocus();
-
- wxWindow* primaryCtrl = GetEditorControl();
-
- if ( primaryCtrl &&
- (focused==primaryCtrl
- || m_editorFocused) )
- {
- // Child key must be processed here, since it can
- // destroy the control which is referred by its own
- // event handling.
- HandleChildKey( event );
- }
- 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:-1;
+ bool wasHandled = false;
- if ( m_selected )
+ if ( selected )
{
- if ( dir == 1 && (m_wndEditor || m_wndEditor2) )
+ // Show dialog?
+ if ( ButtonTriggerKeyTest(action, event) )
+ return;
+
+ wxPGProperty* p = selected;
+
+ if ( action == wxPG_ACTION_EDIT && !editorFocused )
{
- wxWindow* focused = wxWindow::FindFocus();
+ DoSelectProperty( p, wxPG_SEL_FOCUS );
+ wasHandled = true;
+ }
- wxWindow* wndToCheck = GetEditorControl();
+ // Travel and expand/collapse
+ int selectDir = -2;
- // ODComboBox focus goes to its text ctrl, so we need to use it instead
- if ( wndToCheck && wndToCheck->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)) )
+ if ( p->GetChildCount() )
+ {
+ if ( action == wxPG_ACTION_COLLAPSE_PROPERTY || secondAction == wxPG_ACTION_COLLAPSE_PROPERTY )
+ {
+ if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Collapse(p) )
+ wasHandled = true;
+ }
+ else if ( action == wxPG_ACTION_EXPAND_PROPERTY || secondAction == wxPG_ACTION_EXPAND_PROPERTY )
{
- wxTextCtrl* comboTextCtrl = ((wxOwnerDrawnComboBox*)wndToCheck)->GetTextCtrl();
- if ( comboTextCtrl )
- wndToCheck = comboTextCtrl;
+ if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Expand(p) )
+ wasHandled = true;
}
+ }
- /*
- // Because of problems navigating from wxButton, do not go to it.
- if ( !wndToCheck )
+ if ( !wasHandled )
+ {
+ if ( action == wxPG_ACTION_PREV_PROPERTY || secondAction == wxPG_ACTION_PREV_PROPERTY )
{
- // No primary, use secondary
- wndToCheck = m_wndEditor2;
+ selectDir = -1;
}
- // 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_wndEditor2 &&
- !(GetExtraStyle() & wxPG_EX_NO_TAB_TO_BUTTON) )
+ else if ( action == wxPG_ACTION_NEXT_PROPERTY || secondAction == wxPG_ACTION_NEXT_PROPERTY )
{
- wndToCheck = m_wndEditor2;
- wxLogDebug(wxT("Exp1"));
+ selectDir = 1;
}
- */
+ }
- if ( focused != wndToCheck &&
- wndToCheck )
+ if ( selectDir >= -1 )
+ {
+ p = wxPropertyGridIterator::OneStep( m_pState, wxPG_ITERATE_VISIBLE, p, selectDir );
+ if ( p )
{
- wndToCheck->SetFocus();
+ int selFlags = 0;
+ int reopenLabelEditorCol = -1;
+
+ if ( editorFocused )
+ {
+ // If editor was focused, then make the next editor
+ // focused as well
+ selFlags |= wxPG_SEL_FOCUS;
+ }
+ else
+ {
+ // Also maintain the same label editor focus state
+ if ( m_labelEditor )
+ reopenLabelEditorCol = m_selColumn;
+ }
- // Select all text in wxTextCtrl etc.
- if ( m_wndEditor && wndToCheck == m_wndEditor )
- m_selected->GetEditorClass()->OnFocus(m_selected,wndToCheck);
+ DoSelectProperty(p, selFlags);
- m_editorFocused = 1;
- next = m_selected;
+ if ( reopenLabelEditorCol >= 0 )
+ DoBeginLabelEdit(reopenLabelEditorCol);
}
+ wasHandled = true;
}
-
- if ( !next )
+ }
+ else
+ {
+ // If nothing was selected, select the first item now
+ // (or navigate out of tab).
+ if ( action != wxPG_ACTION_CANCEL_EDIT && secondAction != wxPG_ACTION_CANCEL_EDIT )
{
- next = wxPropertyGridIterator::OneStep(m_pState, wxPG_ITERATE_VISIBLE, m_selected, dir);
-
- if ( next )
- {
- // This allows preventing NavigateOut to occur
- DoSelectProperty( next, wxPG_SEL_FOCUS );
- }
+ wxPGProperty* p = wxPropertyGridInterface::GetFirst();
+ if ( p ) DoSelectProperty(p);
+ wasHandled = true;
}
}
- if ( !next )
+ if ( !wasHandled )
event.Skip();
}
// -----------------------------------------------------------------------
-bool wxPropertyGrid::ButtonTriggerKeyTest( wxKeyEvent &event )
+void wxPropertyGrid::OnKey( wxKeyEvent &event )
{
- int keycode = event.GetKeyCode();
-
- // Does the keycode trigger button?
- if ( keycode == m_pushButKeyCode &&
- m_wndEditor2 &&
- (!m_pushButKeyCodeNeedsAlt || event.AltDown()) &&
- (!m_pushButKeyCodeNeedsCtrl || event.ControlDown()) )
+ // If there was editor open and focused, then this event should not
+ // really be processed here.
+ if ( IsEditorFocused() )
{
- m_keyComboConsumed = 1;
-
- wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,m_wndEditor2->GetId());
- GetEventHandler()->AddPendingEvent(evt);
- return true;
+ // However, if event had modifiers, it is probably still best
+ // to skip it.
+ if ( event.HasModifiers() )
+ event.Skip();
+ else
+ event.StopPropagation();
+ return;
}
- return false;
+ HandleKeyEvent(event, false);
}
// -----------------------------------------------------------------------
-void wxPropertyGrid::OnChildKeyDown( wxKeyEvent &event )
+bool wxPropertyGrid::ButtonTriggerKeyTest( int action, wxKeyEvent& event )
{
- int keycode = event.GetKeyCode();
-
- // Ignore Alt and Control when they are down alone
- if ( keycode == WXK_ALT ||
- keycode == WXK_CONTROL )
+ if ( action == -1 )
{
- event.Skip();
- return;
+ int secondAction;
+ action = KeyEventToActions(event, &secondAction);
}
- if ( ButtonTriggerKeyTest(event) )
- return;
-
- if ( HandleChildKey(event) == true )
- event.Skip();
+ // Does the keycode trigger button?
+ if ( action == wxPG_ACTION_PRESS_BUTTON &&
+ m_wndEditor2 )
+ {
+ wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, m_wndEditor2->GetId());
+ GetEventHandler()->AddPendingEvent(evt);
+ return true;
+ }
- GetEventHandler()->AddPendingEvent(event);
+ return false;
}
-void wxPropertyGrid::OnChildKeyUp( wxKeyEvent &event )
-{
- m_keyComboConsumed = 0;
-
- GetEventHandler()->AddPendingEvent(event);
+// -----------------------------------------------------------------------
- event.Skip();
+void wxPropertyGrid::OnChildKeyDown( wxKeyEvent &event )
+{
+ HandleKeyEvent(event, true);
}
// -----------------------------------------------------------------------
if ( newFocused != m_curFocused )
HandleFocusChange( newFocused );
+
+ //
+ // Check if top-level parent has changed
+ if ( GetExtraStyle() & wxPG_EX_ENABLE_TLP_TRACKING )
+ {
+ wxWindow* tlp = ::wxGetTopLevelParent(this);
+ if ( tlp != m_tlp )
+ OnTLPChanging(tlp);
+ }
+
+ //
+ // Resolve pending property removals
+ if ( m_deletedProperties.size() > 0 )
+ {
+ wxArrayPGProperty& arr = m_deletedProperties;
+ for ( unsigned int i=0; i<arr.size(); i++ )
+ {
+ DeleteProperty(arr[i]);
+ }
+ arr.clear();
+ }
+ if ( m_removedProperties.size() > 0 )
+ {
+ wxArrayPGProperty& arr = m_removedProperties;
+ for ( unsigned int i=0; i<arr.size(); i++ )
+ {
+ RemoveProperty(arr[i]);
+ }
+ arr.clear();
+ }
+}
+
+bool wxPropertyGrid::IsEditorFocused() const
+{
+ wxWindow* focus = wxWindow::FindFocus();
+
+ if ( focus == m_wndEditor || focus == m_wndEditor2 ||
+ focus == GetEditorControl() )
+ return true;
+
+ return false;
}
// Called by focus event handlers. newFocused is the window that becomes focused.
void wxPropertyGrid::HandleFocusChange( wxWindow* newFocused )
{
+ //
+ // Never allow focus to be changed when handling editor event.
+ // Especially because they may be displaing a dialog which
+ // could cause all kinds of weird (native) focus changes.
+ if ( HasInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT) )
+ return;
+
unsigned int oldFlags = m_iFlags;
+ bool wasEditorFocused = false;
+ wxWindow* wndEditor = m_wndEditor;
m_iFlags &= ~(wxPG_FL_FOCUSED);
// This must be one of nextFocus' parents.
while ( parent )
{
+ if ( parent == wndEditor )
+ {
+ wasEditorFocused = true;
+ }
// Use m_eventObject, which is either wxPropertyGrid or
// wxPropertyGridManager, as appropriate.
- if ( parent == m_eventObject )
+ else if ( parent == m_eventObject )
{
m_iFlags |= wxPG_FL_FOCUSED;
break;
parent = parent->GetParent();
}
+ // Notify editor control when it receives a focus
+ if ( wasEditorFocused && m_curFocused != newFocused )
+ {
+ wxPGProperty* p = GetSelection();
+ if ( p )
+ {
+ const wxPGEditor* editor = p->GetEditorClass();
+ ResetEditorAppearance();
+ editor->OnFocus(p, GetEditorControl());
+ }
+ }
+
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();
}
}
*/
-
- m_iFlags &= ~(wxPG_FL_IGNORE_NEXT_NAVKEY);
}
// Redraw selected
- if ( m_selected && (m_iFlags & wxPG_FL_INITIALIZED) )
- DrawItem( m_selected );
+ wxPGProperty* selected = GetSelection();
+ if ( selected && (m_iFlags & wxPG_FL_INITIALIZED) )
+ DrawItem( selected );
}
}
void wxPropertyGrid::OnChildFocusEvent( wxChildFocusEvent& event )
{
HandleFocusChange((wxWindow*)event.GetEventObject());
-
- //
- // event.Skip() being commented out is aworkaround for bug reported
- // in ticket #4840 (wxScrolledWindow problem with automatic scrolling).
- //event.Skip();
+ event.Skip();
}
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// noDefCheck = true prevents infinite recursion.
-wxPGEditor* wxPropertyGrid::RegisterEditorClass( wxPGEditor* editorclass,
- const wxString& name,
- bool noDefCheck )
+wxPGEditor* wxPropertyGrid::DoRegisterEditorClass( wxPGEditor* editorClass,
+ const wxString& editorName,
+ bool noDefCheck )
{
- wxASSERT( editorclass );
+ wxASSERT( editorClass );
if ( !noDefCheck && wxPGGlobalVars->m_mapEditorClasses.empty() )
RegisterDefaultEditors();
- wxPGGlobalVars->m_mapEditorClasses[name] = (void*)editorclass;
+ wxString name = editorName;
+ if ( name.empty() )
+ name = editorClass->GetName();
+
+ // Existing editor under this name?
+ wxPGHashMapS2P::iterator vt_it = wxPGGlobalVars->m_mapEditorClasses.find(name);
+
+ if ( vt_it != wxPGGlobalVars->m_mapEditorClasses.end() )
+ {
+ // If this name was already used, try class name.
+ name = editorClass->GetClassInfo()->GetClassName();
+ vt_it = wxPGGlobalVars->m_mapEditorClasses.find(name);
+ }
+
+ wxCHECK_MSG( vt_it == wxPGGlobalVars->m_mapEditorClasses.end(),
+ (wxPGEditor*) vt_it->second,
+ "Editor with given name was already registered" );
- return editorclass;
+ wxPGGlobalVars->m_mapEditorClasses[name] = (void*)editorClass;
+
+ return editorClass;
}
+// Use this in RegisterDefaultEditors.
+#define wxPGRegisterDefaultEditorClass(EDITOR) \
+ if ( wxPGEditor_##EDITOR == NULL ) \
+ { \
+ wxPGEditor_##EDITOR = wxPropertyGrid::RegisterEditorClass( \
+ new wxPG##EDITOR##Editor, true ); \
+ }
+
// Registers all default editor classes
void wxPropertyGrid::RegisterDefaultEditors()
{
}
else
{
- i++;
+ ++i;
m_curPos = i;
return true;
}
prev_a = wxT('\0');
}
}
- i++;
+ ++i;
}
m_curPos = str.end();
{
}
-wxPGChoiceEntry::wxPGChoiceEntry( const wxPGChoiceEntry& entry )
- : wxPGCell( entry.GetText(), entry.GetBitmap(),
- entry.GetFgCol(), entry.GetBgCol() ), m_value(entry.GetValue())
-{
-}
-
// -----------------------------------------------------------------------
// wxPGChoicesData
// -----------------------------------------------------------------------
wxPGChoicesData::wxPGChoicesData()
{
- m_refCount = 1;
}
wxPGChoicesData::~wxPGChoicesData()
void wxPGChoicesData::Clear()
{
- unsigned int i;
-
- for ( i=0; i<m_items.size(); i++ )
- {
- delete Item(i);
- }
-
-#if wxUSE_STL
- m_items.resize(0);
-#else
- m_items.Empty();
-#endif
+ m_items.clear();
}
void wxPGChoicesData::CopyDataFrom( wxPGChoicesData* data )
{
wxASSERT( m_items.size() == 0 );
- unsigned int i;
-
- for ( i=0; i<data->GetCount(); i++ )
- m_items.push_back( new wxPGChoiceEntry(*data->Item(i)) );
-}
-
-// -----------------------------------------------------------------------
-// wxPGChoices
-// -----------------------------------------------------------------------
-
-wxPGChoiceEntry& wxPGChoices::Add( const wxString& label, int value )
-{
- EnsureData();
-
- wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value);
- m_data->Insert( -1, p );
- return *p;
-}
-
-// -----------------------------------------------------------------------
-
-wxPGChoiceEntry& wxPGChoices::Add( const wxString& label, const wxBitmap& bitmap, int value )
-{
- EnsureData();
-
- wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value);
- p->SetBitmap(bitmap);
- m_data->Insert( -1, p );
- return *p;
-}
-
-// -----------------------------------------------------------------------
-
-wxPGChoiceEntry& wxPGChoices::Insert( const wxPGChoiceEntry& entry, int index )
-{
- EnsureData();
-
- wxPGChoiceEntry* p = new wxPGChoiceEntry(entry);
- m_data->Insert(index, p);
- return *p;
-}
-
-// -----------------------------------------------------------------------
-
-wxPGChoiceEntry& wxPGChoices::Insert( const wxString& label, int index, int value )
-{
- EnsureData();
-
- wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value);
- m_data->Insert( index, p );
- return *p;
-}
-
-// -----------------------------------------------------------------------
-
-wxPGChoiceEntry& wxPGChoices::AddAsSorted( const wxString& label, int value )
-{
- EnsureData();
-
- size_t index = 0;
-
- while ( index < GetCount() )
- {
- int cmpRes = GetLabel(index).Cmp(label);
- if ( cmpRes > 0 )
- break;
- index++;
- }
-
- wxPGChoiceEntry* p = new wxPGChoiceEntry(label, value);
- m_data->Insert( index, p );
- return *p;
-}
-
-// -----------------------------------------------------------------------
-
-void wxPGChoices::Add( const wxChar** labels, const ValArrItem* values )
-{
- EnsureData();
-
- unsigned int itemcount = 0;
- const wxChar** p = &labels[0];
- while ( *p ) { p++; itemcount++; }
-
- unsigned int i;
- for ( i = 0; i < itemcount; i++ )
- {
- int value = wxPG_INVALID_VALUE;
- if ( values )
- value = values[i];
- m_data->Insert( -1, new wxPGChoiceEntry(labels[i], value) );
- }
-}
-
-// -----------------------------------------------------------------------
-
-void wxPGChoices::Add( const wxArrayString& arr, const ValArrItem* values )
-{
- EnsureData();
-
- unsigned int i;
- unsigned int itemcount = arr.size();
-
- for ( i = 0; i < itemcount; i++ )
- {
- int value = wxPG_INVALID_VALUE;
- if ( values )
- value = values[i];
- m_data->Insert( -1, new wxPGChoiceEntry(arr[i], value) );
- }
-}
-
-// -----------------------------------------------------------------------
-
-void wxPGChoices::Add( const wxArrayString& arr, const wxArrayInt& arrint )
-{
- EnsureData();
-
- unsigned int i;
- unsigned int itemcount = arr.size();
-
- for ( i = 0; i < itemcount; i++ )
- {
- int value = wxPG_INVALID_VALUE;
- if ( &arrint && arrint.size() )
- value = arrint[i];
- m_data->Insert( -1, new wxPGChoiceEntry(arr[i], value) );
- }
-}
-
-// -----------------------------------------------------------------------
-
-void wxPGChoices::RemoveAt(size_t nIndex, size_t count)
-{
- wxASSERT( m_data->m_refCount != 0xFFFFFFF );
- unsigned int i;
- for ( i=nIndex; i<(nIndex+count); i++)
- delete m_data->Item(i);
- m_data->m_items.RemoveAt(nIndex, count);
-}
-
-// -----------------------------------------------------------------------
-
-int wxPGChoices::Index( const wxString& str ) const
-{
- if ( IsOk() )
- {
- unsigned int i;
- for ( i=0; i< m_data->GetCount(); i++ )
- {
- if ( m_data->Item(i)->GetText() == str )
- return i;
- }
- }
- return -1;
-}
-
-// -----------------------------------------------------------------------
-
-int wxPGChoices::Index( int val ) const
-{
- if ( IsOk() )
- {
- unsigned int i;
- for ( i=0; i< m_data->GetCount(); i++ )
- {
- if ( m_data->Item(i)->GetValue() == val )
- return i;
- }
- }
- return -1;
-}
-
-// -----------------------------------------------------------------------
-
-wxArrayString wxPGChoices::GetLabels() const
-{
- wxArrayString arr;
- unsigned int i;
-
- if ( this && IsOk() )
- for ( i=0; i<GetCount(); i++ )
- arr.push_back(GetLabel(i));
-
- return arr;
+ m_items = data->m_items;
}
-// -----------------------------------------------------------------------
-
-bool wxPGChoices::HasValues() const
-{
- return true;
-}
-
-// -----------------------------------------------------------------------
-
-wxArrayInt wxPGChoices::GetValuesForStrings( const wxArrayString& strings ) const
-{
- wxArrayInt arr;
-
- if ( IsOk() )
- {
- unsigned int i;
- for ( i=0; i< strings.size(); i++ )
- {
- int index = Index(strings[i]);
- if ( index >= 0 )
- arr.Add(GetValue(index));
- else
- arr.Add(wxPG_INVALID_VALUE);
- }
- }
-
- return arr;
-}
-
-// -----------------------------------------------------------------------
-
-wxArrayInt wxPGChoices::GetIndicesForStrings( const wxArrayString& strings,
- wxArrayString* unmatched ) const
+wxPGChoiceEntry& wxPGChoicesData::Insert( int index,
+ const wxPGChoiceEntry& item )
{
- wxArrayInt arr;
-
- if ( IsOk() )
+ wxVector<wxPGChoiceEntry>::iterator it;
+ if ( index == -1 )
{
- unsigned int i;
- for ( i=0; i< strings.size(); i++ )
- {
- const wxString& str = strings[i];
- int index = Index(str);
- if ( index >= 0 )
- arr.Add(index);
- else if ( unmatched )
- unmatched->Add(str);
- }
+ it = m_items.end();
+ index = (int) m_items.size();
}
-
- return arr;
-}
-
-// -----------------------------------------------------------------------
-
-void wxPGChoices::AssignData( wxPGChoicesData* data )
-{
- Free();
-
- if ( data != wxPGChoicesEmptyData )
+ else
{
- m_data = data;
- data->m_refCount++;
+ it = m_items.begin() + index;
}
-}
-// -----------------------------------------------------------------------
+ m_items.insert(it, item);
-void wxPGChoices::Init()
-{
- m_data = wxPGChoicesEmptyData;
-}
+ wxPGChoiceEntry& ownEntry = m_items[index];
-// -----------------------------------------------------------------------
+ // Need to fix value?
+ if ( ownEntry.GetValue() == wxPG_INVALID_VALUE )
+ ownEntry.SetValue(index);
-void wxPGChoices::Free()
-{
- if ( m_data != wxPGChoicesEmptyData )
- {
- m_data->DecRef();
- m_data = wxPGChoicesEmptyData;
- }
+ return ownEntry;
}
// -----------------------------------------------------------------------
IMPLEMENT_DYNAMIC_CLASS(wxPropertyGridEvent, wxCommandEvent)
-DEFINE_EVENT_TYPE( wxEVT_PG_SELECTED )
-DEFINE_EVENT_TYPE( wxEVT_PG_CHANGING )
-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 )
-
+wxDEFINE_EVENT( wxEVT_PG_SELECTED, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_CHANGING, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_CHANGED, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_HIGHLIGHTED, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_RIGHT_CLICK, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_PAGE_CHANGED, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_ITEM_EXPANDED, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_ITEM_COLLAPSED, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_DOUBLE_CLICK, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_BEGIN, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_LABEL_EDIT_ENDING, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_COL_BEGIN_DRAG, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_COL_DRAGGING, wxPropertyGridEvent );
+wxDEFINE_EVENT( wxEVT_PG_COL_END_DRAG, wxPropertyGridEvent );
// -----------------------------------------------------------------------
void wxPropertyGridEvent::Init()
{
m_validationInfo = NULL;
+ m_column = 1;
m_canVeto = false;
m_wasVetoed = false;
+ m_pg = NULL;
}
// -----------------------------------------------------------------------
m_eventType = event.GetEventType();
m_eventObject = event.m_eventObject;
m_pg = event.m_pg;
+ OnPropertyGridSet();
m_property = event.m_property;
m_validationInfo = event.m_validationInfo;
m_canVeto = event.m_canVeto;
// -----------------------------------------------------------------------
-wxPropertyGridEvent::~wxPropertyGridEvent()
+void wxPropertyGridEvent::OnPropertyGridSet()
{
+ if ( !m_pg )
+ return;
+
+#if wxUSE_THREADS
+ wxCriticalSectionLocker(wxPGGlobalVars->m_critSect);
+#endif
+ m_pg->m_liveEvents.push_back(this);
}
// -----------------------------------------------------------------------
-wxEvent* wxPropertyGridEvent::Clone() const
+wxPropertyGridEvent::~wxPropertyGridEvent()
{
- return new wxPropertyGridEvent( *this );
+ if ( m_pg )
+ {
+ #if wxUSE_THREADS
+ wxCriticalSectionLocker(wxPGGlobalVars->m_critSect);
+ #endif
+
+ // Use iterate from the back since it is more likely that the event
+ // being desroyed is at the end of the array.
+ wxVector<wxPropertyGridEvent*>& liveEvents = m_pg->m_liveEvents;
+
+ for ( int i = liveEvents.size()-1; i >= 0; i-- )
+ {
+ if ( liveEvents[i] == this )
+ {
+ liveEvents.erase(liveEvents.begin() + i);
+ break;
+ }
+ }
+ }
}
-void wxPropertyGrid::SetPropertyAttributeAll( const wxString& attrName, wxVariant value )
+// -----------------------------------------------------------------------
+
+wxEvent* wxPropertyGridEvent::Clone() const
{
- DoSetPropertyAttribute(GetRoot(), attrName, value, wxPG_RECURSE);
+ return new wxPropertyGridEvent( *this );
}
// -----------------------------------------------------------------------
m_state->DoInsert(parent, -1, property);
if ( propValue )
- property->SetValueFromString( *propValue, wxPG_FULL_VALUE );
+ property->SetValueFromString( *propValue, wxPG_FULL_VALUE|
+ wxPG_PROGRAMMATIC_VALUE );
return property;
}
else
{
bool found = false;
- if ( idString.length() )
+ if ( !idString.empty() )
{
wxPGHashMapS2P::iterator it = m_dictIdChoices.find(idString);
if ( it != m_dictIdChoices.end() )
int state = 0;
bool labelValid = false;
- for ( ; it != choicesString.end(); it++ )
+ for ( ; it != choicesString.end(); ++it )
{
wxChar c = *it;
}
// Assign to id
- if ( idString.length() )
+ if ( !idString.empty() )
m_dictIdChoices[idString] = choices.GetData();
}
}
wxString valuel = value.Lower();
wxVariant variant;
- if ( type.length() == 0 )
+ if ( type.empty() )
{
long v;
}
// -----------------------------------------------------------------------
+
+#endif // wxUSE_PROPGRID