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