Added multiple selection feature to wxPropertyGrid (enabled by setting wxPG_EX_MULTIP...
[wxWidgets.git] / src / propgrid / propgrid.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/propgrid/propgrid.cpp
3 // Purpose: wxPropertyGrid
4 // Author: Jaakko Salli
5 // Modified by:
6 // Created: 2004-09-25
7 // RCS-ID: $Id$
8 // Copyright: (c) Jaakko Salli
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_PROPGRID
20
21 #ifndef WX_PRECOMP
22 #include "wx/defs.h"
23 #include "wx/object.h"
24 #include "wx/hash.h"
25 #include "wx/string.h"
26 #include "wx/log.h"
27 #include "wx/event.h"
28 #include "wx/window.h"
29 #include "wx/panel.h"
30 #include "wx/dc.h"
31 #include "wx/dcmemory.h"
32 #include "wx/button.h"
33 #include "wx/pen.h"
34 #include "wx/brush.h"
35 #include "wx/cursor.h"
36 #include "wx/dialog.h"
37 #include "wx/settings.h"
38 #include "wx/msgdlg.h"
39 #include "wx/choice.h"
40 #include "wx/stattext.h"
41 #include "wx/scrolwin.h"
42 #include "wx/dirdlg.h"
43 #include "wx/sizer.h"
44 #include "wx/textdlg.h"
45 #include "wx/filedlg.h"
46 #include "wx/statusbr.h"
47 #include "wx/intl.h"
48 #include "wx/frame.h"
49 #endif
50
51
52 // This define is necessary to prevent macro clearing
53 #define __wxPG_SOURCE_FILE__
54
55 #include "wx/propgrid/propgrid.h"
56 #include "wx/propgrid/editors.h"
57
58 #if wxPG_USE_RENDERER_NATIVE
59 #include "wx/renderer.h"
60 #endif
61
62 #include "wx/odcombo.h"
63
64 #include "wx/timer.h"
65 #include "wx/dcbuffer.h"
66
67 #ifdef __WXMSW__
68 #include "wx/msw/private.h"
69 #endif
70
71 // Two pics for the expand / collapse buttons.
72 // Files are not supplied with this project (since it is
73 // recommended to use either custom or native rendering).
74 // If you want them, get wxTreeMultiCtrl by Jorgen Bodde,
75 // and copy xpm files from archive to wxPropertyGrid src directory
76 // (and also comment/undef wxPG_ICON_WIDTH in propGrid.h
77 // and set wxPG_USE_RENDERER_NATIVE to 0).
78 #ifndef wxPG_ICON_WIDTH
79 #if defined(__WXMAC__)
80 #include "mac_collapse.xpm"
81 #include "mac_expand.xpm"
82 #elif defined(__WXGTK__)
83 #include "linux_collapse.xpm"
84 #include "linux_expand.xpm"
85 #else
86 #include "default_collapse.xpm"
87 #include "default_expand.xpm"
88 #endif
89 #endif
90
91
92 //#define wxPG_TEXT_INDENT 4 // For the wxComboControl
93 //#define wxPG_ALLOW_CLIPPING 1 // If 1, GetUpdateRegion() in OnPaint event handler is not ignored
94 #define wxPG_GUTTER_DIV 3 // gutter is max(iconwidth/gutter_div,gutter_min)
95 #define wxPG_GUTTER_MIN 3 // gutter before and after image of [+] or [-]
96 #define wxPG_YSPACING_MIN 1
97 #define wxPG_DEFAULT_VSPACING 2 // This matches .NET propertygrid's value,
98 // but causes normal combobox to spill out under MSW
99
100 //#define wxPG_OPTIMAL_WIDTH 200 // Arbitrary
101
102 //#define wxPG_MIN_SCROLLBAR_WIDTH 10 // Smallest scrollbar width on any platform
103 // Must be larger than largest control border
104 // width * 2.
105
106
107 #define wxPG_DEFAULT_CURSOR wxNullCursor
108
109
110 //#define wxPG_NAT_CHOICE_BORDER_ANY 0
111
112 //#define wxPG_HIDER_BUTTON_HEIGHT 25
113
114 #define wxPG_PIXELS_PER_UNIT m_lineHeight
115
116 #ifdef wxPG_ICON_WIDTH
117 #define m_iconHeight m_iconWidth
118 #endif
119
120 //#define wxPG_TOOLTIP_DELAY 1000
121
122 // -----------------------------------------------------------------------
123
124 #if wxUSE_INTL
125 void wxPropertyGrid::AutoGetTranslation ( bool enable )
126 {
127 wxPGGlobalVars->m_autoGetTranslation = enable;
128 }
129 #else
130 void wxPropertyGrid::AutoGetTranslation ( bool ) { }
131 #endif
132
133 // -----------------------------------------------------------------------
134
135 const char wxPropertyGridNameStr[] = "wxPropertyGrid";
136
137 // -----------------------------------------------------------------------
138 // Statics in one class for easy destruction.
139 // -----------------------------------------------------------------------
140
141 #include "wx/module.h"
142
143 class wxPGGlobalVarsClassManager : public wxModule
144 {
145 DECLARE_DYNAMIC_CLASS(wxPGGlobalVarsClassManager)
146 public:
147 wxPGGlobalVarsClassManager() {}
148 virtual bool OnInit() { wxPGGlobalVars = new wxPGGlobalVarsClass(); return true; }
149 virtual void OnExit() { delete wxPGGlobalVars; wxPGGlobalVars = NULL; }
150 };
151
152 IMPLEMENT_DYNAMIC_CLASS(wxPGGlobalVarsClassManager, wxModule)
153
154
155 // When wxPG is loaded dynamically after the application is already running
156 // then the built-in module system won't pick this one up. Add it manually.
157 void wxPGInitResourceModule()
158 {
159 wxModule* module = new wxPGGlobalVarsClassManager;
160 module->Init();
161 wxModule::RegisterModule(module);
162 }
163
164 wxPGGlobalVarsClass* wxPGGlobalVars = NULL;
165
166
167 wxPGGlobalVarsClass::wxPGGlobalVarsClass()
168 {
169 wxPGProperty::sm_wxPG_LABEL = new wxString(wxPG_LABEL_STRING);
170
171 m_boolChoices.Add(_("False"));
172 m_boolChoices.Add(_("True"));
173
174 m_fontFamilyChoices = NULL;
175
176 m_defaultRenderer = new wxPGDefaultRenderer();
177
178 m_autoGetTranslation = false;
179
180 m_offline = 0;
181
182 m_extraStyle = 0;
183
184 wxVariant v;
185
186 // Prepare some shared variants
187 m_vEmptyString = wxString();
188 m_vZero = (long) 0;
189 m_vMinusOne = (long) -1;
190 m_vTrue = true;
191 m_vFalse = false;
192
193 // Prepare cached string constants
194 m_strstring = wxS("string");
195 m_strlong = wxS("long");
196 m_strbool = wxS("bool");
197 m_strlist = wxS("list");
198 m_strDefaultValue = wxS("DefaultValue");
199 m_strMin = wxS("Min");
200 m_strMax = wxS("Max");
201 m_strUnits = wxS("Units");
202 m_strInlineHelp = wxS("InlineHelp");
203
204 m_warnings = 0;
205 }
206
207
208 wxPGGlobalVarsClass::~wxPGGlobalVarsClass()
209 {
210 size_t i;
211
212 delete m_defaultRenderer;
213
214 // This will always have one ref
215 delete m_fontFamilyChoices;
216
217 #if wxUSE_VALIDATORS
218 for ( i=0; i<m_arrValidators.size(); i++ )
219 delete ((wxValidator*)m_arrValidators[i]);
220 #endif
221
222 //
223 // Destroy value type class instances.
224 wxPGHashMapS2P::iterator vt_it;
225
226 // Destroy editor class instances.
227 // iterate over all the elements in the class
228 for( vt_it = m_mapEditorClasses.begin(); vt_it != m_mapEditorClasses.end(); ++vt_it )
229 {
230 delete ((wxPGEditor*)vt_it->second);
231 }
232
233 delete wxPGProperty::sm_wxPG_LABEL;
234 }
235
236 void wxPropertyGridInitGlobalsIfNeeded()
237 {
238 }
239
240 // -----------------------------------------------------------------------
241 // wxPGCanvas
242 // -----------------------------------------------------------------------
243
244 //
245 // wxPGCanvas acts as a graphics sub-window of the
246 // wxScrolledWindow that wxPropertyGrid is.
247 //
248 class wxPGCanvas : public wxPanel
249 {
250 public:
251 wxPGCanvas() : wxPanel()
252 {
253 }
254 virtual ~wxPGCanvas() { }
255
256 protected:
257 void OnMouseMove( wxMouseEvent &event )
258 {
259 wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
260 pg->OnMouseMove( event );
261 }
262
263 void OnMouseClick( wxMouseEvent &event )
264 {
265 wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
266 pg->OnMouseClick( event );
267 }
268
269 void OnMouseUp( wxMouseEvent &event )
270 {
271 wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
272 pg->OnMouseUp( event );
273 }
274
275 void OnMouseRightClick( wxMouseEvent &event )
276 {
277 wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
278 pg->OnMouseRightClick( event );
279 }
280
281 void OnMouseDoubleClick( wxMouseEvent &event )
282 {
283 wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
284 pg->OnMouseDoubleClick( event );
285 }
286
287 void OnKey( wxKeyEvent& event )
288 {
289 wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
290 pg->OnKey( event );
291 }
292
293 void OnPaint( wxPaintEvent& event );
294
295 // Always be focussable, even with child windows
296 virtual void SetCanFocus(bool WXUNUSED(canFocus))
297 { wxPanel::SetCanFocus(true); }
298
299
300 private:
301 DECLARE_EVENT_TABLE()
302 DECLARE_ABSTRACT_CLASS(wxPGCanvas)
303 };
304
305
306 IMPLEMENT_ABSTRACT_CLASS(wxPGCanvas,wxPanel)
307
308 BEGIN_EVENT_TABLE(wxPGCanvas, wxPanel)
309 EVT_MOTION(wxPGCanvas::OnMouseMove)
310 EVT_PAINT(wxPGCanvas::OnPaint)
311 EVT_LEFT_DOWN(wxPGCanvas::OnMouseClick)
312 EVT_LEFT_UP(wxPGCanvas::OnMouseUp)
313 EVT_RIGHT_UP(wxPGCanvas::OnMouseRightClick)
314 EVT_LEFT_DCLICK(wxPGCanvas::OnMouseDoubleClick)
315 EVT_KEY_DOWN(wxPGCanvas::OnKey)
316 END_EVENT_TABLE()
317
318
319 void wxPGCanvas::OnPaint( wxPaintEvent& WXUNUSED(event) )
320 {
321 wxPropertyGrid* pg = wxStaticCast(GetParent(), wxPropertyGrid);
322 wxASSERT( pg->IsKindOf(CLASSINFO(wxPropertyGrid)) );
323
324 wxPaintDC dc(this);
325
326 // Don't paint after destruction has begun
327 if ( !(pg->GetInternalFlags() & wxPG_FL_INITIALIZED) )
328 return;
329
330 // Update everything inside the box
331 wxRect r = GetUpdateRegion().GetBox();
332
333 // FIXME: This is just a workaround for a bug that causes splitters not
334 // to paint when other windows are being dragged over the grid.
335 wxRect fullRect = GetRect();
336 r.x = fullRect.x;
337 r.width = fullRect.width;
338
339 // Repaint this rectangle
340 pg->DrawItems( dc, r.y, r.y + r.height, &r );
341
342 // We assume that the size set when grid is shown
343 // is what is desired.
344 pg->SetInternalFlag(wxPG_FL_GOOD_SIZE_SET);
345 }
346
347 // -----------------------------------------------------------------------
348 // wxPropertyGrid
349 // -----------------------------------------------------------------------
350
351 IMPLEMENT_DYNAMIC_CLASS(wxPropertyGrid, wxScrolledWindow)
352
353 BEGIN_EVENT_TABLE(wxPropertyGrid, wxScrolledWindow)
354 EVT_IDLE(wxPropertyGrid::OnIdle)
355 EVT_MOTION(wxPropertyGrid::OnMouseMoveBottom)
356 EVT_PAINT(wxPropertyGrid::OnPaint)
357 EVT_SIZE(wxPropertyGrid::OnResize)
358 EVT_ENTER_WINDOW(wxPropertyGrid::OnMouseEntry)
359 EVT_LEAVE_WINDOW(wxPropertyGrid::OnMouseEntry)
360 EVT_MOUSE_CAPTURE_CHANGED(wxPropertyGrid::OnCaptureChange)
361 EVT_SCROLLWIN(wxPropertyGrid::OnScrollEvent)
362 EVT_CHILD_FOCUS(wxPropertyGrid::OnChildFocusEvent)
363 EVT_SET_FOCUS(wxPropertyGrid::OnFocusEvent)
364 EVT_KILL_FOCUS(wxPropertyGrid::OnFocusEvent)
365 EVT_SYS_COLOUR_CHANGED(wxPropertyGrid::OnSysColourChanged)
366 END_EVENT_TABLE()
367
368
369 // -----------------------------------------------------------------------
370
371 wxPropertyGrid::wxPropertyGrid()
372 : wxScrolledWindow()
373 {
374 Init1();
375 }
376
377 // -----------------------------------------------------------------------
378
379 wxPropertyGrid::wxPropertyGrid( wxWindow *parent,
380 wxWindowID id,
381 const wxPoint& pos,
382 const wxSize& size,
383 long style,
384 const wxString& name )
385 : wxScrolledWindow()
386 {
387 Init1();
388 Create(parent,id,pos,size,style,name);
389 }
390
391 // -----------------------------------------------------------------------
392
393 bool wxPropertyGrid::Create( wxWindow *parent,
394 wxWindowID id,
395 const wxPoint& pos,
396 const wxSize& size,
397 long style,
398 const wxString& name )
399 {
400
401 if ( !(style&wxBORDER_MASK) )
402 style |= wxSIMPLE_BORDER;
403
404 style |= wxVSCROLL;
405
406 // Filter out wxTAB_TRAVERSAL - we will handle TABs manually
407 style &= ~(wxTAB_TRAVERSAL);
408 style |= wxWANTS_CHARS;
409
410 wxScrolledWindow::Create(parent,id,pos,size,style,name);
411
412 Init2();
413
414 return true;
415 }
416
417 // -----------------------------------------------------------------------
418
419 //
420 // Initialize values to defaults
421 //
422 void wxPropertyGrid::Init1()
423 {
424 // Register editor classes, if necessary.
425 if ( wxPGGlobalVars->m_mapEditorClasses.empty() )
426 wxPropertyGrid::RegisterDefaultEditors();
427
428 m_iFlags = 0;
429 m_pState = NULL;
430 m_wndEditor = m_wndEditor2 = NULL;
431 m_selColumn = -1;
432 m_propHover = NULL;
433 m_eventObject = this;
434 m_curFocused = NULL;
435 m_sortFunction = NULL;
436 m_inDoPropertyChanged = 0;
437 m_inCommitChangesFromEditor = 0;
438 m_inDoSelectProperty = 0;
439 m_permanentValidationFailureBehavior = wxPG_VFB_DEFAULT;
440 m_dragStatus = 0;
441 m_mouseSide = 16;
442 m_editorFocused = 0;
443
444 // Set default keys
445 AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RIGHT );
446 AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_DOWN );
447 AddActionTrigger( wxPG_ACTION_PREV_PROPERTY, WXK_LEFT );
448 AddActionTrigger( wxPG_ACTION_PREV_PROPERTY, WXK_UP );
449 AddActionTrigger( wxPG_ACTION_EXPAND_PROPERTY, WXK_RIGHT);
450 AddActionTrigger( wxPG_ACTION_COLLAPSE_PROPERTY, WXK_LEFT);
451 AddActionTrigger( wxPG_ACTION_CANCEL_EDIT, WXK_ESCAPE );
452 AddActionTrigger( wxPG_ACTION_PRESS_BUTTON, WXK_DOWN, wxMOD_ALT );
453 AddActionTrigger( wxPG_ACTION_PRESS_BUTTON, WXK_F4 );
454
455 m_coloursCustomized = 0;
456 m_frozen = 0;
457
458 m_canvas = NULL;
459
460 #if wxPG_DOUBLE_BUFFER
461 m_doubleBuffer = NULL;
462 #endif
463
464 #ifndef wxPG_ICON_WIDTH
465 m_expandbmp = NULL;
466 m_collbmp = NULL;
467 m_iconWidth = 11;
468 m_iconHeight = 11;
469 #else
470 m_iconWidth = wxPG_ICON_WIDTH;
471 #endif
472
473 m_prevVY = -1;
474
475 m_gutterWidth = wxPG_GUTTER_MIN;
476 m_subgroup_extramargin = 10;
477
478 m_lineHeight = 0;
479
480 m_width = m_height = 0;
481
482 m_commonValues.push_back(new wxPGCommonValue(_("Unspecified"), wxPGGlobalVars->m_defaultRenderer) );
483 m_cvUnspecified = 0;
484
485 m_chgInfo_changedProperty = NULL;
486 }
487
488 // -----------------------------------------------------------------------
489
490 //
491 // Initialize after parent etc. set
492 //
493 void wxPropertyGrid::Init2()
494 {
495 wxASSERT( !(m_iFlags & wxPG_FL_INITIALIZED ) );
496
497 #ifdef __WXMAC__
498 // Smaller controls on Mac
499 SetWindowVariant(wxWINDOW_VARIANT_SMALL);
500 #endif
501
502 // Now create state, if one didn't exist already
503 // (wxPropertyGridManager might have created it for us).
504 if ( !m_pState )
505 {
506 m_pState = CreateState();
507 m_pState->m_pPropGrid = this;
508 m_iFlags |= wxPG_FL_CREATEDSTATE;
509 }
510
511 if ( !(m_windowStyle & wxPG_SPLITTER_AUTO_CENTER) )
512 m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
513
514 if ( m_windowStyle & wxPG_HIDE_CATEGORIES )
515 {
516 m_pState->InitNonCatMode();
517
518 m_pState->m_properties = m_pState->m_abcArray;
519 }
520
521 GetClientSize(&m_width,&m_height);
522
523 #ifndef wxPG_ICON_WIDTH
524 // create two bitmap nodes for drawing
525 m_expandbmp = new wxBitmap(expand_xpm);
526 m_collbmp = new wxBitmap(collapse_xpm);
527
528 // calculate average font height for bitmap centering
529
530 m_iconWidth = m_expandbmp->GetWidth();
531 m_iconHeight = m_expandbmp->GetHeight();
532 #endif
533
534 m_curcursor = wxCURSOR_ARROW;
535 m_cursorSizeWE = new wxCursor( wxCURSOR_SIZEWE );
536
537 // adjust bitmap icon y position so they are centered
538 m_vspacing = wxPG_DEFAULT_VSPACING;
539
540 CalculateFontAndBitmapStuff( wxPG_DEFAULT_VSPACING );
541
542 // Allocate cell datas indirectly by calling setter
543 m_propertyDefaultCell.SetBgCol(*wxBLACK);
544 m_categoryDefaultCell.SetBgCol(*wxBLACK);
545
546 RegainColours();
547
548 // This helps with flicker
549 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
550
551 // Hook the top-level parent
552 m_tlp = NULL;
553 m_tlpClosed = NULL;
554 m_tlpClosedTime = 0;
555 OnTLPChanging(::wxGetTopLevelParent(this));
556
557 // set virtual size to this window size
558 wxSize wndsize = GetSize();
559 SetVirtualSize(wndsize.GetWidth(), wndsize.GetWidth());
560
561 m_timeCreated = ::wxGetLocalTimeMillis();
562
563 m_canvas = new wxPGCanvas();
564 m_canvas->Create(this, 1, wxPoint(0, 0), GetClientSize(),
565 wxWANTS_CHARS | wxCLIP_CHILDREN);
566 m_canvas->SetBackgroundStyle( wxBG_STYLE_CUSTOM );
567
568 m_iFlags |= wxPG_FL_INITIALIZED;
569
570 m_ncWidth = wndsize.GetWidth();
571
572 // Need to call OnResize handler or size given in constructor/Create
573 // will never work.
574 wxSizeEvent sizeEvent(wndsize,0);
575 OnResize(sizeEvent);
576 }
577
578 // -----------------------------------------------------------------------
579
580 wxPropertyGrid::~wxPropertyGrid()
581 {
582 size_t i;
583
584 DoSelectProperty(NULL, wxPG_SEL_NOVALIDATE|wxPG_SEL_DONT_SEND_EVENT);
585
586 // This should do prevent things from going too badly wrong
587 m_iFlags &= ~(wxPG_FL_INITIALIZED);
588
589 if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
590 m_canvas->ReleaseMouse();
591
592 // Call with NULL to disconnect event handling
593 OnTLPChanging(NULL);
594
595 wxASSERT_MSG( !IsEditorsValueModified(),
596 wxS("Most recent change in property editor was lost!!! ")
597 wxS("(if you don't want this to happen, close your frames ")
598 wxS("and dialogs using Close(false).)") );
599
600 #if wxPG_DOUBLE_BUFFER
601 if ( m_doubleBuffer )
602 delete m_doubleBuffer;
603 #endif
604
605 if ( m_iFlags & wxPG_FL_CREATEDSTATE )
606 delete m_pState;
607
608 delete m_cursorSizeWE;
609
610 #ifndef wxPG_ICON_WIDTH
611 delete m_expandbmp;
612 delete m_collbmp;
613 #endif
614
615 // Delete common value records
616 for ( i=0; i<m_commonValues.size(); i++ )
617 {
618 delete GetCommonValue(i);
619 }
620 }
621
622 // -----------------------------------------------------------------------
623
624 bool wxPropertyGrid::Destroy()
625 {
626 if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
627 m_canvas->ReleaseMouse();
628
629 return wxScrolledWindow::Destroy();
630 }
631
632 // -----------------------------------------------------------------------
633
634 wxPropertyGridPageState* wxPropertyGrid::CreateState() const
635 {
636 return new wxPropertyGridPageState();
637 }
638
639 // -----------------------------------------------------------------------
640 // wxPropertyGrid overridden wxWindow methods
641 // -----------------------------------------------------------------------
642
643 void wxPropertyGrid::SetWindowStyleFlag( long style )
644 {
645 long old_style = m_windowStyle;
646
647 if ( m_iFlags & wxPG_FL_INITIALIZED )
648 {
649 wxASSERT( m_pState );
650
651 if ( !(style & wxPG_HIDE_CATEGORIES) && (old_style & wxPG_HIDE_CATEGORIES) )
652 {
653 // Enable categories
654 EnableCategories( true );
655 }
656 else if ( (style & wxPG_HIDE_CATEGORIES) && !(old_style & wxPG_HIDE_CATEGORIES) )
657 {
658 // Disable categories
659 EnableCategories( false );
660 }
661 if ( !(old_style & wxPG_AUTO_SORT) && (style & wxPG_AUTO_SORT) )
662 {
663 //
664 // Autosort enabled
665 //
666 if ( !m_frozen )
667 PrepareAfterItemsAdded();
668 else
669 m_pState->m_itemsAdded = 1;
670 }
671 #if wxPG_SUPPORT_TOOLTIPS
672 if ( !(old_style & wxPG_TOOLTIPS) && (style & wxPG_TOOLTIPS) )
673 {
674 //
675 // Tooltips enabled
676 //
677 /*
678 wxToolTip* tooltip = new wxToolTip ( wxEmptyString );
679 SetToolTip ( tooltip );
680 tooltip->SetDelay ( wxPG_TOOLTIP_DELAY );
681 */
682 }
683 else if ( (old_style & wxPG_TOOLTIPS) && !(style & wxPG_TOOLTIPS) )
684 {
685 //
686 // Tooltips disabled
687 //
688 m_canvas->SetToolTip( NULL );
689 }
690 #endif
691 }
692
693 wxScrolledWindow::SetWindowStyleFlag ( style );
694
695 if ( m_iFlags & wxPG_FL_INITIALIZED )
696 {
697 if ( (old_style & wxPG_HIDE_MARGIN) != (style & wxPG_HIDE_MARGIN) )
698 {
699 CalculateFontAndBitmapStuff( m_vspacing );
700 Refresh();
701 }
702 }
703 }
704
705 // -----------------------------------------------------------------------
706
707 void wxPropertyGrid::Freeze()
708 {
709 if ( !m_frozen )
710 {
711 wxScrolledWindow::Freeze();
712 }
713 m_frozen++;
714 }
715
716 // -----------------------------------------------------------------------
717
718 void wxPropertyGrid::Thaw()
719 {
720 m_frozen--;
721
722 if ( !m_frozen )
723 {
724 wxScrolledWindow::Thaw();
725 RecalculateVirtualSize();
726 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
727 m_canvas->Refresh();
728 #endif
729
730 // Force property re-selection
731 // NB: We must copy the selection.
732 wxArrayPGProperty selection = m_pState->m_selection;
733 DoSetSelection(selection, wxPG_SEL_FORCE);
734 }
735 }
736
737 // -----------------------------------------------------------------------
738
739 bool wxPropertyGrid::DoAddToSelection( wxPGProperty* prop, int selFlags )
740 {
741 wxCHECK( prop, false );
742
743 if ( !(GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION) )
744 return DoSelectProperty(prop, selFlags);
745
746 wxArrayPGProperty& selection = m_pState->m_selection;
747
748 if ( !selection.size() )
749 {
750 return DoSelectProperty(prop, selFlags);
751 }
752 else
753 {
754 // For categories, only one can be selected at a time
755 if ( prop->IsCategory() || selection[0]->IsCategory() )
756 return true;
757
758 selection.push_back(prop);
759
760 if ( !(selFlags & wxPG_SEL_DONT_SEND_EVENT) )
761 {
762 SendEvent( wxEVT_PG_SELECTED, prop, NULL, selFlags );
763 }
764
765 // For some reason, if we use RefreshProperty(prop) here,
766 // we may go into infinite drawing loop.
767 Refresh();
768 }
769
770 return true;
771 }
772
773 // -----------------------------------------------------------------------
774
775 bool wxPropertyGrid::DoRemoveFromSelection( wxPGProperty* prop, int selFlags )
776 {
777 wxCHECK( prop, false );
778 bool res;
779
780 wxArrayPGProperty& selection = m_pState->m_selection;
781 if ( selection.size() <= 1 )
782 {
783 res = DoSelectProperty(NULL, selFlags);
784 }
785 else
786 {
787 selection.Remove(prop);
788 RefreshProperty(prop);
789 res = true;
790 }
791
792 return res;
793 }
794
795 // -----------------------------------------------------------------------
796
797 bool wxPropertyGrid::AddToSelectionFromInputEvent( wxPGProperty* prop,
798 wxMouseEvent* mouseEvent,
799 int selFlags )
800 {
801 bool alreadySelected = m_pState->DoIsPropertySelected(prop);
802 bool res = true;
803 bool addToExistingSelection;
804
805 if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION )
806 {
807 if ( mouseEvent )
808 {
809 if ( mouseEvent->GetEventType() == wxEVT_RIGHT_DOWN ||
810 mouseEvent->GetEventType() == wxEVT_RIGHT_UP )
811 {
812 // Allow right-click for context menu without
813 // disturbing the selection.
814 if ( GetSelectedProperties().size() <= 1 ||
815 !alreadySelected )
816 return DoSelectProperty(prop, selFlags);
817 return true;
818 }
819 else
820 {
821 addToExistingSelection = mouseEvent->ShiftDown();
822 }
823 }
824 else
825 {
826 addToExistingSelection = false;
827 }
828 }
829 else
830 {
831 addToExistingSelection = false;
832 }
833
834 if ( addToExistingSelection )
835 {
836 if ( !alreadySelected )
837 {
838 res = DoAddToSelection(prop, selFlags);
839 }
840 else if ( GetSelectedProperties().size() > 1 )
841 {
842 res = DoRemoveFromSelection(prop, selFlags);
843 }
844 }
845 else
846 {
847 res = DoSelectProperty(prop, selFlags);
848 }
849
850 return res;
851 }
852
853 // -----------------------------------------------------------------------
854
855 void wxPropertyGrid::DoSetSelection( const wxArrayPGProperty& newSelection,
856 int selFlags )
857 {
858 if ( newSelection.size() > 0 )
859 {
860 if ( !DoSelectProperty(newSelection[0], selFlags) )
861 return;
862 }
863 else
864 {
865 DoClearSelection(false, selFlags);
866 }
867
868 for ( unsigned int i = 1; i < newSelection.size(); i++ )
869 {
870 DoAddToSelection(newSelection[i], selFlags);
871 }
872
873 Refresh();
874 }
875
876 // -----------------------------------------------------------------------
877
878 void wxPropertyGrid::SetExtraStyle( long exStyle )
879 {
880 if ( exStyle & wxPG_EX_NATIVE_DOUBLE_BUFFERING )
881 {
882 #if defined(__WXMSW__)
883
884 /*
885 // Don't use WS_EX_COMPOSITED just now.
886 HWND hWnd;
887
888 if ( m_iFlags & wxPG_FL_IN_MANAGER )
889 hWnd = (HWND)GetParent()->GetHWND();
890 else
891 hWnd = (HWND)GetHWND();
892
893 ::SetWindowLong( hWnd, GWL_EXSTYLE,
894 ::GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_COMPOSITED );
895 */
896
897 //#elif defined(__WXGTK20__)
898 #endif
899 // Only apply wxPG_EX_NATIVE_DOUBLE_BUFFERING if the window
900 // truly was double-buffered.
901 if ( !this->IsDoubleBuffered() )
902 {
903 exStyle &= ~(wxPG_EX_NATIVE_DOUBLE_BUFFERING);
904 }
905 else
906 {
907 #if wxPG_DOUBLE_BUFFER
908 delete m_doubleBuffer;
909 m_doubleBuffer = NULL;
910 #endif
911 }
912 }
913
914 wxScrolledWindow::SetExtraStyle( exStyle );
915
916 if ( exStyle & wxPG_EX_INIT_NOCAT )
917 m_pState->InitNonCatMode();
918
919 if ( exStyle & wxPG_EX_HELP_AS_TOOLTIPS )
920 m_windowStyle |= wxPG_TOOLTIPS;
921
922 // Set global style
923 wxPGGlobalVars->m_extraStyle = exStyle;
924 }
925
926 // -----------------------------------------------------------------------
927
928 // returns the best acceptable minimal size
929 wxSize wxPropertyGrid::DoGetBestSize() const
930 {
931 int lineHeight = wxMax(15, m_lineHeight);
932
933 // don't make the grid too tall (limit height to 10 items) but don't
934 // make it too small neither
935 int numLines = wxMin
936 (
937 wxMax(m_pState->m_properties->GetChildCount(), 3),
938 10
939 );
940
941 const wxSize sz = wxSize(60, lineHeight*numLines + 40);
942 CacheBestSize(sz);
943 return sz;
944 }
945
946 // -----------------------------------------------------------------------
947
948 void wxPropertyGrid::OnTLPChanging( wxWindow* newTLP )
949 {
950 wxLongLong currentTime = ::wxGetLocalTimeMillis();
951
952 //
953 // Parent changed so let's redetermine and re-hook the
954 // correct top-level window.
955 if ( m_tlp )
956 {
957 m_tlp->Disconnect( wxEVT_CLOSE_WINDOW,
958 wxCloseEventHandler(wxPropertyGrid::OnTLPClose),
959 NULL, this );
960 m_tlpClosed = m_tlp;
961 m_tlpClosedTime = currentTime;
962 }
963
964 if ( newTLP )
965 {
966 // Only accept new tlp if same one was not just dismissed.
967 if ( newTLP != m_tlpClosed ||
968 m_tlpClosedTime+250 < currentTime )
969 {
970 newTLP->Connect( wxEVT_CLOSE_WINDOW,
971 wxCloseEventHandler(wxPropertyGrid::OnTLPClose),
972 NULL, this );
973 m_tlpClosed = NULL;
974 }
975 else
976 {
977 newTLP = NULL;
978 }
979 }
980
981 m_tlp = newTLP;
982 }
983
984 // -----------------------------------------------------------------------
985
986 void wxPropertyGrid::OnTLPClose( wxCloseEvent& event )
987 {
988 // ClearSelection forces value validation/commit.
989 if ( event.CanVeto() && !DoClearSelection() )
990 {
991 event.Veto();
992 return;
993 }
994
995 // Ok, it can close, set tlp pointer to NULL. Some other event
996 // handler can of course veto the close, but our OnIdle() should
997 // then be able to regain the tlp pointer.
998 OnTLPChanging(NULL);
999
1000 event.Skip();
1001 }
1002
1003 // -----------------------------------------------------------------------
1004
1005 bool wxPropertyGrid::Reparent( wxWindowBase *newParent )
1006 {
1007 OnTLPChanging((wxWindow*)newParent);
1008
1009 bool res = wxScrolledWindow::Reparent(newParent);
1010
1011 return res;
1012 }
1013
1014 // -----------------------------------------------------------------------
1015 // wxPropertyGrid Font and Colour Methods
1016 // -----------------------------------------------------------------------
1017
1018 void wxPropertyGrid::CalculateFontAndBitmapStuff( int vspacing )
1019 {
1020 int x = 0, y = 0;
1021
1022 m_captionFont = wxScrolledWindow::GetFont();
1023
1024 GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont);
1025 m_subgroup_extramargin = x + (x/2);
1026 m_fontHeight = y;
1027
1028 #if wxPG_USE_RENDERER_NATIVE
1029 m_iconWidth = wxPG_ICON_WIDTH;
1030 #elif wxPG_ICON_WIDTH
1031 // scale icon
1032 m_iconWidth = (m_fontHeight * wxPG_ICON_WIDTH) / 13;
1033 if ( m_iconWidth < 5 ) m_iconWidth = 5;
1034 else if ( !(m_iconWidth & 0x01) ) m_iconWidth++; // must be odd
1035
1036 #endif
1037
1038 m_gutterWidth = m_iconWidth / wxPG_GUTTER_DIV;
1039 if ( m_gutterWidth < wxPG_GUTTER_MIN )
1040 m_gutterWidth = wxPG_GUTTER_MIN;
1041
1042 int vdiv = 6;
1043 if ( vspacing <= 1 ) vdiv = 12;
1044 else if ( vspacing >= 3 ) vdiv = 3;
1045
1046 m_spacingy = m_fontHeight / vdiv;
1047 if ( m_spacingy < wxPG_YSPACING_MIN )
1048 m_spacingy = wxPG_YSPACING_MIN;
1049
1050 m_marginWidth = 0;
1051 if ( !(m_windowStyle & wxPG_HIDE_MARGIN) )
1052 m_marginWidth = m_gutterWidth*2 + m_iconWidth;
1053
1054 m_captionFont.SetWeight(wxBOLD);
1055 GetTextExtent(wxS("jG"), &x, &y, 0, 0, &m_captionFont);
1056
1057 m_lineHeight = m_fontHeight+(2*m_spacingy)+1;
1058
1059 // button spacing
1060 m_buttonSpacingY = (m_lineHeight - m_iconHeight) / 2;
1061 if ( m_buttonSpacingY < 0 ) m_buttonSpacingY = 0;
1062
1063 if ( m_pState )
1064 m_pState->CalculateFontAndBitmapStuff(vspacing);
1065
1066 if ( m_iFlags & wxPG_FL_INITIALIZED )
1067 RecalculateVirtualSize();
1068
1069 InvalidateBestSize();
1070 }
1071
1072 // -----------------------------------------------------------------------
1073
1074 void wxPropertyGrid::OnSysColourChanged( wxSysColourChangedEvent &WXUNUSED(event) )
1075 {
1076 RegainColours();
1077 Refresh();
1078 }
1079
1080 // -----------------------------------------------------------------------
1081
1082 static wxColour wxPGAdjustColour(const wxColour& src, int ra,
1083 int ga = 1000, int ba = 1000,
1084 bool forceDifferent = false)
1085 {
1086 if ( ga >= 1000 )
1087 ga = ra;
1088 if ( ba >= 1000 )
1089 ba = ra;
1090
1091 // Recursion guard (allow 2 max)
1092 static int isinside = 0;
1093 isinside++;
1094 wxCHECK_MSG( isinside < 3,
1095 *wxBLACK,
1096 wxT("wxPGAdjustColour should not be recursively called more than once") );
1097
1098 wxColour dst;
1099
1100 int r = src.Red();
1101 int g = src.Green();
1102 int b = src.Blue();
1103 int r2 = r + ra;
1104 if ( r2>255 ) r2 = 255;
1105 else if ( r2<0) r2 = 0;
1106 int g2 = g + ga;
1107 if ( g2>255 ) g2 = 255;
1108 else if ( g2<0) g2 = 0;
1109 int b2 = b + ba;
1110 if ( b2>255 ) b2 = 255;
1111 else if ( b2<0) b2 = 0;
1112
1113 // Make sure they are somewhat different
1114 if ( forceDifferent && (abs((r+g+b)-(r2+g2+b2)) < abs(ra/2)) )
1115 dst = wxPGAdjustColour(src,-(ra*2));
1116 else
1117 dst = wxColour(r2,g2,b2);
1118
1119 // Recursion guard (allow 2 max)
1120 isinside--;
1121
1122 return dst;
1123 }
1124
1125
1126 static int wxPGGetColAvg( const wxColour& col )
1127 {
1128 return (col.Red() + col.Green() + col.Blue()) / 3;
1129 }
1130
1131
1132 void wxPropertyGrid::RegainColours()
1133 {
1134 if ( !(m_coloursCustomized & 0x0002) )
1135 {
1136 wxColour col = wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE );
1137
1138 // Make sure colour is dark enough
1139 #ifdef __WXGTK__
1140 int colDec = wxPGGetColAvg(col) - 230;
1141 #else
1142 int colDec = wxPGGetColAvg(col) - 200;
1143 #endif
1144 if ( colDec > 0 )
1145 m_colCapBack = wxPGAdjustColour(col,-colDec);
1146 else
1147 m_colCapBack = col;
1148 m_categoryDefaultCell.GetData()->SetBgCol(m_colCapBack);
1149 }
1150
1151 if ( !(m_coloursCustomized & 0x0001) )
1152 m_colMargin = m_colCapBack;
1153
1154 if ( !(m_coloursCustomized & 0x0004) )
1155 {
1156 #ifdef __WXGTK__
1157 int colDec = -90;
1158 #else
1159 int colDec = -72;
1160 #endif
1161 wxColour capForeCol = wxPGAdjustColour(m_colCapBack,colDec,5000,5000,true);
1162 m_colCapFore = capForeCol;
1163 m_categoryDefaultCell.GetData()->SetFgCol(capForeCol);
1164 }
1165
1166 if ( !(m_coloursCustomized & 0x0008) )
1167 {
1168 wxColour bgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
1169 m_colPropBack = bgCol;
1170 m_propertyDefaultCell.GetData()->SetBgCol(bgCol);
1171 }
1172
1173 if ( !(m_coloursCustomized & 0x0010) )
1174 {
1175 wxColour fgCol = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT );
1176 m_colPropFore = fgCol;
1177 m_propertyDefaultCell.GetData()->SetFgCol(fgCol);
1178 }
1179
1180 if ( !(m_coloursCustomized & 0x0020) )
1181 m_colSelBack = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT );
1182
1183 if ( !(m_coloursCustomized & 0x0040) )
1184 m_colSelFore = wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT );
1185
1186 if ( !(m_coloursCustomized & 0x0080) )
1187 m_colLine = m_colCapBack;
1188
1189 if ( !(m_coloursCustomized & 0x0100) )
1190 m_colDisPropFore = m_colCapFore;
1191
1192 m_colEmptySpace = wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW );
1193 }
1194
1195 // -----------------------------------------------------------------------
1196
1197 void wxPropertyGrid::ResetColours()
1198 {
1199 m_coloursCustomized = 0;
1200
1201 RegainColours();
1202
1203 Refresh();
1204 }
1205
1206 // -----------------------------------------------------------------------
1207
1208 bool wxPropertyGrid::SetFont( const wxFont& font )
1209 {
1210 // Must disable active editor.
1211 DoClearSelection();
1212
1213 bool res = wxScrolledWindow::SetFont( font );
1214 if ( res && GetParent()) // may not have been Create()ed yet
1215 {
1216 CalculateFontAndBitmapStuff( m_vspacing );
1217 Refresh();
1218 }
1219
1220 return res;
1221 }
1222
1223 // -----------------------------------------------------------------------
1224
1225 void wxPropertyGrid::SetLineColour( const wxColour& col )
1226 {
1227 m_colLine = col;
1228 m_coloursCustomized |= 0x80;
1229 Refresh();
1230 }
1231
1232 // -----------------------------------------------------------------------
1233
1234 void wxPropertyGrid::SetMarginColour( const wxColour& col )
1235 {
1236 m_colMargin = col;
1237 m_coloursCustomized |= 0x01;
1238 Refresh();
1239 }
1240
1241 // -----------------------------------------------------------------------
1242
1243 void wxPropertyGrid::SetCellBackgroundColour( const wxColour& col )
1244 {
1245 m_colPropBack = col;
1246 m_coloursCustomized |= 0x08;
1247
1248 m_propertyDefaultCell.GetData()->SetBgCol(col);
1249
1250 Refresh();
1251 }
1252
1253 // -----------------------------------------------------------------------
1254
1255 void wxPropertyGrid::SetCellTextColour( const wxColour& col )
1256 {
1257 m_colPropFore = col;
1258 m_coloursCustomized |= 0x10;
1259
1260 m_propertyDefaultCell.GetData()->SetFgCol(col);
1261
1262 Refresh();
1263 }
1264
1265 // -----------------------------------------------------------------------
1266
1267 void wxPropertyGrid::SetEmptySpaceColour( const wxColour& col )
1268 {
1269 m_colEmptySpace = col;
1270
1271 Refresh();
1272 }
1273
1274 // -----------------------------------------------------------------------
1275
1276 void wxPropertyGrid::SetCellDisabledTextColour( const wxColour& col )
1277 {
1278 m_colDisPropFore = col;
1279 m_coloursCustomized |= 0x100;
1280 Refresh();
1281 }
1282
1283 // -----------------------------------------------------------------------
1284
1285 void wxPropertyGrid::SetSelectionBackgroundColour( const wxColour& col )
1286 {
1287 m_colSelBack = col;
1288 m_coloursCustomized |= 0x20;
1289 Refresh();
1290 }
1291
1292 // -----------------------------------------------------------------------
1293
1294 void wxPropertyGrid::SetSelectionTextColour( const wxColour& col )
1295 {
1296 m_colSelFore = col;
1297 m_coloursCustomized |= 0x40;
1298 Refresh();
1299 }
1300
1301 // -----------------------------------------------------------------------
1302
1303 void wxPropertyGrid::SetCaptionBackgroundColour( const wxColour& col )
1304 {
1305 m_colCapBack = col;
1306 m_coloursCustomized |= 0x02;
1307
1308 m_categoryDefaultCell.GetData()->SetBgCol(col);
1309
1310 Refresh();
1311 }
1312
1313 // -----------------------------------------------------------------------
1314
1315 void wxPropertyGrid::SetCaptionTextColour( const wxColour& col )
1316 {
1317 m_colCapFore = col;
1318 m_coloursCustomized |= 0x04;
1319
1320 m_categoryDefaultCell.GetData()->SetFgCol(col);
1321
1322 Refresh();
1323 }
1324
1325 // -----------------------------------------------------------------------
1326 // wxPropertyGrid property adding and removal
1327 // -----------------------------------------------------------------------
1328
1329 void wxPropertyGrid::PrepareAfterItemsAdded()
1330 {
1331 if ( !m_pState || !m_pState->m_itemsAdded ) return;
1332
1333 m_pState->m_itemsAdded = 0;
1334
1335 if ( m_windowStyle & wxPG_AUTO_SORT )
1336 Sort(wxPG_SORT_TOP_LEVEL_ONLY);
1337
1338 RecalculateVirtualSize();
1339 }
1340
1341 // -----------------------------------------------------------------------
1342 // wxPropertyGrid property operations
1343 // -----------------------------------------------------------------------
1344
1345 bool wxPropertyGrid::EnsureVisible( wxPGPropArg id )
1346 {
1347 wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
1348
1349 Update();
1350
1351 bool changed = false;
1352
1353 // Is it inside collapsed section?
1354 if ( !p->IsVisible() )
1355 {
1356 // expand parents
1357 wxPGProperty* parent = p->GetParent();
1358 wxPGProperty* grandparent = parent->GetParent();
1359
1360 if ( grandparent && grandparent != m_pState->m_properties )
1361 Expand( grandparent );
1362
1363 Expand( parent );
1364 changed = true;
1365 }
1366
1367 // Need to scroll?
1368 int vx, vy;
1369 GetViewStart(&vx,&vy);
1370 vy*=wxPG_PIXELS_PER_UNIT;
1371
1372 int y = p->GetY();
1373
1374 if ( y < vy )
1375 {
1376 Scroll(vx, y/wxPG_PIXELS_PER_UNIT );
1377 m_iFlags |= wxPG_FL_SCROLLED;
1378 changed = true;
1379 }
1380 else if ( (y+m_lineHeight) > (vy+m_height) )
1381 {
1382 Scroll(vx, (y-m_height+(m_lineHeight*2))/wxPG_PIXELS_PER_UNIT );
1383 m_iFlags |= wxPG_FL_SCROLLED;
1384 changed = true;
1385 }
1386
1387 if ( changed )
1388 DrawItems( p, p );
1389
1390 return changed;
1391 }
1392
1393 // -----------------------------------------------------------------------
1394 // wxPropertyGrid helper methods called by properties
1395 // -----------------------------------------------------------------------
1396
1397 // Control font changer helper.
1398 void wxPropertyGrid::SetCurControlBoldFont()
1399 {
1400 wxASSERT( m_wndEditor );
1401 m_wndEditor->SetFont( m_captionFont );
1402 }
1403
1404 // -----------------------------------------------------------------------
1405
1406 wxPoint wxPropertyGrid::GetGoodEditorDialogPosition( wxPGProperty* p,
1407 const wxSize& sz )
1408 {
1409 #if wxPG_SMALL_SCREEN
1410 // On small-screen devices, always show dialogs with default position and size.
1411 return wxDefaultPosition;
1412 #else
1413 int splitterX = GetSplitterPosition();
1414 int x = splitterX;
1415 int y = p->GetY();
1416
1417 wxCHECK_MSG( y >= 0, wxPoint(-1,-1), wxT("invalid y?") );
1418
1419 ImprovedClientToScreen( &x, &y );
1420
1421 int sw = wxSystemSettings::GetMetric( ::wxSYS_SCREEN_X );
1422 int sh = wxSystemSettings::GetMetric( ::wxSYS_SCREEN_Y );
1423
1424 int new_x;
1425 int new_y;
1426
1427 if ( x > (sw/2) )
1428 // left
1429 new_x = x + (m_width-splitterX) - sz.x;
1430 else
1431 // right
1432 new_x = x;
1433
1434 if ( y > (sh/2) )
1435 // above
1436 new_y = y - sz.y;
1437 else
1438 // below
1439 new_y = y + m_lineHeight;
1440
1441 return wxPoint(new_x,new_y);
1442 #endif
1443 }
1444
1445 // -----------------------------------------------------------------------
1446
1447 wxString& wxPropertyGrid::ExpandEscapeSequences( wxString& dst_str, wxString& src_str )
1448 {
1449 if ( src_str.length() == 0 )
1450 {
1451 dst_str = src_str;
1452 return src_str;
1453 }
1454
1455 bool prev_is_slash = false;
1456
1457 wxString::const_iterator i = src_str.begin();
1458
1459 dst_str.clear();
1460
1461 for ( ; i != src_str.end(); ++i )
1462 {
1463 wxUniChar a = *i;
1464
1465 if ( a != wxS('\\') )
1466 {
1467 if ( !prev_is_slash )
1468 {
1469 dst_str << a;
1470 }
1471 else
1472 {
1473 if ( a == wxS('n') )
1474 {
1475 #ifdef __WXMSW__
1476 dst_str << wxS('\n');
1477 #else
1478 dst_str << wxS('\n');
1479 #endif
1480 }
1481 else if ( a == wxS('t') )
1482 dst_str << wxS('\t');
1483 else
1484 dst_str << a;
1485 }
1486 prev_is_slash = false;
1487 }
1488 else
1489 {
1490 if ( prev_is_slash )
1491 {
1492 dst_str << wxS('\\');
1493 prev_is_slash = false;
1494 }
1495 else
1496 {
1497 prev_is_slash = true;
1498 }
1499 }
1500 }
1501 return dst_str;
1502 }
1503
1504 // -----------------------------------------------------------------------
1505
1506 wxString& wxPropertyGrid::CreateEscapeSequences( wxString& dst_str, wxString& src_str )
1507 {
1508 if ( src_str.length() == 0 )
1509 {
1510 dst_str = src_str;
1511 return src_str;
1512 }
1513
1514 wxString::const_iterator i = src_str.begin();
1515 wxUniChar prev_a = wxS('\0');
1516
1517 dst_str.clear();
1518
1519 for ( ; i != src_str.end(); ++i )
1520 {
1521 wxChar a = *i;
1522
1523 if ( a >= wxS(' ') )
1524 {
1525 // This surely is not something that requires an escape sequence.
1526 dst_str << a;
1527 }
1528 else
1529 {
1530 // This might need...
1531 if ( a == wxS('\r') )
1532 {
1533 // DOS style line end.
1534 // Already taken care below
1535 }
1536 else if ( a == wxS('\n') )
1537 // UNIX style line end.
1538 dst_str << wxS("\\n");
1539 else if ( a == wxS('\t') )
1540 // Tab.
1541 dst_str << wxS('\t');
1542 else
1543 {
1544 //wxLogDebug(wxT("WARNING: Could not create escape sequence for character #%i"),(int)a);
1545 dst_str << a;
1546 }
1547 }
1548
1549 prev_a = a;
1550 }
1551 return dst_str;
1552 }
1553
1554 // -----------------------------------------------------------------------
1555
1556 wxPGProperty* wxPropertyGrid::DoGetItemAtY( int y ) const
1557 {
1558 // Outside?
1559 if ( y < 0 )
1560 return NULL;
1561
1562 unsigned int a = 0;
1563 return m_pState->m_properties->GetItemAtY(y, m_lineHeight, &a);
1564 }
1565
1566 // -----------------------------------------------------------------------
1567 // wxPropertyGrid graphics related methods
1568 // -----------------------------------------------------------------------
1569
1570 void wxPropertyGrid::OnPaint( wxPaintEvent& WXUNUSED(event) )
1571 {
1572 wxPaintDC dc(this);
1573
1574 // Update everything inside the box
1575 wxRect r = GetUpdateRegion().GetBox();
1576
1577 dc.SetPen(m_colEmptySpace);
1578 dc.SetBrush(m_colEmptySpace);
1579 dc.DrawRectangle(r);
1580 }
1581
1582 // -----------------------------------------------------------------------
1583
1584 void wxPropertyGrid::DrawExpanderButton( wxDC& dc, const wxRect& rect,
1585 wxPGProperty* property ) const
1586 {
1587 // Prepare rectangle to be used
1588 wxRect r(rect);
1589 r.x += m_gutterWidth; r.y += m_buttonSpacingY;
1590 r.width = m_iconWidth; r.height = m_iconHeight;
1591
1592 #if (wxPG_USE_RENDERER_NATIVE)
1593 //
1594 #elif wxPG_ICON_WIDTH
1595 // Drawing expand/collapse button manually
1596 dc.SetPen(m_colPropFore);
1597 if ( property->IsCategory() )
1598 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1599 else
1600 dc.SetBrush(m_colPropBack);
1601
1602 dc.DrawRectangle( r );
1603 int _y = r.y+(m_iconWidth/2);
1604 dc.DrawLine(r.x+2,_y,r.x+m_iconWidth-2,_y);
1605 #else
1606 wxBitmap* bmp;
1607 #endif
1608
1609 if ( property->IsExpanded() )
1610 {
1611 // wxRenderer functions are non-mutating in nature, so it
1612 // should be safe to cast "const wxPropertyGrid*" to "wxWindow*".
1613 // Hopefully this does not cause problems.
1614 #if (wxPG_USE_RENDERER_NATIVE)
1615 wxRendererNative::Get().DrawTreeItemButton(
1616 (wxWindow*)this,
1617 dc,
1618 r,
1619 wxCONTROL_EXPANDED
1620 );
1621 #elif wxPG_ICON_WIDTH
1622 //
1623 #else
1624 bmp = m_collbmp;
1625 #endif
1626
1627 }
1628 else
1629 {
1630 #if (wxPG_USE_RENDERER_NATIVE)
1631 wxRendererNative::Get().DrawTreeItemButton(
1632 (wxWindow*)this,
1633 dc,
1634 r,
1635 0
1636 );
1637 #elif wxPG_ICON_WIDTH
1638 int _x = r.x+(m_iconWidth/2);
1639 dc.DrawLine(_x,r.y+2,_x,r.y+m_iconWidth-2);
1640 #else
1641 bmp = m_expandbmp;
1642 #endif
1643 }
1644
1645 #if (wxPG_USE_RENDERER_NATIVE)
1646 //
1647 #elif wxPG_ICON_WIDTH
1648 //
1649 #else
1650 dc.DrawBitmap( *bmp, r.x, r.y, true );
1651 #endif
1652 }
1653
1654 // -----------------------------------------------------------------------
1655
1656 //
1657 // This is the one called by OnPaint event handler and others.
1658 // topy and bottomy are already unscrolled (ie. physical)
1659 //
1660 void wxPropertyGrid::DrawItems( wxDC& dc,
1661 unsigned int topy,
1662 unsigned int bottomy,
1663 const wxRect* clipRect )
1664 {
1665 if ( m_frozen || m_height < 1 || bottomy < topy || !m_pState ) return;
1666
1667 m_pState->EnsureVirtualHeight();
1668
1669 wxRect tempClipRect;
1670 if ( !clipRect )
1671 {
1672 tempClipRect = wxRect(0,topy,m_pState->m_width,bottomy);
1673 clipRect = &tempClipRect;
1674 }
1675
1676 // items added check
1677 if ( m_pState->m_itemsAdded ) PrepareAfterItemsAdded();
1678
1679 int paintFinishY = 0;
1680
1681 if ( m_pState->m_properties->GetChildCount() > 0 )
1682 {
1683 wxDC* dcPtr = &dc;
1684 bool isBuffered = false;
1685
1686 #if wxPG_DOUBLE_BUFFER
1687 wxMemoryDC* bufferDC = NULL;
1688
1689 if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
1690 {
1691 if ( !m_doubleBuffer )
1692 {
1693 paintFinishY = clipRect->y;
1694 dcPtr = NULL;
1695 }
1696 else
1697 {
1698 bufferDC = new wxMemoryDC();
1699
1700 // If nothing was changed, then just copy from double-buffer
1701 bufferDC->SelectObject( *m_doubleBuffer );
1702 dcPtr = bufferDC;
1703
1704 isBuffered = true;
1705 }
1706 }
1707 #endif
1708
1709 if ( dcPtr )
1710 {
1711 dc.SetClippingRegion( *clipRect );
1712 paintFinishY = DoDrawItems( *dcPtr, clipRect, isBuffered );
1713 }
1714
1715 #if wxPG_DOUBLE_BUFFER
1716 if ( bufferDC )
1717 {
1718 dc.Blit( clipRect->x, clipRect->y, clipRect->width, clipRect->height,
1719 bufferDC, 0, 0, wxCOPY );
1720 dc.DestroyClippingRegion(); // Is this really necessary?
1721 delete bufferDC;
1722 }
1723 #endif
1724 }
1725
1726 // Clear area beyond bottomY?
1727 if ( paintFinishY < (clipRect->y+clipRect->height) )
1728 {
1729 dc.SetPen(m_colEmptySpace);
1730 dc.SetBrush(m_colEmptySpace);
1731 dc.DrawRectangle( 0, paintFinishY, m_width, (clipRect->y+clipRect->height) );
1732 }
1733 }
1734
1735 // -----------------------------------------------------------------------
1736
1737 int wxPropertyGrid::DoDrawItems( wxDC& dc,
1738 const wxRect* clipRect,
1739 bool isBuffered ) const
1740 {
1741 const wxPGProperty* firstItem;
1742 const wxPGProperty* lastItem;
1743
1744 firstItem = DoGetItemAtY(clipRect->y);
1745 lastItem = DoGetItemAtY(clipRect->y+clipRect->height-1);
1746
1747 if ( !lastItem )
1748 lastItem = GetLastItem( wxPG_ITERATE_VISIBLE );
1749
1750 if ( m_frozen || m_height < 1 || firstItem == NULL )
1751 return clipRect->y;
1752
1753 wxCHECK_MSG( !m_pState->m_itemsAdded, clipRect->y, wxT("no items added") );
1754 wxASSERT( m_pState->m_properties->GetChildCount() );
1755
1756 int lh = m_lineHeight;
1757
1758 int firstItemTopY;
1759 int lastItemBottomY;
1760
1761 firstItemTopY = clipRect->y;
1762 lastItemBottomY = clipRect->y + clipRect->height;
1763
1764 // Align y coordinates to item boundaries
1765 firstItemTopY -= firstItemTopY % lh;
1766 lastItemBottomY += lh - (lastItemBottomY % lh);
1767 lastItemBottomY -= 1;
1768
1769 // Entire range outside scrolled, visible area?
1770 if ( firstItemTopY >= (int)m_pState->GetVirtualHeight() || lastItemBottomY <= 0 )
1771 return clipRect->y;
1772
1773 wxCHECK_MSG( firstItemTopY < lastItemBottomY, clipRect->y, wxT("invalid y values") );
1774
1775
1776 /*
1777 wxLogDebug(wxT(" -> DoDrawItems ( \"%s\" -> \"%s\", height=%i (ch=%i), clipRect = 0x%lX )"),
1778 firstItem->GetLabel().c_str(),
1779 lastItem->GetLabel().c_str(),
1780 (int)(lastItemBottomY - firstItemTopY),
1781 (int)m_height,
1782 (unsigned long)clipRect );
1783 */
1784
1785 wxRect r;
1786
1787 long windowStyle = m_windowStyle;
1788
1789 int xRelMod = 0;
1790 int yRelMod = 0;
1791
1792 //
1793 // With wxPG_DOUBLE_BUFFER, do double buffering
1794 // - buffer's y = 0, so align cliprect and coordinates to that
1795 //
1796 #if wxPG_DOUBLE_BUFFER
1797
1798 wxRect cr2;
1799
1800 if ( isBuffered )
1801 {
1802 xRelMod = clipRect->x;
1803 yRelMod = clipRect->y;
1804
1805 //
1806 // clipRect conversion
1807 cr2 = *clipRect;
1808 cr2.x -= xRelMod;
1809 cr2.y -= yRelMod;
1810 clipRect = &cr2;
1811 firstItemTopY -= yRelMod;
1812 lastItemBottomY -= yRelMod;
1813 }
1814 #else
1815 wxUnusedVar(isBuffered);
1816 #endif
1817
1818 int x = m_marginWidth - xRelMod;
1819
1820 wxFont normalFont = GetFont();
1821
1822 bool reallyFocused = (m_iFlags & wxPG_FL_FOCUSED) != 0;
1823
1824 bool isPgEnabled = IsEnabled();
1825
1826 //
1827 // Prepare some pens and brushes that are often changed to.
1828 //
1829
1830 wxBrush marginBrush(m_colMargin);
1831 wxPen marginPen(m_colMargin);
1832 wxBrush capbgbrush(m_colCapBack,wxSOLID);
1833 wxPen linepen(m_colLine,1,wxSOLID);
1834
1835 wxColour selBackCol;
1836 if ( isPgEnabled )
1837 selBackCol = m_colSelBack;
1838 else
1839 selBackCol = m_colMargin;
1840
1841 // pen that has same colour as text
1842 wxPen outlinepen(m_colPropFore,1,wxSOLID);
1843
1844 //
1845 // Clear margin with background colour
1846 //
1847 dc.SetBrush( marginBrush );
1848 if ( !(windowStyle & wxPG_HIDE_MARGIN) )
1849 {
1850 dc.SetPen( *wxTRANSPARENT_PEN );
1851 dc.DrawRectangle(-1-xRelMod,firstItemTopY-1,x+2,lastItemBottomY-firstItemTopY+2);
1852 }
1853
1854 const wxPGProperty* firstSelected = GetSelection();
1855 const wxPropertyGridPageState* state = m_pState;
1856
1857 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
1858 bool wasSelectedPainted = false;
1859 #endif
1860
1861 // TODO: Only render columns that are within clipping region.
1862
1863 dc.SetFont(normalFont);
1864
1865 wxPropertyGridConstIterator it( state, wxPG_ITERATE_VISIBLE, firstItem );
1866 int endScanBottomY = lastItemBottomY + lh;
1867 int y = firstItemTopY;
1868
1869 //
1870 // Pregenerate list of visible properties.
1871 wxArrayPGProperty visPropArray;
1872 visPropArray.reserve((m_height/m_lineHeight)+6);
1873
1874 for ( ; !it.AtEnd(); it.Next() )
1875 {
1876 const wxPGProperty* p = *it;
1877
1878 if ( !p->HasFlag(wxPG_PROP_HIDDEN) )
1879 {
1880 visPropArray.push_back((wxPGProperty*)p);
1881
1882 if ( y > endScanBottomY )
1883 break;
1884
1885 y += lh;
1886 }
1887 }
1888
1889 visPropArray.push_back(NULL);
1890
1891 wxPGProperty* nextP = visPropArray[0];
1892
1893 int gridWidth = state->m_width;
1894
1895 y = firstItemTopY;
1896 for ( unsigned int arrInd=1;
1897 nextP && y <= lastItemBottomY;
1898 arrInd++ )
1899 {
1900 wxPGProperty* p = nextP;
1901 nextP = visPropArray[arrInd];
1902
1903 int rowHeight = m_fontHeight+(m_spacingy*2)+1;
1904 int textMarginHere = x;
1905 int renderFlags = 0;
1906
1907 int greyDepth = m_marginWidth;
1908 if ( !(windowStyle & wxPG_HIDE_CATEGORIES) )
1909 greyDepth = (((int)p->m_depthBgCol)-1) * m_subgroup_extramargin + m_marginWidth;
1910
1911 int greyDepthX = greyDepth - xRelMod;
1912
1913 // Use basic depth if in non-categoric mode and parent is base array.
1914 if ( !(windowStyle & wxPG_HIDE_CATEGORIES) || p->GetParent() != m_pState->m_properties )
1915 {
1916 textMarginHere += ((unsigned int)((p->m_depth-1)*m_subgroup_extramargin));
1917 }
1918
1919 // Paint margin area
1920 dc.SetBrush(marginBrush);
1921 dc.SetPen(marginPen);
1922 dc.DrawRectangle( -xRelMod, y, greyDepth, lh );
1923
1924 dc.SetPen( linepen );
1925
1926 int y2 = y + lh;
1927
1928 // Margin Edge
1929 dc.DrawLine( greyDepthX, y, greyDepthX, y2 );
1930
1931 // Splitters
1932 unsigned int si;
1933 int sx = x;
1934
1935 for ( si=0; si<state->m_colWidths.size(); si++ )
1936 {
1937 sx += state->m_colWidths[si];
1938 dc.DrawLine( sx, y, sx, y2 );
1939 }
1940
1941 // Horizontal Line, below
1942 // (not if both this and next is category caption)
1943 if ( p->IsCategory() &&
1944 nextP && nextP->IsCategory() )
1945 dc.SetPen(m_colCapBack);
1946
1947 dc.DrawLine( greyDepthX, y2-1, gridWidth-xRelMod, y2-1 );
1948
1949 //
1950 // Need to override row colours?
1951 wxColour rowFgCol;
1952 wxColour rowBgCol;
1953
1954 bool isSelected = state->DoIsPropertySelected(p);
1955
1956 if ( !isSelected )
1957 {
1958 // Disabled may get different colour.
1959 if ( !p->IsEnabled() )
1960 {
1961 renderFlags |= wxPGCellRenderer::Disabled |
1962 wxPGCellRenderer::DontUseCellFgCol;
1963 rowFgCol = m_colDisPropFore;
1964 }
1965 }
1966 else
1967 {
1968 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
1969 if ( p == firstSelected )
1970 wasSelectedPainted = true;
1971 #endif
1972
1973 renderFlags |= wxPGCellRenderer::Selected;
1974
1975 if ( !p->IsCategory() )
1976 {
1977 renderFlags |= wxPGCellRenderer::DontUseCellFgCol |
1978 wxPGCellRenderer::DontUseCellBgCol;
1979
1980 if ( reallyFocused && p == firstSelected )
1981 {
1982 rowFgCol = m_colSelFore;
1983 rowBgCol = selBackCol;
1984 }
1985 else if ( isPgEnabled )
1986 {
1987 rowFgCol = m_colPropFore;
1988 if ( p == firstSelected )
1989 rowBgCol = m_colMargin;
1990 else
1991 rowBgCol = selBackCol;
1992 }
1993 else
1994 {
1995 rowFgCol = m_colDisPropFore;
1996 rowBgCol = selBackCol;
1997 }
1998 }
1999 }
2000
2001 wxBrush rowBgBrush;
2002
2003 if ( rowBgCol.IsOk() )
2004 rowBgBrush = wxBrush(rowBgCol);
2005
2006 if ( HasInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL) )
2007 renderFlags = renderFlags & ~wxPGCellRenderer::DontUseCellColours;
2008
2009 //
2010 // Fill additional margin area with background colour of first cell
2011 if ( greyDepthX < textMarginHere )
2012 {
2013 if ( !(renderFlags & wxPGCellRenderer::DontUseCellBgCol) )
2014 {
2015 wxPGCell& cell = p->GetCell(0);
2016 rowBgCol = cell.GetBgCol();
2017 rowBgBrush = wxBrush(rowBgCol);
2018 }
2019 dc.SetBrush(rowBgBrush);
2020 dc.SetPen(rowBgCol);
2021 dc.DrawRectangle(greyDepthX+1, y,
2022 textMarginHere-greyDepthX, lh-1);
2023 }
2024
2025 bool fontChanged = false;
2026
2027 // Expander button rectangle
2028 wxRect butRect( ((p->m_depth - 1) * m_subgroup_extramargin) - xRelMod,
2029 y,
2030 m_marginWidth,
2031 lh );
2032
2033 if ( p->IsCategory() )
2034 {
2035 // Captions have their cell areas merged as one
2036 dc.SetFont(m_captionFont);
2037 fontChanged = true;
2038 wxRect cellRect(greyDepthX, y, gridWidth - greyDepth + 2, rowHeight-1 );
2039
2040 if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol )
2041 {
2042 dc.SetBrush(rowBgBrush);
2043 dc.SetPen(rowBgCol);
2044 }
2045
2046 if ( renderFlags & wxPGCellRenderer::DontUseCellFgCol )
2047 {
2048 dc.SetTextForeground(rowFgCol);
2049 }
2050
2051 wxPGCellRenderer* renderer = p->GetCellRenderer(0);
2052 renderer->Render( dc, cellRect, this, p, 0, -1, renderFlags );
2053
2054 // Tree Item Button
2055 if ( !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() )
2056 DrawExpanderButton( dc, butRect, p );
2057 }
2058 else
2059 {
2060 if ( p->m_flags & wxPG_PROP_MODIFIED && (windowStyle & wxPG_BOLD_MODIFIED) )
2061 {
2062 dc.SetFont(m_captionFont);
2063 fontChanged = true;
2064 }
2065
2066 unsigned int ci;
2067 int cellX = x + 1;
2068 int nextCellWidth = state->m_colWidths[0] -
2069 (greyDepthX - m_marginWidth);
2070 wxRect cellRect(greyDepthX+1, y, 0, rowHeight-1);
2071 int textXAdd = textMarginHere - greyDepthX;
2072
2073 for ( ci=0; ci<state->m_colWidths.size(); ci++ )
2074 {
2075 cellRect.width = nextCellWidth - 1;
2076
2077 bool ctrlCell = false;
2078 int cellRenderFlags = renderFlags;
2079
2080 // Tree Item Button
2081 if ( ci == 0 && !HasFlag(wxPG_HIDE_MARGIN) && p->HasVisibleChildren() )
2082 DrawExpanderButton( dc, butRect, p );
2083
2084 // Background
2085 if ( isSelected && ci == 1 )
2086 {
2087 if ( p == firstSelected && m_wndEditor )
2088 {
2089 wxColour editorBgCol =
2090 GetEditorControl()->GetBackgroundColour();
2091 dc.SetBrush(editorBgCol);
2092 dc.SetPen(editorBgCol);
2093 dc.SetTextForeground(m_colPropFore);
2094 dc.DrawRectangle(cellRect);
2095
2096 if ( m_dragStatus == 0 &&
2097 !(m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE) )
2098 ctrlCell = true;
2099 }
2100 else
2101 {
2102 dc.SetBrush(m_colPropBack);
2103 dc.SetPen(m_colPropBack);
2104 dc.SetTextForeground(m_colDisPropFore);
2105 if ( p->IsEnabled() )
2106 dc.SetTextForeground(rowFgCol);
2107 else
2108 dc.SetTextForeground(m_colDisPropFore);
2109 }
2110 }
2111 else
2112 {
2113 if ( renderFlags & wxPGCellRenderer::DontUseCellBgCol )
2114 {
2115 dc.SetBrush(rowBgBrush);
2116 dc.SetPen(rowBgCol);
2117 }
2118
2119 if ( renderFlags & wxPGCellRenderer::DontUseCellFgCol )
2120 {
2121 dc.SetTextForeground(rowFgCol);
2122 }
2123 }
2124
2125 dc.SetClippingRegion(cellRect);
2126
2127 cellRect.x += textXAdd;
2128 cellRect.width -= textXAdd;
2129
2130 // Foreground
2131 if ( !ctrlCell )
2132 {
2133 wxPGCellRenderer* renderer;
2134 int cmnVal = p->GetCommonValue();
2135 if ( cmnVal == -1 || ci != 1 )
2136 {
2137 renderer = p->GetCellRenderer(ci);
2138 renderer->Render( dc, cellRect, this, p, ci, -1,
2139 cellRenderFlags );
2140 }
2141 else
2142 {
2143 renderer = GetCommonValue(cmnVal)->GetRenderer();
2144 renderer->Render( dc, cellRect, this, p, ci, -1,
2145 cellRenderFlags );
2146 }
2147 }
2148
2149 cellX += state->m_colWidths[ci];
2150 if ( ci < (state->m_colWidths.size()-1) )
2151 nextCellWidth = state->m_colWidths[ci+1];
2152 cellRect.x = cellX;
2153 dc.DestroyClippingRegion(); // Is this really necessary?
2154 textXAdd = 0;
2155 }
2156 }
2157
2158 if ( fontChanged )
2159 dc.SetFont(normalFont);
2160
2161 y += rowHeight;
2162 }
2163
2164 // Refresh editor controls (seems not needed on msw)
2165 // NOTE: This code is mandatory for GTK!
2166 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
2167 if ( wasSelectedPainted )
2168 {
2169 if ( m_wndEditor )
2170 m_wndEditor->Refresh();
2171 if ( m_wndEditor2 )
2172 m_wndEditor2->Refresh();
2173 }
2174 #endif
2175
2176 return y + yRelMod;
2177 }
2178
2179 // -----------------------------------------------------------------------
2180
2181 wxRect wxPropertyGrid::GetPropertyRect( const wxPGProperty* p1, const wxPGProperty* p2 ) const
2182 {
2183 wxRect r;
2184
2185 if ( m_width < 10 || m_height < 10 ||
2186 !m_pState->m_properties->GetChildCount() ||
2187 p1 == NULL )
2188 return wxRect(0,0,0,0);
2189
2190 int vy = 0;
2191
2192 //
2193 // Return rect which encloses the given property range
2194
2195 int visTop = p1->GetY();
2196 int visBottom;
2197 if ( p2 )
2198 visBottom = p2->GetY() + m_lineHeight;
2199 else
2200 visBottom = m_height + visTop;
2201
2202 // If seleced property is inside the range, we'll extend the range to include
2203 // control's size.
2204 wxPGProperty* selected = GetSelection();
2205 if ( selected )
2206 {
2207 int selectedY = selected->GetY();
2208 if ( selectedY >= visTop && selectedY < visBottom )
2209 {
2210 wxWindow* editor = GetEditorControl();
2211 if ( editor )
2212 {
2213 int visBottom2 = selectedY + editor->GetSize().y;
2214 if ( visBottom2 > visBottom )
2215 visBottom = visBottom2;
2216 }
2217 }
2218 }
2219
2220 return wxRect(0,visTop-vy,m_pState->m_width,visBottom-visTop);
2221 }
2222
2223 // -----------------------------------------------------------------------
2224
2225 void wxPropertyGrid::DrawItems( const wxPGProperty* p1, const wxPGProperty* p2 )
2226 {
2227 if ( m_frozen )
2228 return;
2229
2230 if ( m_pState->m_itemsAdded )
2231 PrepareAfterItemsAdded();
2232
2233 wxRect r = GetPropertyRect(p1, p2);
2234 if ( r.width > 0 )
2235 {
2236 m_canvas->RefreshRect(r);
2237 }
2238 }
2239
2240 // -----------------------------------------------------------------------
2241
2242 void wxPropertyGrid::RefreshProperty( wxPGProperty* p )
2243 {
2244 if ( m_pState->DoIsPropertySelected(p) )
2245 {
2246 // NB: We must copy the selection.
2247 wxArrayPGProperty selection = m_pState->m_selection;
2248 DoSetSelection(selection, wxPG_SEL_FORCE);
2249 }
2250
2251 DrawItemAndChildren(p);
2252 }
2253
2254 // -----------------------------------------------------------------------
2255
2256 void wxPropertyGrid::DrawItemAndValueRelated( wxPGProperty* p )
2257 {
2258 if ( m_frozen )
2259 return;
2260
2261 // Draw item, children, and parent too, if it is not category
2262 wxPGProperty* parent = p->GetParent();
2263
2264 while ( parent &&
2265 !parent->IsCategory() &&
2266 parent->GetParent() )
2267 {
2268 DrawItem(parent);
2269 parent = parent->GetParent();
2270 }
2271
2272 DrawItemAndChildren(p);
2273 }
2274
2275 void wxPropertyGrid::DrawItemAndChildren( wxPGProperty* p )
2276 {
2277 wxCHECK_RET( p, wxT("invalid property id") );
2278
2279 // Do not draw if in non-visible page
2280 if ( p->GetParentState() != m_pState )
2281 return;
2282
2283 // do not draw a single item if multiple pending
2284 if ( m_pState->m_itemsAdded || m_frozen )
2285 return;
2286
2287 // Update child control.
2288 wxPGProperty* selected = GetSelection();
2289 if ( selected && selected->GetParent() == p )
2290 RefreshEditor();
2291
2292 const wxPGProperty* lastDrawn = p->GetLastVisibleSubItem();
2293
2294 DrawItems(p, lastDrawn);
2295 }
2296
2297 // -----------------------------------------------------------------------
2298
2299 void wxPropertyGrid::Refresh( bool WXUNUSED(eraseBackground),
2300 const wxRect *rect )
2301 {
2302 PrepareAfterItemsAdded();
2303
2304 wxWindow::Refresh(false);
2305 if ( m_canvas )
2306 // TODO: Coordinate translation
2307 m_canvas->Refresh(false, rect);
2308
2309 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
2310 // I think this really helps only GTK+1.2
2311 if ( m_wndEditor ) m_wndEditor->Refresh();
2312 if ( m_wndEditor2 ) m_wndEditor2->Refresh();
2313 #endif
2314 }
2315
2316 // -----------------------------------------------------------------------
2317 // wxPropertyGrid global operations
2318 // -----------------------------------------------------------------------
2319
2320 void wxPropertyGrid::Clear()
2321 {
2322 m_pState->DoClear();
2323
2324 m_propHover = NULL;
2325
2326 m_prevVY = 0;
2327
2328 RecalculateVirtualSize();
2329
2330 // Need to clear some area at the end
2331 if ( !m_frozen )
2332 RefreshRect(wxRect(0, 0, m_width, m_height));
2333 }
2334
2335 // -----------------------------------------------------------------------
2336
2337 bool wxPropertyGrid::EnableCategories( bool enable )
2338 {
2339 DoClearSelection();
2340
2341 if ( enable )
2342 {
2343 //
2344 // Enable categories
2345 //
2346
2347 m_windowStyle &= ~(wxPG_HIDE_CATEGORIES);
2348 }
2349 else
2350 {
2351 //
2352 // Disable categories
2353 //
2354 m_windowStyle |= wxPG_HIDE_CATEGORIES;
2355 }
2356
2357 if ( !m_pState->EnableCategories(enable) )
2358 return false;
2359
2360 if ( !m_frozen )
2361 {
2362 if ( m_windowStyle & wxPG_AUTO_SORT )
2363 {
2364 m_pState->m_itemsAdded = 1; // force
2365 PrepareAfterItemsAdded();
2366 }
2367 }
2368 else
2369 m_pState->m_itemsAdded = 1;
2370
2371 // No need for RecalculateVirtualSize() here - it is already called in
2372 // wxPropertyGridPageState method above.
2373
2374 Refresh();
2375
2376 return true;
2377 }
2378
2379 // -----------------------------------------------------------------------
2380
2381 void wxPropertyGrid::SwitchState( wxPropertyGridPageState* pNewState )
2382 {
2383 wxASSERT( pNewState );
2384 wxASSERT( pNewState->GetGrid() );
2385
2386 if ( pNewState == m_pState )
2387 return;
2388
2389 wxArrayPGProperty oldSelection = m_pState->m_selection;
2390
2391 // Call ClearSelection() instead of DoClearSelection()
2392 // so that selection clear events are not sent.
2393 ClearSelection();
2394
2395 m_pState->m_selection = oldSelection;
2396
2397 bool orig_mode = m_pState->IsInNonCatMode();
2398 bool new_state_mode = pNewState->IsInNonCatMode();
2399
2400 m_pState = pNewState;
2401
2402 // Validate width
2403 int pgWidth = GetClientSize().x;
2404 if ( HasVirtualWidth() )
2405 {
2406 int minWidth = pgWidth;
2407 if ( pNewState->m_width < minWidth )
2408 {
2409 pNewState->m_width = minWidth;
2410 pNewState->CheckColumnWidths();
2411 }
2412 }
2413 else
2414 {
2415 //
2416 // Just in case, fully re-center splitter
2417 if ( HasFlag( wxPG_SPLITTER_AUTO_CENTER ) )
2418 pNewState->m_fSplitterX = -1.0;
2419
2420 pNewState->OnClientWidthChange( pgWidth, pgWidth - pNewState->m_width );
2421 }
2422
2423 m_propHover = NULL;
2424
2425 // If necessary, convert state to correct mode.
2426 if ( orig_mode != new_state_mode )
2427 {
2428 // This should refresh as well.
2429 EnableCategories( orig_mode?false:true );
2430 }
2431 else if ( !m_frozen )
2432 {
2433 // Refresh, if not frozen.
2434 m_pState->PrepareAfterItemsAdded();
2435
2436 // Reselect (Use SetSelection() instead of Do-variant so that
2437 // events won't be sent).
2438 SetSelection(m_pState->m_selection);
2439
2440 RecalculateVirtualSize(0);
2441 Refresh();
2442 }
2443 else
2444 m_pState->m_itemsAdded = 1;
2445 }
2446
2447 // -----------------------------------------------------------------------
2448
2449 // Call to SetSplitterPosition will always disable splitter auto-centering
2450 // if parent window is shown.
2451 void wxPropertyGrid::DoSetSplitterPosition_( int newxpos, bool refresh, int splitterIndex, bool allPages )
2452 {
2453 if ( ( newxpos < wxPG_DRAG_MARGIN ) )
2454 return;
2455
2456 wxPropertyGridPageState* state = m_pState;
2457
2458 state->DoSetSplitterPosition( newxpos, splitterIndex, allPages );
2459
2460 if ( refresh )
2461 {
2462 if ( GetSelection() )
2463 CorrectEditorWidgetSizeX();
2464
2465 Refresh();
2466 }
2467 }
2468
2469 // -----------------------------------------------------------------------
2470
2471 void wxPropertyGrid::CenterSplitter( bool enableAutoCentering )
2472 {
2473 SetSplitterPosition( m_width/2, true );
2474 if ( enableAutoCentering && ( m_windowStyle & wxPG_SPLITTER_AUTO_CENTER ) )
2475 m_iFlags &= ~(wxPG_FL_DONT_CENTER_SPLITTER);
2476 }
2477
2478 // -----------------------------------------------------------------------
2479 // wxPropertyGrid item iteration (GetNextProperty etc.) methods
2480 // -----------------------------------------------------------------------
2481
2482 // Returns nearest paint visible property (such that will be painted unless
2483 // window is scrolled or resized). If given property is paint visible, then
2484 // it itself will be returned
2485 wxPGProperty* wxPropertyGrid::GetNearestPaintVisible( wxPGProperty* p ) const
2486 {
2487 int vx,vy1;// Top left corner of client
2488 GetViewStart(&vx,&vy1);
2489 vy1 *= wxPG_PIXELS_PER_UNIT;
2490
2491 int vy2 = vy1 + m_height;
2492 int propY = p->GetY2(m_lineHeight);
2493
2494 if ( (propY + m_lineHeight) < vy1 )
2495 {
2496 // Too high
2497 return DoGetItemAtY( vy1 );
2498 }
2499 else if ( propY > vy2 )
2500 {
2501 // Too low
2502 return DoGetItemAtY( vy2 );
2503 }
2504
2505 // Itself paint visible
2506 return p;
2507
2508 }
2509
2510 // -----------------------------------------------------------------------
2511 // Methods related to change in value, value modification and sending events
2512 // -----------------------------------------------------------------------
2513
2514 // commits any changes in editor of selected property
2515 // return true if validation did not fail
2516 // flags are same as with DoSelectProperty
2517 bool wxPropertyGrid::CommitChangesFromEditor( wxUint32 flags )
2518 {
2519 // Committing already?
2520 if ( m_inCommitChangesFromEditor )
2521 return true;
2522
2523 // Don't do this if already processing editor event. It might
2524 // induce recursive dialogs and crap like that.
2525 if ( m_iFlags & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT )
2526 {
2527 if ( m_inDoPropertyChanged )
2528 return true;
2529
2530 return false;
2531 }
2532
2533 wxPGProperty* selected = GetSelection();
2534
2535 if ( m_wndEditor &&
2536 IsEditorsValueModified() &&
2537 (m_iFlags & wxPG_FL_INITIALIZED) &&
2538 selected )
2539 {
2540 m_inCommitChangesFromEditor = 1;
2541
2542 wxVariant variant(selected->GetValueRef());
2543 bool valueIsPending = false;
2544
2545 // JACS - necessary to avoid new focus being found spuriously within OnIdle
2546 // due to another window getting focus
2547 wxWindow* oldFocus = m_curFocused;
2548
2549 bool validationFailure = false;
2550 bool forceSuccess = (flags & (wxPG_SEL_NOVALIDATE|wxPG_SEL_FORCE)) ? true : false;
2551
2552 m_chgInfo_changedProperty = NULL;
2553
2554 // If truly modified, schedule value as pending.
2555 if ( selected->GetEditorClass()->
2556 GetValueFromControl( variant,
2557 selected,
2558 GetEditorControl() ) )
2559 {
2560 if ( DoEditorValidate() &&
2561 PerformValidation(selected, variant) )
2562 {
2563 valueIsPending = true;
2564 }
2565 else
2566 {
2567 validationFailure = true;
2568 }
2569 }
2570 else
2571 {
2572 EditorsValueWasNotModified();
2573 }
2574
2575 bool res = true;
2576
2577 m_inCommitChangesFromEditor = 0;
2578
2579 if ( validationFailure && !forceSuccess )
2580 {
2581 if (oldFocus)
2582 {
2583 oldFocus->SetFocus();
2584 m_curFocused = oldFocus;
2585 }
2586
2587 res = OnValidationFailure(selected, variant);
2588
2589 // Now prevent further validation failure messages
2590 if ( res )
2591 {
2592 EditorsValueWasNotModified();
2593 OnValidationFailureReset(selected);
2594 }
2595 }
2596 else if ( valueIsPending )
2597 {
2598 DoPropertyChanged( selected, flags );
2599 EditorsValueWasNotModified();
2600 }
2601
2602 return res;
2603 }
2604
2605 return true;
2606 }
2607
2608 // -----------------------------------------------------------------------
2609
2610 bool wxPropertyGrid::PerformValidation( wxPGProperty* p, wxVariant& pendingValue,
2611 int flags )
2612 {
2613 //
2614 // Runs all validation functionality.
2615 // Returns true if value passes all tests.
2616 //
2617
2618 m_validationInfo.m_failureBehavior = m_permanentValidationFailureBehavior;
2619
2620 if ( pendingValue.GetType() == wxPG_VARIANT_TYPE_LIST )
2621 {
2622 if ( !p->ValidateValue(pendingValue, m_validationInfo) )
2623 return false;
2624 }
2625
2626 //
2627 // Adapt list to child values, if necessary
2628 wxVariant listValue = pendingValue;
2629 wxVariant* pPendingValue = &pendingValue;
2630 wxVariant* pList = NULL;
2631
2632 // If parent has wxPG_PROP_AGGREGATE flag, or uses composite
2633 // string value, then we need treat as it was changed instead
2634 // (or, in addition, as is the case with composite string parent).
2635 // This includes creating list variant for child values.
2636
2637 wxPGProperty* pwc = p->GetParent();
2638 wxPGProperty* changedProperty = p;
2639 wxPGProperty* baseChangedProperty = changedProperty;
2640 wxVariant bcpPendingList;
2641
2642 listValue = pendingValue;
2643 listValue.SetName(p->GetBaseName());
2644
2645 while ( pwc &&
2646 (pwc->HasFlag(wxPG_PROP_AGGREGATE) || pwc->HasFlag(wxPG_PROP_COMPOSED_VALUE)) )
2647 {
2648 wxVariantList tempList;
2649 wxVariant lv(tempList, pwc->GetBaseName());
2650 lv.Append(listValue);
2651 listValue = lv;
2652 pPendingValue = &listValue;
2653
2654 if ( pwc->HasFlag(wxPG_PROP_AGGREGATE) )
2655 {
2656 baseChangedProperty = pwc;
2657 bcpPendingList = lv;
2658 }
2659
2660 changedProperty = pwc;
2661 pwc = pwc->GetParent();
2662 }
2663
2664 wxVariant value;
2665 wxPGProperty* evtChangingProperty = changedProperty;
2666
2667 if ( pPendingValue->GetType() != wxPG_VARIANT_TYPE_LIST )
2668 {
2669 value = *pPendingValue;
2670 }
2671 else
2672 {
2673 // Convert list to child values
2674 pList = pPendingValue;
2675 changedProperty->AdaptListToValue( *pPendingValue, &value );
2676 }
2677
2678 wxVariant evtChangingValue = value;
2679
2680 if ( flags & SendEvtChanging )
2681 {
2682 // FIXME: After proper ValueToString()s added, remove
2683 // this. It is just a temporary fix, as evt_changing
2684 // will simply not work for wxPG_PROP_COMPOSED_VALUE
2685 // (unless it is selected, and textctrl editor is open).
2686 if ( changedProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) )
2687 {
2688 evtChangingProperty = baseChangedProperty;
2689 if ( evtChangingProperty != p )
2690 {
2691 evtChangingProperty->AdaptListToValue( bcpPendingList, &evtChangingValue );
2692 }
2693 else
2694 {
2695 evtChangingValue = pendingValue;
2696 }
2697 }
2698
2699 if ( evtChangingProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) )
2700 {
2701 if ( changedProperty == GetSelection() )
2702 {
2703 wxWindow* editor = GetEditorControl();
2704 wxASSERT( editor->IsKindOf(CLASSINFO(wxTextCtrl)) );
2705 evtChangingValue = wxStaticCast(editor, wxTextCtrl)->GetValue();
2706 }
2707 else
2708 {
2709 wxLogDebug(wxT("WARNING: wxEVT_PG_CHANGING is about to happen with old value."));
2710 }
2711 }
2712 }
2713
2714 wxASSERT( m_chgInfo_changedProperty == NULL );
2715 m_chgInfo_changedProperty = changedProperty;
2716 m_chgInfo_baseChangedProperty = baseChangedProperty;
2717 m_chgInfo_pendingValue = value;
2718
2719 if ( pList )
2720 m_chgInfo_valueList = *pList;
2721 else
2722 m_chgInfo_valueList.MakeNull();
2723
2724 // If changedProperty is not property which value was edited,
2725 // then call wxPGProperty::ValidateValue() for that as well.
2726 if ( p != changedProperty && value.GetType() != wxPG_VARIANT_TYPE_LIST )
2727 {
2728 if ( !changedProperty->ValidateValue(value, m_validationInfo) )
2729 return false;
2730 }
2731
2732 if ( flags & SendEvtChanging )
2733 {
2734 // SendEvent returns true if event was vetoed
2735 if ( SendEvent( wxEVT_PG_CHANGING, evtChangingProperty, &evtChangingValue, 0 ) )
2736 return false;
2737 }
2738
2739 if ( flags & IsStandaloneValidation )
2740 {
2741 // If called in 'generic' context, we need to reset
2742 // m_chgInfo_changedProperty and write back translated value.
2743 m_chgInfo_changedProperty = NULL;
2744 pendingValue = value;
2745 }
2746
2747 return true;
2748 }
2749
2750 // -----------------------------------------------------------------------
2751
2752 void wxPropertyGrid::DoShowPropertyError( wxPGProperty* WXUNUSED(property), const wxString& msg )
2753 {
2754 if ( !msg.length() )
2755 return;
2756
2757 #if wxUSE_STATUSBAR
2758 if ( !wxPGGlobalVars->m_offline )
2759 {
2760 wxWindow* topWnd = ::wxGetTopLevelParent(this);
2761 if ( topWnd )
2762 {
2763 wxFrame* pFrame = wxDynamicCast(topWnd, wxFrame);
2764 if ( pFrame )
2765 {
2766 wxStatusBar* pStatusBar = pFrame->GetStatusBar();
2767 if ( pStatusBar )
2768 {
2769 pStatusBar->SetStatusText(msg);
2770 return;
2771 }
2772 }
2773 }
2774 }
2775 #endif
2776
2777 ::wxMessageBox(msg, wxT("Property Error"));
2778 }
2779
2780 // -----------------------------------------------------------------------
2781
2782 bool wxPropertyGrid::OnValidationFailure( wxPGProperty* property,
2783 wxVariant& invalidValue )
2784 {
2785 wxWindow* editor = GetEditorControl();
2786
2787 // First call property's handler
2788 property->OnValidationFailure(invalidValue);
2789
2790 bool res = DoOnValidationFailure(property, invalidValue);
2791
2792 //
2793 // For non-wxTextCtrl editors, we do need to revert the value
2794 if ( !editor->IsKindOf(CLASSINFO(wxTextCtrl)) &&
2795 property == GetSelection() )
2796 {
2797 property->GetEditorClass()->UpdateControl(property, editor);
2798 }
2799
2800 property->SetFlag(wxPG_PROP_INVALID_VALUE);
2801
2802 return res;
2803 }
2804
2805 bool wxPropertyGrid::DoOnValidationFailure( wxPGProperty* property, wxVariant& WXUNUSED(invalidValue) )
2806 {
2807 int vfb = m_validationInfo.m_failureBehavior;
2808
2809 if ( vfb & wxPG_VFB_BEEP )
2810 ::wxBell();
2811
2812 if ( (vfb & wxPG_VFB_MARK_CELL) &&
2813 !property->HasFlag(wxPG_PROP_INVALID_VALUE) )
2814 {
2815 unsigned int colCount = m_pState->GetColumnCount();
2816
2817 // We need backup marked property's cells
2818 m_propCellsBackup = property->m_cells;
2819
2820 wxColour vfbFg = *wxWHITE;
2821 wxColour vfbBg = *wxRED;
2822
2823 property->EnsureCells(colCount);
2824
2825 for ( unsigned int i=0; i<colCount; i++ )
2826 {
2827 wxPGCell& cell = property->m_cells[i];
2828 cell.SetFgCol(vfbFg);
2829 cell.SetBgCol(vfbBg);
2830 }
2831
2832 DrawItemAndChildren(property);
2833
2834 if ( property == GetSelection() )
2835 {
2836 SetInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL);
2837
2838 wxWindow* editor = GetEditorControl();
2839 if ( editor )
2840 {
2841 editor->SetForegroundColour(vfbFg);
2842 editor->SetBackgroundColour(vfbBg);
2843 }
2844 }
2845 }
2846
2847 if ( vfb & wxPG_VFB_SHOW_MESSAGE )
2848 {
2849 wxString msg = m_validationInfo.m_failureMessage;
2850
2851 if ( !msg.length() )
2852 msg = wxT("You have entered invalid value. Press ESC to cancel editing.");
2853
2854 DoShowPropertyError(property, msg);
2855 }
2856
2857 return (vfb & wxPG_VFB_STAY_IN_PROPERTY) ? false : true;
2858 }
2859
2860 // -----------------------------------------------------------------------
2861
2862 void wxPropertyGrid::DoOnValidationFailureReset( wxPGProperty* property )
2863 {
2864 int vfb = m_validationInfo.m_failureBehavior;
2865
2866 if ( vfb & wxPG_VFB_MARK_CELL )
2867 {
2868 // Revert cells
2869 property->m_cells = m_propCellsBackup;
2870
2871 ClearInternalFlag(wxPG_FL_CELL_OVERRIDES_SEL);
2872
2873 if ( property == GetSelection() && GetEditorControl() )
2874 {
2875 // Calling this will recreate the control, thus resetting its colour
2876 RefreshProperty(property);
2877 }
2878 else
2879 {
2880 DrawItemAndChildren(property);
2881 }
2882 }
2883 }
2884
2885 // -----------------------------------------------------------------------
2886
2887 // flags are same as with DoSelectProperty
2888 bool wxPropertyGrid::DoPropertyChanged( wxPGProperty* p, unsigned int selFlags )
2889 {
2890 if ( m_inDoPropertyChanged )
2891 return true;
2892
2893 wxWindow* editor = GetEditorControl();
2894 wxPGProperty* selected = GetSelection();
2895
2896 m_pState->m_anyModified = 1;
2897
2898 m_inDoPropertyChanged = 1;
2899
2900 // Maybe need to update control
2901 wxASSERT( m_chgInfo_changedProperty != NULL );
2902
2903 // These values were calculated in PerformValidation()
2904 wxPGProperty* changedProperty = m_chgInfo_changedProperty;
2905 wxVariant value = m_chgInfo_pendingValue;
2906
2907 wxPGProperty* topPaintedProperty = changedProperty;
2908
2909 while ( !topPaintedProperty->IsCategory() &&
2910 !topPaintedProperty->IsRoot() )
2911 {
2912 topPaintedProperty = topPaintedProperty->GetParent();
2913 }
2914
2915 changedProperty->SetValue(value, &m_chgInfo_valueList, wxPG_SETVAL_BY_USER);
2916
2917 // Set as Modified (not if dragging just began)
2918 if ( !(p->m_flags & wxPG_PROP_MODIFIED) )
2919 {
2920 p->m_flags |= wxPG_PROP_MODIFIED;
2921 if ( p == selected && (m_windowStyle & wxPG_BOLD_MODIFIED) )
2922 {
2923 if ( editor )
2924 SetCurControlBoldFont();
2925 }
2926 }
2927
2928 wxPGProperty* pwc;
2929
2930 // Propagate updates to parent(s)
2931 pwc = p;
2932 wxPGProperty* prevPwc = NULL;
2933
2934 while ( prevPwc != topPaintedProperty )
2935 {
2936 pwc->m_flags |= wxPG_PROP_MODIFIED;
2937
2938 if ( pwc == selected && (m_windowStyle & wxPG_BOLD_MODIFIED) )
2939 {
2940 if ( editor )
2941 SetCurControlBoldFont();
2942 }
2943
2944 prevPwc = pwc;
2945 pwc = pwc->GetParent();
2946 }
2947
2948 // Draw the actual property
2949 DrawItemAndChildren( topPaintedProperty );
2950
2951 //
2952 // If value was set by wxPGProperty::OnEvent, then update the editor
2953 // control.
2954 if ( selFlags & wxPG_SEL_DIALOGVAL )
2955 {
2956 RefreshEditor();
2957 }
2958 else
2959 {
2960 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
2961 if ( m_wndEditor ) m_wndEditor->Refresh();
2962 if ( m_wndEditor2 ) m_wndEditor2->Refresh();
2963 #endif
2964 }
2965
2966 // Sanity check
2967 wxASSERT( !changedProperty->GetParent()->HasFlag(wxPG_PROP_AGGREGATE) );
2968
2969 // If top parent has composite string value, then send to child parents,
2970 // starting from baseChangedProperty.
2971 if ( changedProperty->HasFlag(wxPG_PROP_COMPOSED_VALUE) )
2972 {
2973 pwc = m_chgInfo_baseChangedProperty;
2974
2975 while ( pwc != changedProperty )
2976 {
2977 SendEvent( wxEVT_PG_CHANGED, pwc, NULL, selFlags );
2978 pwc = pwc->GetParent();
2979 }
2980 }
2981
2982 SendEvent( wxEVT_PG_CHANGED, changedProperty, NULL, selFlags );
2983
2984 m_inDoPropertyChanged = 0;
2985
2986 return true;
2987 }
2988
2989 // -----------------------------------------------------------------------
2990
2991 bool wxPropertyGrid::ChangePropertyValue( wxPGPropArg id, wxVariant newValue )
2992 {
2993 wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
2994
2995 m_chgInfo_changedProperty = NULL;
2996
2997 if ( PerformValidation(p, newValue) )
2998 {
2999 DoPropertyChanged(p);
3000 return true;
3001 }
3002 else
3003 {
3004 OnValidationFailure(p, newValue);
3005 }
3006
3007 return false;
3008 }
3009
3010 // -----------------------------------------------------------------------
3011
3012 wxVariant wxPropertyGrid::GetUncommittedPropertyValue()
3013 {
3014 wxPGProperty* prop = GetSelectedProperty();
3015
3016 if ( !prop )
3017 return wxNullVariant;
3018
3019 wxTextCtrl* tc = GetEditorTextCtrl();
3020 wxVariant value = prop->GetValue();
3021
3022 if ( !tc || !IsEditorsValueModified() )
3023 return value;
3024
3025 if ( !prop->StringToValue(value, tc->GetValue()) )
3026 return value;
3027
3028 if ( !PerformValidation(prop, value, IsStandaloneValidation) )
3029 return prop->GetValue();
3030
3031 return value;
3032 }
3033
3034 // -----------------------------------------------------------------------
3035
3036 // Runs wxValidator for the selected property
3037 bool wxPropertyGrid::DoEditorValidate()
3038 {
3039 return true;
3040 }
3041
3042 // -----------------------------------------------------------------------
3043
3044 void wxPropertyGrid::HandleCustomEditorEvent( wxEvent &event )
3045 {
3046 wxPGProperty* selected = GetSelection();
3047
3048 // Somehow, event is handled after property has been deselected.
3049 // Possibly, but very rare.
3050 if ( !selected || selected->HasFlag(wxPG_PROP_BEING_DELETED) )
3051 return;
3052
3053 if ( m_iFlags & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT )
3054 return;
3055
3056 wxVariant pendingValue(selected->GetValueRef());
3057 wxWindow* wnd = GetEditorControl();
3058 wxWindow* editorWnd = wxDynamicCast(event.GetEventObject(), wxWindow);
3059 int selFlags = 0;
3060 bool wasUnspecified = selected->IsValueUnspecified();
3061 int usesAutoUnspecified = selected->UsesAutoUnspecified();
3062 bool valueIsPending = false;
3063
3064 m_chgInfo_changedProperty = NULL;
3065
3066 m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED|wxPG_FL_VALUE_CHANGE_IN_EVENT);
3067
3068 //
3069 // Filter out excess wxTextCtrl modified events
3070 if ( event.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED &&
3071 wnd &&
3072 wnd->IsKindOf(CLASSINFO(wxTextCtrl)) )
3073 {
3074 wxTextCtrl* tc = (wxTextCtrl*) wnd;
3075
3076 wxString newTcValue = tc->GetValue();
3077 if ( m_prevTcValue == newTcValue )
3078 return;
3079
3080 m_prevTcValue = newTcValue;
3081 }
3082
3083 SetInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT);
3084
3085 bool validationFailure = false;
3086 bool buttonWasHandled = false;
3087
3088 //
3089 // Try common button handling
3090 if ( m_wndEditor2 && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
3091 {
3092 wxPGEditorDialogAdapter* adapter = selected->GetEditorDialog();
3093
3094 if ( adapter )
3095 {
3096 buttonWasHandled = true;
3097 // Store as res2, as previously (and still currently alternatively)
3098 // dialogs can be shown by handling wxEVT_COMMAND_BUTTON_CLICKED
3099 // in wxPGProperty::OnEvent().
3100 adapter->ShowDialog( this, selected );
3101 delete adapter;
3102 }
3103 }
3104
3105 if ( !buttonWasHandled )
3106 {
3107 if ( wnd || m_wndEditor2 )
3108 {
3109 // First call editor class' event handler.
3110 const wxPGEditor* editor = selected->GetEditorClass();
3111
3112 if ( editor->OnEvent( this, selected, editorWnd, event ) )
3113 {
3114 // If changes, validate them
3115 if ( DoEditorValidate() )
3116 {
3117 if ( editor->GetValueFromControl( pendingValue,
3118 selected,
3119 wnd ) )
3120 valueIsPending = true;
3121 }
3122 else
3123 {
3124 validationFailure = true;
3125 }
3126 }
3127 }
3128
3129 // Then the property's custom handler (must be always called, unless
3130 // validation failed).
3131 if ( !validationFailure )
3132 buttonWasHandled = selected->OnEvent( this, editorWnd, event );
3133 }
3134
3135 // SetValueInEvent(), as called in one of the functions referred above
3136 // overrides editor's value.
3137 if ( m_iFlags & wxPG_FL_VALUE_CHANGE_IN_EVENT )
3138 {
3139 valueIsPending = true;
3140 pendingValue = m_changeInEventValue;
3141 selFlags |= wxPG_SEL_DIALOGVAL;
3142 }
3143
3144 if ( !validationFailure && valueIsPending )
3145 if ( !PerformValidation(selected, pendingValue) )
3146 validationFailure = true;
3147
3148 if ( validationFailure)
3149 {
3150 OnValidationFailure(selected, pendingValue);
3151 }
3152 else if ( valueIsPending )
3153 {
3154 selFlags |= ( !wasUnspecified && selected->IsValueUnspecified() && usesAutoUnspecified ) ? wxPG_SEL_SETUNSPEC : 0;
3155
3156 DoPropertyChanged(selected, selFlags);
3157 EditorsValueWasNotModified();
3158
3159 // Regardless of editor type, unfocus editor on
3160 // text-editing related enter press.
3161 if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER )
3162 {
3163 SetFocusOnCanvas();
3164 }
3165 }
3166 else
3167 {
3168 // No value after all
3169
3170 // Regardless of editor type, unfocus editor on
3171 // text-editing related enter press.
3172 if ( event.GetEventType() == wxEVT_COMMAND_TEXT_ENTER )
3173 {
3174 SetFocusOnCanvas();
3175 }
3176
3177 // Let unhandled button click events go to the parent
3178 if ( !buttonWasHandled && event.GetEventType() == wxEVT_COMMAND_BUTTON_CLICKED )
3179 {
3180 wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED,GetId());
3181 GetEventHandler()->AddPendingEvent(evt);
3182 }
3183 }
3184
3185 ClearInternalFlag(wxPG_FL_IN_HANDLECUSTOMEDITOREVENT);
3186 }
3187
3188 // -----------------------------------------------------------------------
3189 // wxPropertyGrid editor control helper methods
3190 // -----------------------------------------------------------------------
3191
3192 wxRect wxPropertyGrid::GetEditorWidgetRect( wxPGProperty* p, int column ) const
3193 {
3194 int itemy = p->GetY2(m_lineHeight);
3195 int vy = 0;
3196 int splitterX = m_pState->DoGetSplitterPosition(column-1);
3197 int colEnd = splitterX + m_pState->m_colWidths[column];
3198 int imageOffset = 0;
3199
3200 // TODO: If custom image detection changes from current, change this.
3201 if ( m_iFlags & wxPG_FL_CUR_USES_CUSTOM_IMAGE )
3202 {
3203 //m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
3204 int iw = p->OnMeasureImage().x;
3205 if ( iw < 1 )
3206 iw = wxPG_CUSTOM_IMAGE_WIDTH;
3207 imageOffset = p->GetImageOffset(iw);
3208 }
3209
3210 return wxRect
3211 (
3212 splitterX+imageOffset+wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1,
3213 itemy-vy,
3214 colEnd-splitterX-wxPG_XBEFOREWIDGET-wxPG_CONTROL_MARGIN-imageOffset-1,
3215 m_lineHeight-1
3216 );
3217 }
3218
3219 // -----------------------------------------------------------------------
3220
3221 wxRect wxPropertyGrid::GetImageRect( wxPGProperty* p, int item ) const
3222 {
3223 wxSize sz = GetImageSize(p, item);
3224 return wxRect(wxPG_CONTROL_MARGIN + wxCC_CUSTOM_IMAGE_MARGIN1,
3225 wxPG_CUSTOM_IMAGE_SPACINGY,
3226 sz.x,
3227 sz.y);
3228 }
3229
3230 // return size of custom paint image
3231 wxSize wxPropertyGrid::GetImageSize( wxPGProperty* p, int item ) const
3232 {
3233 // If called with NULL property, then return default image
3234 // size for properties that use image.
3235 if ( !p )
3236 return wxSize(wxPG_CUSTOM_IMAGE_WIDTH,wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight));
3237
3238 wxSize cis = p->OnMeasureImage(item);
3239
3240 int choiceCount = p->m_choices.GetCount();
3241 int comVals = p->GetDisplayedCommonValueCount();
3242 if ( item >= choiceCount && comVals > 0 )
3243 {
3244 unsigned int cvi = item-choiceCount;
3245 cis = GetCommonValue(cvi)->GetRenderer()->GetImageSize(NULL, 1, cvi);
3246 }
3247 else if ( item >= 0 && choiceCount == 0 )
3248 return wxSize(0, 0);
3249
3250 if ( cis.x < 0 )
3251 {
3252 if ( cis.x <= -1 )
3253 cis.x = wxPG_CUSTOM_IMAGE_WIDTH;
3254 }
3255 if ( cis.y <= 0 )
3256 {
3257 if ( cis.y >= -1 )
3258 cis.y = wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight);
3259 else
3260 cis.y = -cis.y;
3261 }
3262 return cis;
3263 }
3264
3265 // -----------------------------------------------------------------------
3266
3267 // takes scrolling into account
3268 void wxPropertyGrid::ImprovedClientToScreen( int* px, int* py )
3269 {
3270 int vx, vy;
3271 GetViewStart(&vx,&vy);
3272 vy*=wxPG_PIXELS_PER_UNIT;
3273 vx*=wxPG_PIXELS_PER_UNIT;
3274 *px -= vx;
3275 *py -= vy;
3276 ClientToScreen( px, py );
3277 }
3278
3279 // -----------------------------------------------------------------------
3280
3281 wxPropertyGridHitTestResult wxPropertyGrid::HitTest( const wxPoint& pt ) const
3282 {
3283 wxPoint pt2;
3284 GetViewStart(&pt2.x,&pt2.y);
3285 pt2.x *= wxPG_PIXELS_PER_UNIT;
3286 pt2.y *= wxPG_PIXELS_PER_UNIT;
3287 pt2.x += pt.x;
3288 pt2.y += pt.y;
3289
3290 return m_pState->HitTest(pt2);
3291 }
3292
3293 // -----------------------------------------------------------------------
3294
3295 // custom set cursor
3296 void wxPropertyGrid::CustomSetCursor( int type, bool override )
3297 {
3298 if ( type == m_curcursor && !override ) return;
3299
3300 wxCursor* cursor = &wxPG_DEFAULT_CURSOR;
3301
3302 if ( type == wxCURSOR_SIZEWE )
3303 cursor = m_cursorSizeWE;
3304
3305 m_canvas->SetCursor( *cursor );
3306
3307 m_curcursor = type;
3308 }
3309
3310 // -----------------------------------------------------------------------
3311 // wxPropertyGrid property selection, editor creation
3312 // -----------------------------------------------------------------------
3313
3314 //
3315 // This class forwards events from property editor controls to wxPropertyGrid.
3316 class wxPropertyGridEditorEventForwarder : public wxEvtHandler
3317 {
3318 public:
3319 wxPropertyGridEditorEventForwarder( wxPropertyGrid* propGrid )
3320 : wxEvtHandler(), m_propGrid(propGrid)
3321 {
3322 }
3323
3324 virtual ~wxPropertyGridEditorEventForwarder()
3325 {
3326 }
3327
3328 private:
3329 bool ProcessEvent( wxEvent& event )
3330 {
3331 // Always skip
3332 event.Skip();
3333
3334 m_propGrid->HandleCustomEditorEvent(event);
3335
3336 return wxEvtHandler::ProcessEvent(event);
3337 }
3338
3339 wxPropertyGrid* m_propGrid;
3340 };
3341
3342 // Setups event handling for child control
3343 void wxPropertyGrid::SetupChildEventHandling( wxWindow* argWnd )
3344 {
3345 wxWindowID id = argWnd->GetId();
3346
3347 if ( argWnd == m_wndEditor )
3348 {
3349 argWnd->Connect(id, wxEVT_MOTION,
3350 wxMouseEventHandler(wxPropertyGrid::OnMouseMoveChild),
3351 NULL, this);
3352 argWnd->Connect(id, wxEVT_LEFT_UP,
3353 wxMouseEventHandler(wxPropertyGrid::OnMouseUpChild),
3354 NULL, this);
3355 argWnd->Connect(id, wxEVT_LEFT_DOWN,
3356 wxMouseEventHandler(wxPropertyGrid::OnMouseClickChild),
3357 NULL, this);
3358 argWnd->Connect(id, wxEVT_RIGHT_UP,
3359 wxMouseEventHandler(wxPropertyGrid::OnMouseRightClickChild),
3360 NULL, this);
3361 argWnd->Connect(id, wxEVT_ENTER_WINDOW,
3362 wxMouseEventHandler(wxPropertyGrid::OnMouseEntry),
3363 NULL, this);
3364 argWnd->Connect(id, wxEVT_LEAVE_WINDOW,
3365 wxMouseEventHandler(wxPropertyGrid::OnMouseEntry),
3366 NULL, this);
3367 }
3368
3369 wxPropertyGridEditorEventForwarder* forwarder;
3370 forwarder = new wxPropertyGridEditorEventForwarder(this);
3371 argWnd->PushEventHandler(forwarder);
3372
3373 argWnd->Connect(id, wxEVT_KEY_DOWN,
3374 wxCharEventHandler(wxPropertyGrid::OnChildKeyDown),
3375 NULL, this);
3376 }
3377
3378 void wxPropertyGrid::FreeEditors()
3379 {
3380 //
3381 // Return focus back to canvas from children (this is required at least for
3382 // GTK+, which, unlike Windows, clears focus when control is destroyed
3383 // instead of moving it to closest parent).
3384 wxWindow* focus = wxWindow::FindFocus();
3385 if ( focus )
3386 {
3387 wxWindow* parent = focus->GetParent();
3388 while ( parent )
3389 {
3390 if ( parent == m_canvas )
3391 {
3392 SetFocusOnCanvas();
3393 break;
3394 }
3395 parent = parent->GetParent();
3396 }
3397 }
3398
3399 // Do not free editors immediately if processing events
3400 if ( m_wndEditor2 )
3401 {
3402 wxEvtHandler* handler = m_wndEditor2->PopEventHandler(false);
3403 m_wndEditor2->Hide();
3404 wxPendingDelete.Append( handler );
3405 wxPendingDelete.Append( m_wndEditor2 );
3406 m_wndEditor2 = NULL;
3407 }
3408
3409 if ( m_wndEditor )
3410 {
3411 wxEvtHandler* handler = m_wndEditor->PopEventHandler(false);
3412 m_wndEditor->Hide();
3413 wxPendingDelete.Append( handler );
3414 wxPendingDelete.Append( m_wndEditor );
3415 m_wndEditor = NULL;
3416 }
3417 }
3418
3419 // Call with NULL to de-select property
3420 bool wxPropertyGrid::DoSelectProperty( wxPGProperty* p, unsigned int flags )
3421 {
3422 /*
3423 if (p)
3424 {
3425 wxLogDebug(wxT("SelectProperty( %s (%s[%i]) )"),p->m_label.c_str(),
3426 p->m_parent->m_label.c_str(),p->GetIndexInParent());
3427 }
3428 else
3429 {
3430 wxLogDebug(wxT("SelectProperty( NULL, -1 )"));
3431 }
3432 */
3433
3434 if ( m_inDoSelectProperty )
3435 return true;
3436
3437 m_inDoSelectProperty = 1;
3438
3439 if ( !m_pState )
3440 {
3441 m_inDoSelectProperty = 0;
3442 return false;
3443 }
3444
3445 wxArrayPGProperty prevSelection = m_pState->m_selection;
3446 wxPGProperty* prevFirstSel;
3447
3448 if ( prevSelection.size() > 0 )
3449 prevFirstSel = prevSelection[0];
3450 else
3451 prevFirstSel = NULL;
3452
3453 if ( prevFirstSel && prevFirstSel->HasFlag(wxPG_PROP_BEING_DELETED) )
3454 prevFirstSel = NULL;
3455
3456 /*
3457 if ( prevFirstSel )
3458 wxPrintf( "Selected %s\n", prevFirstSel->GetClassInfo()->GetClassName() );
3459 else
3460 wxPrintf( "None selected\n" );
3461
3462 if (p)
3463 wxPrintf( "P = %s\n", p->GetClassInfo()->GetClassName() );
3464 else
3465 wxPrintf( "P = NULL\n" );
3466 */
3467
3468 // If we are frozen, then just set the values.
3469 if ( m_frozen )
3470 {
3471 m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR);
3472 m_editorFocused = 0;
3473 m_selColumn = 1;
3474 m_pState->DoSetSelection(p);
3475
3476 // If frozen, always free controls. But don't worry, as Thaw will
3477 // recall SelectProperty to recreate them.
3478 FreeEditors();
3479
3480 // Prevent any further selection measures in this call
3481 p = NULL;
3482 }
3483 else
3484 {
3485 // Is it the same?
3486 if ( prevFirstSel == p &&
3487 prevSelection.size() <= 1 &&
3488 !(flags & wxPG_SEL_FORCE) )
3489 {
3490 // Only set focus if not deselecting
3491 if ( p )
3492 {
3493 if ( flags & wxPG_SEL_FOCUS )
3494 {
3495 if ( m_wndEditor )
3496 {
3497 m_wndEditor->SetFocus();
3498 m_editorFocused = 1;
3499 }
3500 }
3501 else
3502 {
3503 SetFocusOnCanvas();
3504 }
3505 }
3506
3507 m_inDoSelectProperty = 0;
3508 return true;
3509 }
3510
3511 //
3512 // First, deactivate previous
3513 if ( prevFirstSel )
3514 {
3515 OnValidationFailureReset(prevFirstSel);
3516
3517 // Must double-check if this is an selected in case of forceswitch
3518 if ( p != prevFirstSel )
3519 {
3520 if ( !CommitChangesFromEditor(flags) )
3521 {
3522 // Validation has failed, so we can't exit the previous editor
3523 //::wxMessageBox(_("Please correct the value or press ESC to cancel the edit."),
3524 // _("Invalid Value"),wxOK|wxICON_ERROR);
3525 m_inDoSelectProperty = 0;
3526 return false;
3527 }
3528 }
3529
3530 FreeEditors();
3531 m_selColumn = -1;
3532
3533 // We need to always fully refresh the grid here
3534 Refresh(false);
3535
3536 m_iFlags &= ~(wxPG_FL_ABNORMAL_EDITOR);
3537 EditorsValueWasNotModified();
3538 }
3539
3540 SetInternalFlag(wxPG_FL_IN_SELECT_PROPERTY);
3541
3542 m_pState->DoSetSelection(p);
3543
3544 //
3545 // Then, activate the one given.
3546 if ( p )
3547 {
3548 int propY = p->GetY2(m_lineHeight);
3549
3550 int splitterX = GetSplitterPosition();
3551 m_editorFocused = 0;
3552 m_iFlags |= wxPG_FL_PRIMARY_FILLS_ENTIRE;
3553 if ( p != prevFirstSel )
3554 m_iFlags &= ~(wxPG_FL_VALIDATION_FAILED);
3555
3556 wxASSERT( m_wndEditor == NULL );
3557
3558 //
3559 // Only create editor for non-disabled non-caption
3560 if ( !p->IsCategory() && !(p->m_flags & wxPG_PROP_DISABLED) )
3561 {
3562 // do this for non-caption items
3563
3564 m_selColumn = 1;
3565
3566 // Do we need to paint the custom image, if any?
3567 m_iFlags &= ~(wxPG_FL_CUR_USES_CUSTOM_IMAGE);
3568 if ( (p->m_flags & wxPG_PROP_CUSTOMIMAGE) &&
3569 !p->GetEditorClass()->CanContainCustomImage()
3570 )
3571 m_iFlags |= wxPG_FL_CUR_USES_CUSTOM_IMAGE;
3572
3573 wxRect grect = GetEditorWidgetRect(p, m_selColumn);
3574 wxPoint goodPos = grect.GetPosition();
3575 #if wxPG_CREATE_CONTROLS_HIDDEN
3576 int coord_adjust = m_height - goodPos.y;
3577 goodPos.y += coord_adjust;
3578 #endif
3579
3580 const wxPGEditor* editor = p->GetEditorClass();
3581 wxCHECK_MSG(editor, false,
3582 wxT("NULL editor class not allowed"));
3583
3584 m_iFlags &= ~wxPG_FL_FIXED_WIDTH_EDITOR;
3585
3586 wxPGWindowList wndList = editor->CreateControls(this,
3587 p,
3588 goodPos,
3589 grect.GetSize());
3590
3591 m_wndEditor = wndList.m_primary;
3592 m_wndEditor2 = wndList.m_secondary;
3593 wxWindow* primaryCtrl = GetEditorControl();
3594
3595 //
3596 // Essentially, primaryCtrl == m_wndEditor
3597 //
3598
3599 // NOTE: It is allowed for m_wndEditor to be NULL - in this case
3600 // value is drawn as normal, and m_wndEditor2 is assumed
3601 // to be a right-aligned button that triggers a separate editorCtrl
3602 // window.
3603
3604 if ( m_wndEditor )
3605 {
3606 wxASSERT_MSG( m_wndEditor->GetParent() == GetPanel(),
3607 wxT("CreateControls must use result of wxPropertyGrid::GetPanel() as parent of controls.") );
3608
3609 // Set validator, if any
3610 #if wxUSE_VALIDATORS
3611 wxValidator* validator = p->GetValidator();
3612 if ( validator )
3613 primaryCtrl->SetValidator(*validator);
3614 #endif
3615
3616 if ( m_wndEditor->GetSize().y > (m_lineHeight+6) )
3617 m_iFlags |= wxPG_FL_ABNORMAL_EDITOR;
3618
3619 // If it has modified status, use bold font
3620 // (must be done before capturing m_ctrlXAdjust)
3621 if ( (p->m_flags & wxPG_PROP_MODIFIED) && (m_windowStyle & wxPG_BOLD_MODIFIED) )
3622 SetCurControlBoldFont();
3623
3624 //
3625 // Fix TextCtrl indentation
3626 #if defined(__WXMSW__) && !defined(__WXWINCE__)
3627 wxTextCtrl* tc = NULL;
3628 if ( primaryCtrl->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)) )
3629 tc = ((wxOwnerDrawnComboBox*)primaryCtrl)->GetTextCtrl();
3630 else
3631 tc = wxDynamicCast(primaryCtrl, wxTextCtrl);
3632 if ( tc )
3633 ::SendMessage(GetHwndOf(tc), EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 0));
3634 #endif
3635
3636 // Store x relative to splitter (we'll need it).
3637 m_ctrlXAdjust = m_wndEditor->GetPosition().x - splitterX;
3638
3639 // Check if background clear is not necessary
3640 wxPoint pos = m_wndEditor->GetPosition();
3641 if ( pos.x > (splitterX+1) || pos.y > propY )
3642 {
3643 m_iFlags &= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE);
3644 }
3645
3646 m_wndEditor->SetSizeHints(3, 3);
3647
3648 #if wxPG_CREATE_CONTROLS_HIDDEN
3649 m_wndEditor->Show(false);
3650 m_wndEditor->Freeze();
3651
3652 goodPos = m_wndEditor->GetPosition();
3653 goodPos.y -= coord_adjust;
3654 m_wndEditor->Move( goodPos );
3655 #endif
3656
3657 SetupChildEventHandling(primaryCtrl);
3658
3659 // Focus and select all (wxTextCtrl, wxComboBox etc)
3660 if ( flags & wxPG_SEL_FOCUS )
3661 {
3662 primaryCtrl->SetFocus();
3663
3664 p->GetEditorClass()->OnFocus(p, primaryCtrl);
3665 }
3666 }
3667
3668 if ( m_wndEditor2 )
3669 {
3670 wxASSERT_MSG( m_wndEditor2->GetParent() == GetPanel(),
3671 wxT("CreateControls must use result of wxPropertyGrid::GetPanel() as parent of controls.") );
3672
3673 // Get proper id for wndSecondary
3674 m_wndSecId = m_wndEditor2->GetId();
3675 wxWindowList children = m_wndEditor2->GetChildren();
3676 wxWindowList::iterator node = children.begin();
3677 if ( node != children.end() )
3678 m_wndSecId = ((wxWindow*)*node)->GetId();
3679
3680 m_wndEditor2->SetSizeHints(3,3);
3681
3682 #if wxPG_CREATE_CONTROLS_HIDDEN
3683 wxRect sec_rect = m_wndEditor2->GetRect();
3684 sec_rect.y -= coord_adjust;
3685
3686 // Fine tuning required to fix "oversized"
3687 // button disappearance bug.
3688 if ( sec_rect.y < 0 )
3689 {
3690 sec_rect.height += sec_rect.y;
3691 sec_rect.y = 0;
3692 }
3693 m_wndEditor2->SetSize( sec_rect );
3694 #endif
3695 m_wndEditor2->Show();
3696
3697 SetupChildEventHandling(m_wndEditor2);
3698
3699 // If no primary editor, focus to button to allow
3700 // it to interprete ENTER etc.
3701 // NOTE: Due to problems focusing away from it, this
3702 // has been disabled.
3703 /*
3704 if ( (flags & wxPG_SEL_FOCUS) && !m_wndEditor )
3705 m_wndEditor2->SetFocus();
3706 */
3707 }
3708
3709 if ( flags & wxPG_SEL_FOCUS )
3710 m_editorFocused = 1;
3711
3712 }
3713 else
3714 {
3715 // Make sure focus is in grid canvas (important for wxGTK, at least)
3716 SetFocusOnCanvas();
3717 }
3718
3719 EditorsValueWasNotModified();
3720
3721 // If it's inside collapsed section, expand parent, scroll, etc.
3722 // Also, if it was partially visible, scroll it into view.
3723 if ( !(flags & wxPG_SEL_NONVISIBLE) )
3724 EnsureVisible( p );
3725
3726 if ( m_wndEditor )
3727 {
3728 #if wxPG_CREATE_CONTROLS_HIDDEN
3729 m_wndEditor->Thaw();
3730 #endif
3731 m_wndEditor->Show(true);
3732 }
3733
3734 DrawItems(p, p);
3735 }
3736 else
3737 {
3738 // Make sure focus is in grid canvas
3739 SetFocusOnCanvas();
3740 }
3741
3742 ClearInternalFlag(wxPG_FL_IN_SELECT_PROPERTY);
3743 }
3744
3745 #if wxUSE_STATUSBAR
3746
3747 //
3748 // Show help text in status bar.
3749 // (if found and grid not embedded in manager with help box and
3750 // style wxPG_EX_HELP_AS_TOOLTIPS is not used).
3751 //
3752
3753 if ( !(GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS) )
3754 {
3755 wxStatusBar* statusbar = NULL;
3756 if ( !(m_iFlags & wxPG_FL_NOSTATUSBARHELP) )
3757 {
3758 wxFrame* frame = wxDynamicCast(::wxGetTopLevelParent(this),wxFrame);
3759 if ( frame )
3760 statusbar = frame->GetStatusBar();
3761 }
3762
3763 if ( statusbar )
3764 {
3765 const wxString* pHelpString = (const wxString*) NULL;
3766
3767 if ( p )
3768 {
3769 pHelpString = &p->GetHelpString();
3770 if ( pHelpString->length() )
3771 {
3772 // Set help box text.
3773 statusbar->SetStatusText( *pHelpString );
3774 m_iFlags |= wxPG_FL_STRING_IN_STATUSBAR;
3775 }
3776 }
3777
3778 if ( (!pHelpString || !pHelpString->length()) &&
3779 (m_iFlags & wxPG_FL_STRING_IN_STATUSBAR) )
3780 {
3781 // Clear help box - but only if it was written
3782 // by us at previous time.
3783 statusbar->SetStatusText( m_emptyString );
3784 m_iFlags &= ~(wxPG_FL_STRING_IN_STATUSBAR);
3785 }
3786 }
3787 }
3788 #endif
3789
3790 m_inDoSelectProperty = 0;
3791
3792 // call wx event handler (here so that it also occurs on deselection)
3793 if ( !(flags & wxPG_SEL_DONT_SEND_EVENT) )
3794 SendEvent( wxEVT_PG_SELECTED, p, NULL, flags );
3795
3796 return true;
3797 }
3798
3799 // -----------------------------------------------------------------------
3800
3801 bool wxPropertyGrid::UnfocusEditor()
3802 {
3803 wxPGProperty* selected = GetSelection();
3804
3805 if ( !selected || !m_wndEditor || m_frozen )
3806 return true;
3807
3808 if ( !CommitChangesFromEditor(0) )
3809 return false;
3810
3811 SetFocusOnCanvas();
3812 DrawItem(selected);
3813
3814 return true;
3815 }
3816
3817 // -----------------------------------------------------------------------
3818
3819 void wxPropertyGrid::RefreshEditor()
3820 {
3821 wxPGProperty* p = GetSelection();
3822 if ( !p )
3823 return;
3824
3825 wxWindow* wnd = GetEditorControl();
3826 if ( !wnd )
3827 return;
3828
3829 // Set editor font boldness - must do this before
3830 // calling UpdateControl().
3831 if ( HasFlag(wxPG_BOLD_MODIFIED) )
3832 {
3833 if ( p->HasFlag(wxPG_PROP_MODIFIED) )
3834 wnd->SetFont(GetCaptionFont());
3835 else
3836 wnd->SetFont(GetFont());
3837 }
3838
3839 const wxPGEditor* editorClass = p->GetEditorClass();
3840
3841 editorClass->UpdateControl(p, wnd);
3842
3843 if ( p->IsValueUnspecified() )
3844 editorClass ->SetValueToUnspecified(p, wnd);
3845 }
3846
3847 // -----------------------------------------------------------------------
3848
3849 bool wxPropertyGrid::SelectProperty( wxPGPropArg id, bool focus )
3850 {
3851 wxPG_PROP_ARG_CALL_PROLOG_RETVAL(false)
3852
3853 int flags = wxPG_SEL_DONT_SEND_EVENT;
3854 if ( focus )
3855 flags |= wxPG_SEL_FOCUS;
3856
3857 return DoSelectProperty(p, flags);
3858 }
3859
3860 // -----------------------------------------------------------------------
3861 // wxPropertyGrid expand/collapse state
3862 // -----------------------------------------------------------------------
3863
3864 bool wxPropertyGrid::DoCollapse( wxPGProperty* p, bool sendEvents )
3865 {
3866 wxPGProperty* pwc = wxStaticCast(p, wxPGProperty);
3867 wxPGProperty* selected = GetSelection();
3868
3869 // If active editor was inside collapsed section, then disable it
3870 if ( selected && selected->IsSomeParent(p) )
3871 {
3872 DoClearSelection();
3873 }
3874
3875 // Store dont-center-splitter flag 'cause we need to temporarily set it
3876 wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
3877 m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
3878
3879 bool res = m_pState->DoCollapse(pwc);
3880
3881 if ( res )
3882 {
3883 if ( sendEvents )
3884 SendEvent( wxEVT_PG_ITEM_COLLAPSED, p );
3885
3886 RecalculateVirtualSize();
3887
3888 // Redraw etc. only if collapsed was visible.
3889 if (pwc->IsVisible() &&
3890 !m_frozen &&
3891 ( !pwc->IsCategory() || !(m_windowStyle & wxPG_HIDE_CATEGORIES) ) )
3892 {
3893 // When item is collapsed so that scrollbar would move,
3894 // graphics mess is about (unless we redraw everything).
3895 Refresh();
3896 }
3897 }
3898
3899 // Clear dont-center-splitter flag if it wasn't set
3900 m_iFlags = (m_iFlags & ~wxPG_FL_DONT_CENTER_SPLITTER) | old_flag;
3901
3902 return res;
3903 }
3904
3905 // -----------------------------------------------------------------------
3906
3907 bool wxPropertyGrid::DoExpand( wxPGProperty* p, bool sendEvents )
3908 {
3909 wxCHECK_MSG( p, false, wxT("invalid property id") );
3910
3911 wxPGProperty* pwc = (wxPGProperty*)p;
3912
3913 // Store dont-center-splitter flag 'cause we need to temporarily set it
3914 wxUint32 old_flag = m_iFlags & wxPG_FL_DONT_CENTER_SPLITTER;
3915 m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
3916
3917 bool res = m_pState->DoExpand(pwc);
3918
3919 if ( res )
3920 {
3921 if ( sendEvents )
3922 SendEvent( wxEVT_PG_ITEM_EXPANDED, p );
3923
3924 RecalculateVirtualSize();
3925
3926 // Redraw etc. only if expanded was visible.
3927 if ( pwc->IsVisible() && !m_frozen &&
3928 ( !pwc->IsCategory() || !(m_windowStyle & wxPG_HIDE_CATEGORIES) )
3929 )
3930 {
3931 // Redraw
3932 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
3933 Refresh();
3934 #else
3935 DrawItems(pwc, NULL);
3936 #endif
3937 }
3938 }
3939
3940 // Clear dont-center-splitter flag if it wasn't set
3941 m_iFlags = (m_iFlags & ~wxPG_FL_DONT_CENTER_SPLITTER) | old_flag;
3942
3943 return res;
3944 }
3945
3946 // -----------------------------------------------------------------------
3947
3948 bool wxPropertyGrid::DoHideProperty( wxPGProperty* p, bool hide, int flags )
3949 {
3950 if ( m_frozen )
3951 return m_pState->DoHideProperty(p, hide, flags);
3952
3953 wxArrayPGProperty selection = m_pState->m_selection; // Must use a copy
3954 int selRemoveCount = 0;
3955 for ( unsigned int i=0; i<selection.size(); i++ )
3956 {
3957 wxPGProperty* selected = selection[i];
3958 if ( selected == p || selected->IsSomeParent(p) )
3959 {
3960 if ( !DoRemoveFromSelection(p, flags) )
3961 return false;
3962 selRemoveCount += 1;
3963 }
3964 }
3965
3966 m_pState->DoHideProperty(p, hide, flags);
3967
3968 RecalculateVirtualSize();
3969 Refresh();
3970
3971 return true;
3972 }
3973
3974
3975 // -----------------------------------------------------------------------
3976 // wxPropertyGrid size related methods
3977 // -----------------------------------------------------------------------
3978
3979 void wxPropertyGrid::RecalculateVirtualSize( int forceXPos )
3980 {
3981 if ( (m_iFlags & wxPG_FL_RECALCULATING_VIRTUAL_SIZE) || m_frozen )
3982 return;
3983
3984 //
3985 // If virtual height was changed, then recalculate editor control position(s)
3986 if ( m_pState->m_vhCalcPending )
3987 CorrectEditorWidgetPosY();
3988
3989 m_pState->EnsureVirtualHeight();
3990
3991 wxASSERT_LEVEL_2_MSG(
3992 m_pState->GetVirtualHeight() == m_pState->GetActualVirtualHeight(),
3993 "VirtualHeight and ActualVirtualHeight should match"
3994 );
3995
3996 m_iFlags |= wxPG_FL_RECALCULATING_VIRTUAL_SIZE;
3997
3998 int x = m_pState->m_width;
3999 int y = m_pState->m_virtualHeight;
4000
4001 int width, height;
4002 GetClientSize(&width,&height);
4003
4004 // Now adjust virtual size.
4005 SetVirtualSize(x, y);
4006
4007 int xAmount = 0;
4008 int xPos = 0;
4009
4010 //
4011 // Adjust scrollbars
4012 if ( HasVirtualWidth() )
4013 {
4014 xAmount = x/wxPG_PIXELS_PER_UNIT;
4015 xPos = GetScrollPos( wxHORIZONTAL );
4016 }
4017
4018 if ( forceXPos != -1 )
4019 xPos = forceXPos;
4020 // xPos too high?
4021 else if ( xPos > (xAmount-(width/wxPG_PIXELS_PER_UNIT)) )
4022 xPos = 0;
4023
4024 int yAmount = y / wxPG_PIXELS_PER_UNIT;
4025 int yPos = GetScrollPos( wxVERTICAL );
4026
4027 SetScrollbars( wxPG_PIXELS_PER_UNIT, wxPG_PIXELS_PER_UNIT,
4028 xAmount, yAmount, xPos, yPos, true );
4029
4030 // Must re-get size now
4031 GetClientSize(&width,&height);
4032
4033 if ( !HasVirtualWidth() )
4034 {
4035 m_pState->SetVirtualWidth(width);
4036 x = width;
4037 }
4038
4039 m_width = width;
4040 m_height = height;
4041
4042 m_canvas->SetSize( x, y );
4043
4044 m_pState->CheckColumnWidths();
4045
4046 if ( GetSelection() )
4047 CorrectEditorWidgetSizeX();
4048
4049 m_iFlags &= ~wxPG_FL_RECALCULATING_VIRTUAL_SIZE;
4050 }
4051
4052 // -----------------------------------------------------------------------
4053
4054 void wxPropertyGrid::OnResize( wxSizeEvent& event )
4055 {
4056 if ( !(m_iFlags & wxPG_FL_INITIALIZED) )
4057 return;
4058
4059 int width, height;
4060 GetClientSize(&width,&height);
4061
4062 m_width = width;
4063 m_height = height;
4064
4065 #if wxPG_DOUBLE_BUFFER
4066 if ( !(GetExtraStyle() & wxPG_EX_NATIVE_DOUBLE_BUFFERING) )
4067 {
4068 int dblh = (m_lineHeight*2);
4069 if ( !m_doubleBuffer )
4070 {
4071 // Create double buffer bitmap to draw on, if none
4072 int w = (width>250)?width:250;
4073 int h = height + dblh;
4074 h = (h>400)?h:400;
4075 m_doubleBuffer = new wxBitmap( w, h );
4076 }
4077 else
4078 {
4079 int w = m_doubleBuffer->GetWidth();
4080 int h = m_doubleBuffer->GetHeight();
4081
4082 // Double buffer must be large enough
4083 if ( w < width || h < (height+dblh) )
4084 {
4085 if ( w < width ) w = width;
4086 if ( h < (height+dblh) ) h = height + dblh;
4087 delete m_doubleBuffer;
4088 m_doubleBuffer = new wxBitmap( w, h );
4089 }
4090 }
4091 }
4092
4093 #endif
4094
4095 m_pState->OnClientWidthChange( width, event.GetSize().x - m_ncWidth, true );
4096 m_ncWidth = event.GetSize().x;
4097
4098 if ( !m_frozen )
4099 {
4100 if ( m_pState->m_itemsAdded )
4101 PrepareAfterItemsAdded();
4102 else
4103 // Without this, virtual size (atleast under wxGTK) will be skewed
4104 RecalculateVirtualSize();
4105
4106 Refresh();
4107 }
4108 }
4109
4110 // -----------------------------------------------------------------------
4111
4112 void wxPropertyGrid::SetVirtualWidth( int width )
4113 {
4114 if ( width == -1 )
4115 {
4116 // Disable virtual width
4117 width = GetClientSize().x;
4118 ClearInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH);
4119 }
4120 else
4121 {
4122 // Enable virtual width
4123 SetInternalFlag(wxPG_FL_HAS_VIRTUAL_WIDTH);
4124 }
4125 m_pState->SetVirtualWidth( width );
4126 }
4127
4128 void wxPropertyGrid::SetFocusOnCanvas()
4129 {
4130 m_canvas->SetFocusIgnoringChildren();
4131 m_editorFocused = 0;
4132 }
4133
4134 // -----------------------------------------------------------------------
4135 // wxPropertyGrid mouse event handling
4136 // -----------------------------------------------------------------------
4137
4138 // selFlags uses same values DoSelectProperty's flags
4139 // Returns true if event was vetoed.
4140 bool wxPropertyGrid::SendEvent( int eventType, wxPGProperty* p, wxVariant* pValue, unsigned int WXUNUSED(selFlags) )
4141 {
4142 // Send property grid event of specific type and with specific property
4143 wxPropertyGridEvent evt( eventType, m_eventObject->GetId() );
4144 evt.SetPropertyGrid(this);
4145 evt.SetEventObject(m_eventObject);
4146 evt.SetProperty(p);
4147 if ( pValue )
4148 {
4149 evt.SetCanVeto(true);
4150 evt.SetupValidationInfo();
4151 m_validationInfo.m_pValue = pValue;
4152 }
4153 wxEvtHandler* evtHandler = m_eventObject->GetEventHandler();
4154
4155 evtHandler->ProcessEvent(evt);
4156
4157 return evt.WasVetoed();
4158 }
4159
4160 // -----------------------------------------------------------------------
4161
4162 // Return false if should be skipped
4163 bool wxPropertyGrid::HandleMouseClick( int x, unsigned int y, wxMouseEvent &event )
4164 {
4165 bool res = true;
4166
4167 // Need to set focus?
4168 if ( !(m_iFlags & wxPG_FL_FOCUSED) )
4169 {
4170 SetFocusOnCanvas();
4171 }
4172
4173 wxPropertyGridPageState* state = m_pState;
4174 int splitterHit;
4175 int splitterHitOffset;
4176 int columnHit = state->HitTestH( x, &splitterHit, &splitterHitOffset );
4177
4178 wxPGProperty* p = DoGetItemAtY(y);
4179
4180 if ( p )
4181 {
4182 int depth = (int)p->GetDepth() - 1;
4183
4184 int marginEnds = m_marginWidth + ( depth * m_subgroup_extramargin );
4185
4186 if ( x >= marginEnds )
4187 {
4188 // Outside margin.
4189
4190 if ( p->IsCategory() )
4191 {
4192 // This is category.
4193 wxPropertyCategory* pwc = (wxPropertyCategory*)p;
4194
4195 int textX = m_marginWidth + ((unsigned int)((pwc->m_depth-1)*m_subgroup_extramargin));
4196
4197 // Expand, collapse, activate etc. if click on text or left of splitter.
4198 if ( x >= textX
4199 &&
4200 ( x < (textX+pwc->GetTextExtent(this, m_captionFont)+(wxPG_CAPRECTXMARGIN*2)) ||
4201 columnHit == 0
4202 )
4203 )
4204 {
4205 if ( !AddToSelectionFromInputEvent( p, &event ) )
4206 return res;
4207
4208 // On double-click, expand/collapse.
4209 if ( event.ButtonDClick() && !(m_windowStyle & wxPG_HIDE_MARGIN) )
4210 {
4211 if ( pwc->IsExpanded() ) DoCollapse( p, true );
4212 else DoExpand( p, true );
4213 }
4214 }
4215 }
4216 else if ( splitterHit == -1 )
4217 {
4218 // Click on value.
4219 unsigned int selFlag = 0;
4220 if ( columnHit == 1 )
4221 {
4222 m_iFlags |= wxPG_FL_ACTIVATION_BY_CLICK;
4223 selFlag = wxPG_SEL_FOCUS;
4224 }
4225 if ( !AddToSelectionFromInputEvent( p, &event, selFlag ) )
4226 return res;
4227
4228 m_iFlags &= ~(wxPG_FL_ACTIVATION_BY_CLICK);
4229
4230 if ( p->GetChildCount() && !p->IsCategory() )
4231 // On double-click, expand/collapse.
4232 if ( event.ButtonDClick() && !(m_windowStyle & wxPG_HIDE_MARGIN) )
4233 {
4234 wxPGProperty* pwc = (wxPGProperty*)p;
4235 if ( pwc->IsExpanded() ) DoCollapse( p, true );
4236 else DoExpand( p, true );
4237 }
4238
4239 res = false;
4240 }
4241 else
4242 {
4243 // click on splitter
4244 if ( !(m_windowStyle & wxPG_STATIC_SPLITTER) )
4245 {
4246 if ( event.GetEventType() == wxEVT_LEFT_DCLICK )
4247 {
4248 // Double-clicking the splitter causes auto-centering
4249 CenterSplitter( true );
4250 }
4251 else if ( m_dragStatus == 0 )
4252 {
4253 //
4254 // Begin draggin the splitter
4255 //
4256 if ( m_wndEditor )
4257 {
4258 // Changes must be committed here or the
4259 // value won't be drawn correctly
4260 if ( !CommitChangesFromEditor() )
4261 return res;
4262
4263 m_wndEditor->Show ( false );
4264 }
4265
4266 if ( !(m_iFlags & wxPG_FL_MOUSE_CAPTURED) )
4267 {
4268 m_canvas->CaptureMouse();
4269 m_iFlags |= wxPG_FL_MOUSE_CAPTURED;
4270 }
4271
4272 m_dragStatus = 1;
4273 m_draggedSplitter = splitterHit;
4274 m_dragOffset = splitterHitOffset;
4275
4276 wxClientDC dc(m_canvas);
4277
4278 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
4279 // Fixes button disappearance bug
4280 if ( m_wndEditor2 )
4281 m_wndEditor2->Show ( false );
4282 #endif
4283
4284 m_startingSplitterX = x - splitterHitOffset;
4285 }
4286 }
4287 }
4288 }
4289 else
4290 {
4291 // Click on margin.
4292 if ( p->GetChildCount() )
4293 {
4294 int nx = x + m_marginWidth - marginEnds; // Normalize x.
4295
4296 if ( (nx >= m_gutterWidth && nx < (m_gutterWidth+m_iconWidth)) )
4297 {
4298 int y2 = y % m_lineHeight;
4299 if ( (y2 >= m_buttonSpacingY && y2 < (m_buttonSpacingY+m_iconHeight)) )
4300 {
4301 // On click on expander button, expand/collapse
4302 if ( ((wxPGProperty*)p)->IsExpanded() )
4303 DoCollapse( p, true );
4304 else
4305 DoExpand( p, true );
4306 }
4307 }
4308 }
4309 }
4310 }
4311 return res;
4312 }
4313
4314 // -----------------------------------------------------------------------
4315
4316 bool wxPropertyGrid::HandleMouseRightClick( int WXUNUSED(x),
4317 unsigned int WXUNUSED(y),
4318 wxMouseEvent& event )
4319 {
4320 if ( m_propHover )
4321 {
4322 // Select property here as well
4323 wxPGProperty* p = m_propHover;
4324 AddToSelectionFromInputEvent(p, &event);
4325
4326 // Send right click event.
4327 SendEvent( wxEVT_PG_RIGHT_CLICK, p );
4328
4329 return true;
4330 }
4331 return false;
4332 }
4333
4334 // -----------------------------------------------------------------------
4335
4336 bool wxPropertyGrid::HandleMouseDoubleClick( int WXUNUSED(x),
4337 unsigned int WXUNUSED(y),
4338 wxMouseEvent& event )
4339 {
4340 if ( m_propHover )
4341 {
4342 // Select property here as well
4343 wxPGProperty* p = m_propHover;
4344
4345 AddToSelectionFromInputEvent(p, &event);
4346
4347 // Send double-click event.
4348 SendEvent( wxEVT_PG_DOUBLE_CLICK, m_propHover );
4349
4350 return true;
4351 }
4352 return false;
4353 }
4354
4355 // -----------------------------------------------------------------------
4356
4357 #if wxPG_SUPPORT_TOOLTIPS
4358
4359 void wxPropertyGrid::SetToolTip( const wxString& tipString )
4360 {
4361 if ( tipString.length() )
4362 {
4363 m_canvas->SetToolTip(tipString);
4364 }
4365 else
4366 {
4367 #if wxPG_ALLOW_EMPTY_TOOLTIPS
4368 m_canvas->SetToolTip( m_emptyString );
4369 #else
4370 m_canvas->SetToolTip( NULL );
4371 #endif
4372 }
4373 }
4374
4375 #endif // #if wxPG_SUPPORT_TOOLTIPS
4376
4377 // -----------------------------------------------------------------------
4378
4379 // Return false if should be skipped
4380 bool wxPropertyGrid::HandleMouseMove( int x, unsigned int y, wxMouseEvent &event )
4381 {
4382 // Safety check (needed because mouse capturing may
4383 // otherwise freeze the control)
4384 if ( m_dragStatus > 0 && !event.Dragging() )
4385 {
4386 HandleMouseUp(x,y,event);
4387 }
4388
4389 wxPropertyGridPageState* state = m_pState;
4390 int splitterHit;
4391 int splitterHitOffset;
4392 int columnHit = state->HitTestH( x, &splitterHit, &splitterHitOffset );
4393 int splitterX = x - splitterHitOffset;
4394
4395 if ( m_dragStatus > 0 )
4396 {
4397 if ( x > (m_marginWidth + wxPG_DRAG_MARGIN) &&
4398 x < (m_pState->m_width - wxPG_DRAG_MARGIN) )
4399 {
4400
4401 int newSplitterX = x - m_dragOffset;
4402 int splitterX = x - splitterHitOffset;
4403
4404 // Splitter redraw required?
4405 if ( newSplitterX != splitterX )
4406 {
4407 // Move everything
4408 SetInternalFlag(wxPG_FL_DONT_CENTER_SPLITTER);
4409 state->DoSetSplitterPosition( newSplitterX, m_draggedSplitter, false );
4410 state->m_fSplitterX = (float) newSplitterX;
4411
4412 if ( GetSelection() )
4413 CorrectEditorWidgetSizeX();
4414
4415 Update();
4416 Refresh();
4417 }
4418
4419 m_dragStatus = 2;
4420 }
4421
4422 return false;
4423 }
4424 else
4425 {
4426
4427 int ih = m_lineHeight;
4428 int sy = y;
4429
4430 #if wxPG_SUPPORT_TOOLTIPS
4431 wxPGProperty* prevHover = m_propHover;
4432 unsigned char prevSide = m_mouseSide;
4433 #endif
4434 int curPropHoverY = y - (y % ih);
4435
4436 // On which item it hovers
4437 if ( !m_propHover
4438 ||
4439 ( sy < m_propHoverY || sy >= (m_propHoverY+ih) )
4440 )
4441 {
4442 // Mouse moves on another property
4443
4444 m_propHover = DoGetItemAtY(y);
4445 m_propHoverY = curPropHoverY;
4446
4447 // Send hover event
4448 SendEvent( wxEVT_PG_HIGHLIGHTED, m_propHover );
4449 }
4450
4451 #if wxPG_SUPPORT_TOOLTIPS
4452 // Store which side we are on
4453 m_mouseSide = 0;
4454 if ( columnHit == 1 )
4455 m_mouseSide = 2;
4456 else if ( columnHit == 0 )
4457 m_mouseSide = 1;
4458
4459 //
4460 // If tooltips are enabled, show label or value as a tip
4461 // in case it doesn't otherwise show in full length.
4462 //
4463 if ( m_windowStyle & wxPG_TOOLTIPS )
4464 {
4465 wxToolTip* tooltip = m_canvas->GetToolTip();
4466
4467 if ( m_propHover != prevHover || prevSide != m_mouseSide )
4468 {
4469 if ( m_propHover && !m_propHover->IsCategory() )
4470 {
4471
4472 if ( GetExtraStyle() & wxPG_EX_HELP_AS_TOOLTIPS )
4473 {
4474 // Show help string as a tooltip
4475 wxString tipString = m_propHover->GetHelpString();
4476
4477 SetToolTip(tipString);
4478 }
4479 else
4480 {
4481 // Show cropped value string as a tooltip
4482 wxString tipString;
4483 int space = 0;
4484
4485 if ( m_mouseSide == 1 )
4486 {
4487 tipString = m_propHover->m_label;
4488 space = splitterX-m_marginWidth-3;
4489 }
4490 else if ( m_mouseSide == 2 )
4491 {
4492 tipString = m_propHover->GetDisplayedString();
4493
4494 space = m_width - splitterX;
4495 if ( m_propHover->m_flags & wxPG_PROP_CUSTOMIMAGE )
4496 space -= wxPG_CUSTOM_IMAGE_WIDTH + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2;
4497 }
4498
4499 if ( space )
4500 {
4501 int tw, th;
4502 GetTextExtent( tipString, &tw, &th, 0, 0 );
4503 if ( tw > space )
4504 {
4505 SetToolTip( tipString );
4506 }
4507 }
4508 else
4509 {
4510 if ( tooltip )
4511 {
4512 #if wxPG_ALLOW_EMPTY_TOOLTIPS
4513 m_canvas->SetToolTip( m_emptyString );
4514 #else
4515 m_canvas->SetToolTip( NULL );
4516 #endif
4517 }
4518 }
4519
4520 }
4521 }
4522 else
4523 {
4524 if ( tooltip )
4525 {
4526 #if wxPG_ALLOW_EMPTY_TOOLTIPS
4527 m_canvas->SetToolTip( m_emptyString );
4528 #else
4529 m_canvas->SetToolTip( NULL );
4530 #endif
4531 }
4532 }
4533 }
4534 }
4535 #endif
4536
4537 if ( splitterHit == -1 ||
4538 !m_propHover ||
4539 HasFlag(wxPG_STATIC_SPLITTER) )
4540 {
4541 // hovering on something else
4542 if ( m_curcursor != wxCURSOR_ARROW )
4543 CustomSetCursor( wxCURSOR_ARROW );
4544 }
4545 else
4546 {
4547 // Do not allow splitter cursor on caption items.
4548 // (also not if we were dragging and its started
4549 // outside the splitter region)
4550
4551 if ( !m_propHover->IsCategory() &&
4552 !event.Dragging() )
4553 {
4554
4555 // hovering on splitter
4556
4557 // NB: Condition disabled since MouseLeave event (from the editor control) cannot be
4558 // reliably detected.
4559 //if ( m_curcursor != wxCURSOR_SIZEWE )
4560 CustomSetCursor( wxCURSOR_SIZEWE, true );
4561
4562 return false;
4563 }
4564 else
4565 {
4566 // hovering on something else
4567 if ( m_curcursor != wxCURSOR_ARROW )
4568 CustomSetCursor( wxCURSOR_ARROW );
4569 }
4570 }
4571
4572 //
4573 // Multi select by dragging
4574 //
4575 if ( GetExtraStyle() & wxPG_EX_MULTIPLE_SELECTION &&
4576 event.LeftIsDown() &&
4577 m_propHover &&
4578 GetSelection() &&
4579 !state->DoIsPropertySelected(m_propHover) )
4580 {
4581 DoAddToSelection(m_propHover);
4582 }
4583 }
4584 return true;
4585 }
4586
4587 // -----------------------------------------------------------------------
4588
4589 // Also handles Leaving event
4590 bool wxPropertyGrid::HandleMouseUp( int x, unsigned int WXUNUSED(y),
4591 wxMouseEvent &WXUNUSED(event) )
4592 {
4593 wxPropertyGridPageState* state = m_pState;
4594 bool res = false;
4595
4596 int splitterHit;
4597 int splitterHitOffset;
4598 state->HitTestH( x, &splitterHit, &splitterHitOffset );
4599
4600 // No event type check - basicly calling this method should
4601 // just stop dragging.
4602 // Left up after dragged?
4603 if ( m_dragStatus >= 1 )
4604 {
4605 //
4606 // End Splitter Dragging
4607 //
4608 // DO NOT ENABLE FOLLOWING LINE!
4609 // (it is only here as a reminder to not to do it)
4610 //splitterX = x;
4611
4612 // Disable splitter auto-centering
4613 m_iFlags |= wxPG_FL_DONT_CENTER_SPLITTER;
4614
4615 // This is necessary to return cursor
4616 if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
4617 {
4618 m_canvas->ReleaseMouse();
4619 m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED);
4620 }
4621
4622 // Set back the default cursor, if necessary
4623 if ( splitterHit == -1 ||
4624 !m_propHover )
4625 {
4626 CustomSetCursor( wxCURSOR_ARROW );
4627 }
4628
4629 m_dragStatus = 0;
4630
4631 // Control background needs to be cleared
4632 wxPGProperty* selected = GetSelection();
4633 if ( !(m_iFlags & wxPG_FL_PRIMARY_FILLS_ENTIRE) && selected )
4634 DrawItem( selected );
4635
4636 if ( m_wndEditor )
4637 {
4638 m_wndEditor->Show ( true );
4639 }
4640
4641 #if wxPG_REFRESH_CONTROLS_AFTER_REPAINT
4642 // Fixes button disappearance bug
4643 if ( m_wndEditor2 )
4644 m_wndEditor2->Show ( true );
4645 #endif
4646
4647 // This clears the focus.
4648 m_editorFocused = 0;
4649
4650 }
4651 return res;
4652 }
4653
4654 // -----------------------------------------------------------------------
4655
4656 bool wxPropertyGrid::OnMouseCommon( wxMouseEvent& event, int* px, int* py )
4657 {
4658 int splitterX = GetSplitterPosition();
4659
4660 //int ux, uy;
4661 //CalcUnscrolledPosition( event.m_x, event.m_y, &ux, &uy );
4662 int ux = event.m_x;
4663 int uy = event.m_y;
4664
4665 wxWindow* wnd = GetEditorControl();
4666
4667 // Hide popup on clicks
4668 if ( event.GetEventType() != wxEVT_MOTION )
4669 if ( wnd && wnd->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)) )
4670 {
4671 ((wxOwnerDrawnComboBox*)wnd)->HidePopup();
4672 }
4673
4674 wxRect r;
4675 if ( wnd )
4676 r = wnd->GetRect();
4677 if ( wnd == NULL || m_dragStatus ||
4678 (
4679 ux <= (splitterX + wxPG_SPLITTERX_DETECTMARGIN2) ||
4680 ux >= (r.x+r.width) ||
4681 event.m_y < r.y ||
4682 event.m_y >= (r.y+r.height)
4683 )
4684 )
4685 {
4686 *px = ux;
4687 *py = uy;
4688 return true;
4689 }
4690 else
4691 {
4692 if ( m_curcursor != wxCURSOR_ARROW ) CustomSetCursor ( wxCURSOR_ARROW );
4693 }
4694 return false;
4695 }
4696
4697 // -----------------------------------------------------------------------
4698
4699 void wxPropertyGrid::OnMouseClick( wxMouseEvent &event )
4700 {
4701 int x, y;
4702 if ( OnMouseCommon( event, &x, &y ) )
4703 {
4704 HandleMouseClick(x,y,event);
4705 }
4706 event.Skip();
4707 }
4708
4709 // -----------------------------------------------------------------------
4710
4711 void wxPropertyGrid::OnMouseRightClick( wxMouseEvent &event )
4712 {
4713 int x, y;
4714 CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
4715 HandleMouseRightClick(x,y,event);
4716 event.Skip();
4717 }
4718
4719 // -----------------------------------------------------------------------
4720
4721 void wxPropertyGrid::OnMouseDoubleClick( wxMouseEvent &event )
4722 {
4723 // Always run standard mouse-down handler as well
4724 OnMouseClick(event);
4725
4726 int x, y;
4727 CalcUnscrolledPosition( event.m_x, event.m_y, &x, &y );
4728 HandleMouseDoubleClick(x,y,event);
4729 event.Skip();
4730 }
4731
4732 // -----------------------------------------------------------------------
4733
4734 void wxPropertyGrid::OnMouseMove( wxMouseEvent &event )
4735 {
4736 int x, y;
4737 if ( OnMouseCommon( event, &x, &y ) )
4738 {
4739 HandleMouseMove(x,y,event);
4740 }
4741 event.Skip();
4742 }
4743
4744 // -----------------------------------------------------------------------
4745
4746 void wxPropertyGrid::OnMouseMoveBottom( wxMouseEvent& WXUNUSED(event) )
4747 {
4748 // Called when mouse moves in the empty space below the properties.
4749 CustomSetCursor( wxCURSOR_ARROW );
4750 }
4751
4752 // -----------------------------------------------------------------------
4753
4754 void wxPropertyGrid::OnMouseUp( wxMouseEvent &event )
4755 {
4756 int x, y;
4757 if ( OnMouseCommon( event, &x, &y ) )
4758 {
4759 HandleMouseUp(x,y,event);
4760 }
4761 event.Skip();
4762 }
4763
4764 // -----------------------------------------------------------------------
4765
4766 void wxPropertyGrid::OnMouseEntry( wxMouseEvent &event )
4767 {
4768 // This may get called from child control as well, so event's
4769 // mouse position cannot be relied on.
4770
4771 if ( event.Entering() )
4772 {
4773 if ( !(m_iFlags & wxPG_FL_MOUSE_INSIDE) )
4774 {
4775 // TODO: Fix this (detect parent and only do
4776 // cursor trick if it is a manager).
4777 wxASSERT( GetParent() );
4778 GetParent()->SetCursor(wxNullCursor);
4779
4780 m_iFlags |= wxPG_FL_MOUSE_INSIDE;
4781 }
4782 else
4783 GetParent()->SetCursor(wxNullCursor);
4784 }
4785 else if ( event.Leaving() )
4786 {
4787 // Without this, wxSpinCtrl editor will sometimes have wrong cursor
4788 m_canvas->SetCursor( wxNullCursor );
4789
4790 // Get real cursor position
4791 wxPoint pt = ScreenToClient(::wxGetMousePosition());
4792
4793 if ( ( pt.x <= 0 || pt.y <= 0 || pt.x >= m_width || pt.y >= m_height ) )
4794 {
4795 {
4796 if ( (m_iFlags & wxPG_FL_MOUSE_INSIDE) )
4797 {
4798 m_iFlags &= ~(wxPG_FL_MOUSE_INSIDE);
4799 }
4800
4801 if ( m_dragStatus )
4802 wxPropertyGrid::HandleMouseUp ( -1, 10000, event );
4803 }
4804 }
4805 }
4806
4807 event.Skip();
4808 }
4809
4810 // -----------------------------------------------------------------------
4811
4812 // Common code used by various OnMouseXXXChild methods.
4813 bool wxPropertyGrid::OnMouseChildCommon( wxMouseEvent &event, int* px, int *py )
4814 {
4815 wxWindow* topCtrlWnd = (wxWindow*)event.GetEventObject();
4816 wxASSERT( topCtrlWnd );
4817 int x, y;
4818 event.GetPosition(&x,&y);
4819
4820 int splitterX = GetSplitterPosition();
4821
4822 wxRect r = topCtrlWnd->GetRect();
4823 if ( !m_dragStatus &&
4824 x > (splitterX-r.x+wxPG_SPLITTERX_DETECTMARGIN2) &&
4825 y >= 0 && y < r.height \
4826 )
4827 {
4828 if ( m_curcursor != wxCURSOR_ARROW ) CustomSetCursor ( wxCURSOR_ARROW );
4829 event.Skip();
4830 }
4831 else
4832 {
4833 CalcUnscrolledPosition( event.m_x + r.x, event.m_y + r.y, \
4834 px, py );
4835 return true;
4836 }
4837 return false;
4838 }
4839
4840 void wxPropertyGrid::OnMouseClickChild( wxMouseEvent &event )
4841 {
4842 int x,y;
4843 if ( OnMouseChildCommon(event,&x,&y) )
4844 {
4845 bool res = HandleMouseClick(x,y,event);
4846 if ( !res ) event.Skip();
4847 }
4848 }
4849
4850 void wxPropertyGrid::OnMouseRightClickChild( wxMouseEvent &event )
4851 {
4852 int x,y;
4853 wxASSERT( m_wndEditor );
4854 // These coords may not be exact (about +-2),
4855 // but that should not matter (right click is about item, not position).
4856 wxPoint pt = m_wndEditor->GetPosition();
4857 CalcUnscrolledPosition( event.m_x + pt.x, event.m_y + pt.y, &x, &y );
4858
4859 // FIXME: Used to set m_propHover to selection here. Was it really
4860 // necessary?
4861
4862 bool res = HandleMouseRightClick(x,y,event);
4863 if ( !res ) event.Skip();
4864 }
4865
4866 void wxPropertyGrid::OnMouseMoveChild( wxMouseEvent &event )
4867 {
4868 int x,y;
4869 if ( OnMouseChildCommon(event,&x,&y) )
4870 {
4871 bool res = HandleMouseMove(x,y,event);
4872 if ( !res ) event.Skip();
4873 }
4874 }
4875
4876 void wxPropertyGrid::OnMouseUpChild( wxMouseEvent &event )
4877 {
4878 int x,y;
4879 if ( OnMouseChildCommon(event,&x,&y) )
4880 {
4881 bool res = HandleMouseUp(x,y,event);
4882 if ( !res ) event.Skip();
4883 }
4884 }
4885
4886 // -----------------------------------------------------------------------
4887 // wxPropertyGrid keyboard event handling
4888 // -----------------------------------------------------------------------
4889
4890 int wxPropertyGrid::KeyEventToActions(wxKeyEvent &event, int* pSecond) const
4891 {
4892 // Translates wxKeyEvent to wxPG_ACTION_XXX
4893
4894 int keycode = event.GetKeyCode();
4895 int modifiers = event.GetModifiers();
4896
4897 wxASSERT( !(modifiers&~(0xFFFF)) );
4898
4899 int hashMapKey = (keycode & 0xFFFF) | ((modifiers & 0xFFFF) << 16);
4900
4901 wxPGHashMapI2I::const_iterator it = m_actionTriggers.find(hashMapKey);
4902
4903 if ( it == m_actionTriggers.end() )
4904 return 0;
4905
4906 if ( pSecond )
4907 {
4908 int second = (it->second>>16) & 0xFFFF;
4909 *pSecond = second;
4910 }
4911
4912 return (it->second & 0xFFFF);
4913 }
4914
4915 void wxPropertyGrid::AddActionTrigger( int action, int keycode, int modifiers )
4916 {
4917 wxASSERT( !(modifiers&~(0xFFFF)) );
4918
4919 int hashMapKey = (keycode & 0xFFFF) | ((modifiers & 0xFFFF) << 16);
4920
4921 wxPGHashMapI2I::iterator it = m_actionTriggers.find(hashMapKey);
4922
4923 if ( it != m_actionTriggers.end() )
4924 {
4925 // This key combination is already used
4926
4927 // Can add secondary?
4928 wxASSERT_MSG( !(it->second&~(0xFFFF)),
4929 wxT("You can only add up to two separate actions per key combination.") );
4930
4931 action = it->second | (action<<16);
4932 }
4933
4934 m_actionTriggers[hashMapKey] = action;
4935 }
4936
4937 void wxPropertyGrid::ClearActionTriggers( int action )
4938 {
4939 wxPGHashMapI2I::iterator it;
4940
4941 for ( it = m_actionTriggers.begin(); it != m_actionTriggers.end(); ++it )
4942 {
4943 if ( it->second == action )
4944 {
4945 m_actionTriggers.erase(it);
4946 }
4947 }
4948 }
4949
4950 void wxPropertyGrid::HandleKeyEvent( wxKeyEvent &event, bool fromChild )
4951 {
4952 //
4953 // Handles key event when editor control is not focused.
4954 //
4955
4956 wxCHECK2(!m_frozen, return);
4957
4958 // Travelsal between items, collapsing/expanding, etc.
4959 wxPGProperty* selected = GetSelection();
4960 int keycode = event.GetKeyCode();
4961 bool editorFocused = IsEditorFocused();
4962
4963 if ( keycode == WXK_TAB )
4964 {
4965 wxWindow* mainControl;
4966
4967 if ( HasInternalFlag(wxPG_FL_IN_MANAGER) )
4968 mainControl = GetParent();
4969 else
4970 mainControl = this;
4971
4972 if ( !event.ShiftDown() )
4973 {
4974 if ( !editorFocused && m_wndEditor )
4975 {
4976 DoSelectProperty( selected, wxPG_SEL_FOCUS );
4977 }
4978 else
4979 {
4980 // Tab traversal workaround for platforms on which
4981 // wxWindow::Navigate() may navigate into first child
4982 // instead of next sibling. Does not work perfectly
4983 // in every scenario (for instance, when property grid
4984 // is either first or last control).
4985 #if defined(__WXGTK__)
4986 wxWindow* sibling = mainControl->GetNextSibling();
4987 if ( sibling )
4988 sibling->SetFocusFromKbd();
4989 #else
4990 Navigate(wxNavigationKeyEvent::IsForward);
4991 #endif
4992 }
4993 }
4994 else
4995 {
4996 if ( editorFocused )
4997 {
4998 UnfocusEditor();
4999 }
5000 else
5001 {
5002 #if defined(__WXGTK__)
5003 wxWindow* sibling = mainControl->GetPrevSibling();
5004 if ( sibling )
5005 sibling->SetFocusFromKbd();
5006 #else
5007 Navigate(wxNavigationKeyEvent::IsBackward);
5008 #endif
5009 }
5010 }
5011
5012 return;
5013 }
5014
5015 // Ignore Alt and Control when they are down alone
5016 if ( keycode == WXK_ALT ||
5017 keycode == WXK_CONTROL )
5018 {
5019 event.Skip();
5020 return;
5021 }
5022
5023 int secondAction;
5024 int action = KeyEventToActions(event, &secondAction);
5025
5026 if ( editorFocused && action == wxPG_ACTION_CANCEL_EDIT )
5027 {
5028 //
5029 // Esc cancels any changes
5030 if ( IsEditorsValueModified() )
5031 {
5032 EditorsValueWasNotModified();
5033
5034 // Update the control as well
5035 selected->GetEditorClass()->
5036 SetControlStringValue( selected,
5037 GetEditorControl(),
5038 selected->GetDisplayedString() );
5039 }
5040
5041 OnValidationFailureReset(selected);
5042
5043 UnfocusEditor();
5044 return;
5045 }
5046
5047 // Except for TAB and ESC, handle child control events in child control
5048 if ( fromChild )
5049 {
5050 // Only propagate event if it had modifiers
5051 if ( !event.HasModifiers() )
5052 {
5053 event.StopPropagation();
5054 }
5055 event.Skip();
5056 return;
5057 }
5058
5059 bool wasHandled = false;
5060
5061 if ( selected )
5062 {
5063 // Show dialog?
5064 if ( ButtonTriggerKeyTest(action, event) )
5065 return;
5066
5067 wxPGProperty* p = selected;
5068
5069 // Travel and expand/collapse
5070 int selectDir = -2;
5071
5072 if ( p->GetChildCount() )
5073 {
5074 if ( action == wxPG_ACTION_COLLAPSE_PROPERTY || secondAction == wxPG_ACTION_COLLAPSE_PROPERTY )
5075 {
5076 if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Collapse(p) )
5077 wasHandled = true;
5078 }
5079 else if ( action == wxPG_ACTION_EXPAND_PROPERTY || secondAction == wxPG_ACTION_EXPAND_PROPERTY )
5080 {
5081 if ( (m_windowStyle & wxPG_HIDE_MARGIN) || Expand(p) )
5082 wasHandled = true;
5083 }
5084 }
5085
5086 if ( !wasHandled )
5087 {
5088 if ( action == wxPG_ACTION_PREV_PROPERTY || secondAction == wxPG_ACTION_PREV_PROPERTY )
5089 {
5090 selectDir = -1;
5091 }
5092 else if ( action == wxPG_ACTION_NEXT_PROPERTY || secondAction == wxPG_ACTION_NEXT_PROPERTY )
5093 {
5094 selectDir = 1;
5095 }
5096 }
5097
5098 if ( selectDir >= -1 )
5099 {
5100 p = wxPropertyGridIterator::OneStep( m_pState, wxPG_ITERATE_VISIBLE, p, selectDir );
5101 if ( p )
5102 DoSelectProperty(p);
5103 wasHandled = true;
5104 }
5105 }
5106 else
5107 {
5108 // If nothing was selected, select the first item now
5109 // (or navigate out of tab).
5110 if ( action != wxPG_ACTION_CANCEL_EDIT && secondAction != wxPG_ACTION_CANCEL_EDIT )
5111 {
5112 wxPGProperty* p = wxPropertyGridInterface::GetFirst();
5113 if ( p ) DoSelectProperty(p);
5114 wasHandled = true;
5115 }
5116 }
5117
5118 if ( !wasHandled )
5119 event.Skip();
5120 }
5121
5122 // -----------------------------------------------------------------------
5123
5124 void wxPropertyGrid::OnKey( wxKeyEvent &event )
5125 {
5126 // If there was editor open and focused, then this event should not
5127 // really be processed here.
5128 if ( IsEditorFocused() )
5129 {
5130 // However, if event had modifiers, it is probably still best
5131 // to skip it.
5132 if ( event.HasModifiers() )
5133 event.Skip();
5134 else
5135 event.StopPropagation();
5136 return;
5137 }
5138
5139 HandleKeyEvent(event, false);
5140 }
5141
5142 // -----------------------------------------------------------------------
5143
5144 bool wxPropertyGrid::ButtonTriggerKeyTest( int action, wxKeyEvent& event )
5145 {
5146 if ( action == -1 )
5147 {
5148 int secondAction;
5149 action = KeyEventToActions(event, &secondAction);
5150 }
5151
5152 // Does the keycode trigger button?
5153 if ( action == wxPG_ACTION_PRESS_BUTTON &&
5154 m_wndEditor2 )
5155 {
5156 wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, m_wndEditor2->GetId());
5157 GetEventHandler()->AddPendingEvent(evt);
5158 return true;
5159 }
5160
5161 return false;
5162 }
5163
5164 // -----------------------------------------------------------------------
5165
5166 void wxPropertyGrid::OnChildKeyDown( wxKeyEvent &event )
5167 {
5168 HandleKeyEvent(event, true);
5169 }
5170
5171 // -----------------------------------------------------------------------
5172 // wxPropertyGrid miscellaneous event handling
5173 // -----------------------------------------------------------------------
5174
5175 void wxPropertyGrid::OnIdle( wxIdleEvent& WXUNUSED(event) )
5176 {
5177 //
5178 // Check if the focus is in this control or one of its children
5179 wxWindow* newFocused = wxWindow::FindFocus();
5180
5181 if ( newFocused != m_curFocused )
5182 HandleFocusChange( newFocused );
5183
5184 //
5185 // Check if top-level parent has changed
5186 wxWindow* tlp = ::wxGetTopLevelParent(this);
5187 if ( tlp != m_tlp )
5188 {
5189 OnTLPChanging(tlp);
5190 }
5191 }
5192
5193 bool wxPropertyGrid::IsEditorFocused() const
5194 {
5195 wxWindow* focus = wxWindow::FindFocus();
5196
5197 if ( focus == m_wndEditor || focus == m_wndEditor2 ||
5198 focus == GetEditorControl() )
5199 return true;
5200
5201 return false;
5202 }
5203
5204 // Called by focus event handlers. newFocused is the window that becomes focused.
5205 void wxPropertyGrid::HandleFocusChange( wxWindow* newFocused )
5206 {
5207 unsigned int oldFlags = m_iFlags;
5208
5209 m_iFlags &= ~(wxPG_FL_FOCUSED);
5210
5211 wxWindow* parent = newFocused;
5212
5213 // This must be one of nextFocus' parents.
5214 while ( parent )
5215 {
5216 // Use m_eventObject, which is either wxPropertyGrid or
5217 // wxPropertyGridManager, as appropriate.
5218 if ( parent == m_eventObject )
5219 {
5220 m_iFlags |= wxPG_FL_FOCUSED;
5221 break;
5222 }
5223 parent = parent->GetParent();
5224 }
5225
5226 m_curFocused = newFocused;
5227
5228 if ( (m_iFlags & wxPG_FL_FOCUSED) !=
5229 (oldFlags & wxPG_FL_FOCUSED) )
5230 {
5231 if ( !(m_iFlags & wxPG_FL_FOCUSED) )
5232 {
5233 // Need to store changed value
5234 CommitChangesFromEditor();
5235 }
5236 else
5237 {
5238 /*
5239 //
5240 // Preliminary code for tab-order respecting
5241 // tab-traversal (but should be moved to
5242 // OnNav handler)
5243 //
5244 wxWindow* prevFocus = event.GetWindow();
5245 wxWindow* useThis = this;
5246 if ( m_iFlags & wxPG_FL_IN_MANAGER )
5247 useThis = GetParent();
5248
5249 if ( prevFocus &&
5250 prevFocus->GetParent() == useThis->GetParent() )
5251 {
5252 wxList& children = useThis->GetParent()->GetChildren();
5253
5254 wxNode* node = children.Find(prevFocus);
5255
5256 if ( node->GetNext() &&
5257 useThis == node->GetNext()->GetData() )
5258 DoSelectProperty(GetFirst());
5259 else if ( node->GetPrevious () &&
5260 useThis == node->GetPrevious()->GetData() )
5261 DoSelectProperty(GetLastProperty());
5262
5263 }
5264 */
5265 }
5266
5267 // Redraw selected
5268 wxPGProperty* selected = GetSelection();
5269 if ( selected && (m_iFlags & wxPG_FL_INITIALIZED) )
5270 DrawItem( selected );
5271 }
5272 }
5273
5274 void wxPropertyGrid::OnFocusEvent( wxFocusEvent& event )
5275 {
5276 if ( event.GetEventType() == wxEVT_SET_FOCUS )
5277 HandleFocusChange((wxWindow*)event.GetEventObject());
5278 // Line changed to "else" when applying wxPropertyGrid patch #1675902
5279 //else if ( event.GetWindow() )
5280 else
5281 HandleFocusChange(event.GetWindow());
5282
5283 event.Skip();
5284 }
5285
5286 // -----------------------------------------------------------------------
5287
5288 void wxPropertyGrid::OnChildFocusEvent( wxChildFocusEvent& event )
5289 {
5290 HandleFocusChange((wxWindow*)event.GetEventObject());
5291 event.Skip();
5292 }
5293
5294 // -----------------------------------------------------------------------
5295
5296 void wxPropertyGrid::OnScrollEvent( wxScrollWinEvent &event )
5297 {
5298 m_iFlags |= wxPG_FL_SCROLLED;
5299
5300 event.Skip();
5301 }
5302
5303 // -----------------------------------------------------------------------
5304
5305 void wxPropertyGrid::OnCaptureChange( wxMouseCaptureChangedEvent& WXUNUSED(event) )
5306 {
5307 if ( m_iFlags & wxPG_FL_MOUSE_CAPTURED )
5308 {
5309 m_iFlags &= ~(wxPG_FL_MOUSE_CAPTURED);
5310 }
5311 }
5312
5313 // -----------------------------------------------------------------------
5314 // Property editor related functions
5315 // -----------------------------------------------------------------------
5316
5317 // noDefCheck = true prevents infinite recursion.
5318 wxPGEditor* wxPropertyGrid::DoRegisterEditorClass( wxPGEditor* editorClass,
5319 const wxString& editorName,
5320 bool noDefCheck )
5321 {
5322 wxASSERT( editorClass );
5323
5324 if ( !noDefCheck && wxPGGlobalVars->m_mapEditorClasses.empty() )
5325 RegisterDefaultEditors();
5326
5327 wxString name = editorName;
5328 if ( name.length() == 0 )
5329 name = editorClass->GetName();
5330
5331 // Existing editor under this name?
5332 wxPGHashMapS2P::iterator vt_it = wxPGGlobalVars->m_mapEditorClasses.find(name);
5333
5334 if ( vt_it != wxPGGlobalVars->m_mapEditorClasses.end() )
5335 {
5336 // If this name was already used, try class name.
5337 name = editorClass->GetClassInfo()->GetClassName();
5338 vt_it = wxPGGlobalVars->m_mapEditorClasses.find(name);
5339 }
5340
5341 wxCHECK_MSG( vt_it == wxPGGlobalVars->m_mapEditorClasses.end(),
5342 (wxPGEditor*) vt_it->second,
5343 "Editor with given name was already registered" );
5344
5345 wxPGGlobalVars->m_mapEditorClasses[name] = (void*)editorClass;
5346
5347 return editorClass;
5348 }
5349
5350 // Use this in RegisterDefaultEditors.
5351 #define wxPGRegisterDefaultEditorClass(EDITOR) \
5352 if ( wxPGEditor_##EDITOR == NULL ) \
5353 { \
5354 wxPGEditor_##EDITOR = wxPropertyGrid::RegisterEditorClass( \
5355 new wxPG##EDITOR##Editor, true ); \
5356 }
5357
5358 // Registers all default editor classes
5359 void wxPropertyGrid::RegisterDefaultEditors()
5360 {
5361 wxPGRegisterDefaultEditorClass( TextCtrl );
5362 wxPGRegisterDefaultEditorClass( Choice );
5363 wxPGRegisterDefaultEditorClass( ComboBox );
5364 wxPGRegisterDefaultEditorClass( TextCtrlAndButton );
5365 #if wxPG_INCLUDE_CHECKBOX
5366 wxPGRegisterDefaultEditorClass( CheckBox );
5367 #endif
5368 wxPGRegisterDefaultEditorClass( ChoiceAndButton );
5369
5370 // Register SpinCtrl etc. editors before use
5371 RegisterAdditionalEditors();
5372 }
5373
5374 // -----------------------------------------------------------------------
5375 // wxPGStringTokenizer
5376 // Needed to handle C-style string lists (e.g. "str1" "str2")
5377 // -----------------------------------------------------------------------
5378
5379 wxPGStringTokenizer::wxPGStringTokenizer( const wxString& str, wxChar delimeter )
5380 : m_str(&str), m_curPos(str.begin()), m_delimeter(delimeter)
5381 {
5382 }
5383
5384 wxPGStringTokenizer::~wxPGStringTokenizer()
5385 {
5386 }
5387
5388 bool wxPGStringTokenizer::HasMoreTokens()
5389 {
5390 const wxString& str = *m_str;
5391
5392 wxString::const_iterator i = m_curPos;
5393
5394 wxUniChar delim = m_delimeter;
5395 wxUniChar a;
5396 wxUniChar prev_a = wxT('\0');
5397
5398 bool inToken = false;
5399
5400 while ( i != str.end() )
5401 {
5402 a = *i;
5403
5404 if ( !inToken )
5405 {
5406 if ( a == delim )
5407 {
5408 inToken = true;
5409 m_readyToken.clear();
5410 }
5411 }
5412 else
5413 {
5414 if ( prev_a != wxT('\\') )
5415 {
5416 if ( a != delim )
5417 {
5418 if ( a != wxT('\\') )
5419 m_readyToken << a;
5420 }
5421 else
5422 {
5423 ++i;
5424 m_curPos = i;
5425 return true;
5426 }
5427 prev_a = a;
5428 }
5429 else
5430 {
5431 m_readyToken << a;
5432 prev_a = wxT('\0');
5433 }
5434 }
5435 ++i;
5436 }
5437
5438 m_curPos = str.end();
5439
5440 if ( inToken )
5441 return true;
5442
5443 return false;
5444 }
5445
5446 wxString wxPGStringTokenizer::GetNextToken()
5447 {
5448 return m_readyToken;
5449 }
5450
5451 // -----------------------------------------------------------------------
5452 // wxPGChoiceEntry
5453 // -----------------------------------------------------------------------
5454
5455 wxPGChoiceEntry::wxPGChoiceEntry()
5456 : wxPGCell(), m_value(wxPG_INVALID_VALUE)
5457 {
5458 }
5459
5460 // -----------------------------------------------------------------------
5461 // wxPGChoicesData
5462 // -----------------------------------------------------------------------
5463
5464 wxPGChoicesData::wxPGChoicesData()
5465 {
5466 }
5467
5468 wxPGChoicesData::~wxPGChoicesData()
5469 {
5470 Clear();
5471 }
5472
5473 void wxPGChoicesData::Clear()
5474 {
5475 m_items.clear();
5476 }
5477
5478 void wxPGChoicesData::CopyDataFrom( wxPGChoicesData* data )
5479 {
5480 wxASSERT( m_items.size() == 0 );
5481
5482 m_items = data->m_items;
5483 }
5484
5485 wxPGChoiceEntry& wxPGChoicesData::Insert( int index,
5486 const wxPGChoiceEntry& item )
5487 {
5488 wxVector<wxPGChoiceEntry>::iterator it;
5489 if ( index == -1 )
5490 {
5491 it = m_items.end();
5492 index = (int) m_items.size();
5493 }
5494 else
5495 {
5496 it = m_items.begin() + index;
5497 }
5498
5499 m_items.insert(it, item);
5500
5501 wxPGChoiceEntry& ownEntry = m_items[index];
5502
5503 // Need to fix value?
5504 if ( ownEntry.GetValue() == wxPG_INVALID_VALUE )
5505 ownEntry.SetValue(index);
5506
5507 return ownEntry;
5508 }
5509
5510 // -----------------------------------------------------------------------
5511 // wxPropertyGridEvent
5512 // -----------------------------------------------------------------------
5513
5514 IMPLEMENT_DYNAMIC_CLASS(wxPropertyGridEvent, wxCommandEvent)
5515
5516
5517 wxDEFINE_EVENT( wxEVT_PG_SELECTED, wxPropertyGridEvent );
5518 wxDEFINE_EVENT( wxEVT_PG_CHANGING, wxPropertyGridEvent );
5519 wxDEFINE_EVENT( wxEVT_PG_CHANGED, wxPropertyGridEvent );
5520 wxDEFINE_EVENT( wxEVT_PG_HIGHLIGHTED, wxPropertyGridEvent );
5521 wxDEFINE_EVENT( wxEVT_PG_RIGHT_CLICK, wxPropertyGridEvent );
5522 wxDEFINE_EVENT( wxEVT_PG_PAGE_CHANGED, wxPropertyGridEvent );
5523 wxDEFINE_EVENT( wxEVT_PG_ITEM_EXPANDED, wxPropertyGridEvent );
5524 wxDEFINE_EVENT( wxEVT_PG_ITEM_COLLAPSED, wxPropertyGridEvent );
5525 wxDEFINE_EVENT( wxEVT_PG_DOUBLE_CLICK, wxPropertyGridEvent );
5526
5527
5528 // -----------------------------------------------------------------------
5529
5530 void wxPropertyGridEvent::Init()
5531 {
5532 m_validationInfo = NULL;
5533 m_canVeto = false;
5534 m_wasVetoed = false;
5535 }
5536
5537 // -----------------------------------------------------------------------
5538
5539 wxPropertyGridEvent::wxPropertyGridEvent(wxEventType commandType, int id)
5540 : wxCommandEvent(commandType,id)
5541 {
5542 m_property = NULL;
5543 Init();
5544 }
5545
5546 // -----------------------------------------------------------------------
5547
5548 wxPropertyGridEvent::wxPropertyGridEvent(const wxPropertyGridEvent& event)
5549 : wxCommandEvent(event)
5550 {
5551 m_eventType = event.GetEventType();
5552 m_eventObject = event.m_eventObject;
5553 m_pg = event.m_pg;
5554 m_property = event.m_property;
5555 m_validationInfo = event.m_validationInfo;
5556 m_canVeto = event.m_canVeto;
5557 m_wasVetoed = event.m_wasVetoed;
5558 }
5559
5560 // -----------------------------------------------------------------------
5561
5562 wxPropertyGridEvent::~wxPropertyGridEvent()
5563 {
5564 }
5565
5566 // -----------------------------------------------------------------------
5567
5568 wxEvent* wxPropertyGridEvent::Clone() const
5569 {
5570 return new wxPropertyGridEvent( *this );
5571 }
5572
5573 // -----------------------------------------------------------------------
5574 // wxPropertyGridPopulator
5575 // -----------------------------------------------------------------------
5576
5577 wxPropertyGridPopulator::wxPropertyGridPopulator()
5578 {
5579 m_state = NULL;
5580 m_pg = NULL;
5581 wxPGGlobalVars->m_offline++;
5582 }
5583
5584 // -----------------------------------------------------------------------
5585
5586 void wxPropertyGridPopulator::SetState( wxPropertyGridPageState* state )
5587 {
5588 m_state = state;
5589 m_propHierarchy.clear();
5590 }
5591
5592 // -----------------------------------------------------------------------
5593
5594 void wxPropertyGridPopulator::SetGrid( wxPropertyGrid* pg )
5595 {
5596 m_pg = pg;
5597 pg->Freeze();
5598 }
5599
5600 // -----------------------------------------------------------------------
5601
5602 wxPropertyGridPopulator::~wxPropertyGridPopulator()
5603 {
5604 //
5605 // Free unused sets of choices
5606 wxPGHashMapS2P::iterator it;
5607
5608 for( it = m_dictIdChoices.begin(); it != m_dictIdChoices.end(); ++it )
5609 {
5610 wxPGChoicesData* data = (wxPGChoicesData*) it->second;
5611 data->DecRef();
5612 }
5613
5614 if ( m_pg )
5615 {
5616 m_pg->Thaw();
5617 m_pg->GetPanel()->Refresh();
5618 }
5619 wxPGGlobalVars->m_offline--;
5620 }
5621
5622 // -----------------------------------------------------------------------
5623
5624 wxPGProperty* wxPropertyGridPopulator::Add( const wxString& propClass,
5625 const wxString& propLabel,
5626 const wxString& propName,
5627 const wxString* propValue,
5628 wxPGChoices* pChoices )
5629 {
5630 wxClassInfo* classInfo = wxClassInfo::FindClass(propClass);
5631 wxPGProperty* parent = GetCurParent();
5632
5633 if ( parent->HasFlag(wxPG_PROP_AGGREGATE) )
5634 {
5635 ProcessError(wxString::Format(wxT("new children cannot be added to '%s'"),parent->GetName().c_str()));
5636 return NULL;
5637 }
5638
5639 if ( !classInfo || !classInfo->IsKindOf(CLASSINFO(wxPGProperty)) )
5640 {
5641 ProcessError(wxString::Format(wxT("'%s' is not valid property class"),propClass.c_str()));
5642 return NULL;
5643 }
5644
5645 wxPGProperty* property = (wxPGProperty*) classInfo->CreateObject();
5646
5647 property->SetLabel(propLabel);
5648 property->DoSetName(propName);
5649
5650 if ( pChoices && pChoices->IsOk() )
5651 property->SetChoices(*pChoices);
5652
5653 m_state->DoInsert(parent, -1, property);
5654
5655 if ( propValue )
5656 property->SetValueFromString( *propValue, wxPG_FULL_VALUE|
5657 wxPG_PROGRAMMATIC_VALUE );
5658
5659 return property;
5660 }
5661
5662 // -----------------------------------------------------------------------
5663
5664 void wxPropertyGridPopulator::AddChildren( wxPGProperty* property )
5665 {
5666 m_propHierarchy.push_back(property);
5667 DoScanForChildren();
5668 m_propHierarchy.pop_back();
5669 }
5670
5671 // -----------------------------------------------------------------------
5672
5673 wxPGChoices wxPropertyGridPopulator::ParseChoices( const wxString& choicesString,
5674 const wxString& idString )
5675 {
5676 wxPGChoices choices;
5677
5678 // Using id?
5679 if ( choicesString[0] == wxT('@') )
5680 {
5681 wxString ids = choicesString.substr(1);
5682 wxPGHashMapS2P::iterator it = m_dictIdChoices.find(ids);
5683 if ( it == m_dictIdChoices.end() )
5684 ProcessError(wxString::Format(wxT("No choices defined for id '%s'"),ids.c_str()));
5685 else
5686 choices.AssignData((wxPGChoicesData*)it->second);
5687 }
5688 else
5689 {
5690 bool found = false;
5691 if ( idString.length() )
5692 {
5693 wxPGHashMapS2P::iterator it = m_dictIdChoices.find(idString);
5694 if ( it != m_dictIdChoices.end() )
5695 {
5696 choices.AssignData((wxPGChoicesData*)it->second);
5697 found = true;
5698 }
5699 }
5700
5701 if ( !found )
5702 {
5703 // Parse choices string
5704 wxString::const_iterator it = choicesString.begin();
5705 wxString label;
5706 wxString value;
5707 int state = 0;
5708 bool labelValid = false;
5709
5710 for ( ; it != choicesString.end(); ++it )
5711 {
5712 wxChar c = *it;
5713
5714 if ( state != 1 )
5715 {
5716 if ( c == wxT('"') )
5717 {
5718 if ( labelValid )
5719 {
5720 long l;
5721 if ( !value.ToLong(&l, 0) ) l = wxPG_INVALID_VALUE;
5722 choices.Add(label, l);
5723 }
5724 labelValid = false;
5725 //wxLogDebug(wxT("%s, %s"),label.c_str(),value.c_str());
5726 value.clear();
5727 label.clear();
5728 state = 1;
5729 }
5730 else if ( c == wxT('=') )
5731 {
5732 if ( labelValid )
5733 {
5734 state = 2;
5735 }
5736 }
5737 else if ( state == 2 && (wxIsalnum(c) || c == wxT('x')) )
5738 {
5739 value << c;
5740 }
5741 }
5742 else
5743 {
5744 if ( c == wxT('"') )
5745 {
5746 state = 0;
5747 labelValid = true;
5748 }
5749 else
5750 label << c;
5751 }
5752 }
5753
5754 if ( labelValid )
5755 {
5756 long l;
5757 if ( !value.ToLong(&l, 0) ) l = wxPG_INVALID_VALUE;
5758 choices.Add(label, l);
5759 }
5760
5761 if ( !choices.IsOk() )
5762 {
5763 choices.EnsureData();
5764 }
5765
5766 // Assign to id
5767 if ( idString.length() )
5768 m_dictIdChoices[idString] = choices.GetData();
5769 }
5770 }
5771
5772 return choices;
5773 }
5774
5775 // -----------------------------------------------------------------------
5776
5777 bool wxPropertyGridPopulator::ToLongPCT( const wxString& s, long* pval, long max )
5778 {
5779 if ( s.Last() == wxT('%') )
5780 {
5781 wxString s2 = s.substr(0,s.length()-1);
5782 long val;
5783 if ( s2.ToLong(&val, 10) )
5784 {
5785 *pval = (val*max)/100;
5786 return true;
5787 }
5788 return false;
5789 }
5790
5791 return s.ToLong(pval, 10);
5792 }
5793
5794 // -----------------------------------------------------------------------
5795
5796 bool wxPropertyGridPopulator::AddAttribute( const wxString& name,
5797 const wxString& type,
5798 const wxString& value )
5799 {
5800 int l = m_propHierarchy.size();
5801 if ( !l )
5802 return false;
5803
5804 wxPGProperty* p = m_propHierarchy[l-1];
5805 wxString valuel = value.Lower();
5806 wxVariant variant;
5807
5808 if ( type.length() == 0 )
5809 {
5810 long v;
5811
5812 // Auto-detect type
5813 if ( valuel == wxT("true") || valuel == wxT("yes") || valuel == wxT("1") )
5814 variant = true;
5815 else if ( valuel == wxT("false") || valuel == wxT("no") || valuel == wxT("0") )
5816 variant = false;
5817 else if ( value.ToLong(&v, 0) )
5818 variant = v;
5819 else
5820 variant = value;
5821 }
5822 else
5823 {
5824 if ( type == wxT("string") )
5825 {
5826 variant = value;
5827 }
5828 else if ( type == wxT("int") )
5829 {
5830 long v = 0;
5831 value.ToLong(&v, 0);
5832 variant = v;
5833 }
5834 else if ( type == wxT("bool") )
5835 {
5836 if ( valuel == wxT("true") || valuel == wxT("yes") || valuel == wxT("1") )
5837 variant = true;
5838 else
5839 variant = false;
5840 }
5841 else
5842 {
5843 ProcessError(wxString::Format(wxT("Invalid attribute type '%s'"),type.c_str()));
5844 return false;
5845 }
5846 }
5847
5848 p->SetAttribute( name, variant );
5849
5850 return true;
5851 }
5852
5853 // -----------------------------------------------------------------------
5854
5855 void wxPropertyGridPopulator::ProcessError( const wxString& msg )
5856 {
5857 wxLogError(_("Error in resource: %s"),msg.c_str());
5858 }
5859
5860 // -----------------------------------------------------------------------
5861
5862 #endif // wxUSE_PROPGRID