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