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