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