]> git.saurik.com Git - wxWidgets.git/blob - src/propgrid/advprops.cpp
fix wxOS2 build where OpenGL headers are available but there's no wxGlCanvas implemen...
[wxWidgets.git] / src / propgrid / advprops.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/propgrid/advprops.cpp
3 // Purpose: wxPropertyGrid Advanced Properties (font, colour, etc.)
4 // Author: Jaakko Salli
5 // Modified by:
6 // Created: 2004-09-25
7 // RCS-ID: $Id$
8 // Copyright: (c) Jaakko Salli
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 #if wxUSE_PROPGRID
20
21 #ifndef WX_PRECOMP
22 #include "wx/defs.h"
23 #include "wx/object.h"
24 #include "wx/hash.h"
25 #include "wx/string.h"
26 #include "wx/log.h"
27 #include "wx/event.h"
28 #include "wx/window.h"
29 #include "wx/panel.h"
30 #include "wx/dc.h"
31 #include "wx/dcclient.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/textctrl.h"
42 #include "wx/scrolwin.h"
43 #include "wx/dirdlg.h"
44 #include "wx/combobox.h"
45 #include "wx/sizer.h"
46 #include "wx/textdlg.h"
47 #include "wx/filedlg.h"
48 #include "wx/intl.h"
49 #include "wx/wxcrtvararg.h"
50 #endif
51
52 #define __wxPG_SOURCE_FILE__
53
54 #include "wx/propgrid/propgrid.h"
55
56 #if wxPG_INCLUDE_ADVPROPS
57
58 #include "wx/propgrid/advprops.h"
59
60 #ifdef __WXMSW__
61 #include "wx/msw/private.h"
62 #include "wx/msw/dc.h"
63 #endif
64
65 // -----------------------------------------------------------------------
66
67 #if defined(__WXMSW__)
68 #define wxPG_CAN_DRAW_CURSOR 1
69 #elif defined(__WXGTK__)
70 #define wxPG_CAN_DRAW_CURSOR 0
71 #elif defined(__WXMAC__)
72 #define wxPG_CAN_DRAW_CURSOR 0
73 #else
74 #define wxPG_CAN_DRAW_CURSOR 0
75 #endif
76
77
78 // -----------------------------------------------------------------------
79 // Value type related
80 // -----------------------------------------------------------------------
81
82
83 // Implement dynamic class for type value.
84 IMPLEMENT_DYNAMIC_CLASS(wxColourPropertyValue, wxObject)
85
86 bool operator == (const wxColourPropertyValue& a, const wxColourPropertyValue& b)
87 {
88 return ( ( a.m_colour == b.m_colour ) && (a.m_type == b.m_type) );
89 }
90
91 bool operator == (const wxArrayInt& array1, const wxArrayInt& array2)
92 {
93 if ( array1.size() != array2.size() )
94 return false;
95 size_t i;
96 for ( i=0; i<array1.size(); i++ )
97 {
98 if ( array1[i] != array2[i] )
99 return false;
100 }
101 return true;
102 }
103
104 // -----------------------------------------------------------------------
105 // wxSpinCtrl-based property editor
106 // -----------------------------------------------------------------------
107
108 #if wxUSE_SPINBTN
109
110
111 //
112 // This class implements ability to rapidly change "spin" value
113 // by moving mouse when one of the spin buttons is depressed.
114 class wxPGSpinButton : public wxSpinButton
115 {
116 public:
117 wxPGSpinButton() : wxSpinButton()
118 {
119 m_bLeftDown = false;
120 m_hasCapture = false;
121 m_spins = 1;
122
123 Connect( wxEVT_LEFT_DOWN,
124 wxMouseEventHandler(wxPGSpinButton::OnMouseEvent) );
125 Connect( wxEVT_LEFT_UP,
126 wxMouseEventHandler(wxPGSpinButton::OnMouseEvent) );
127 Connect( wxEVT_MOTION,
128 wxMouseEventHandler(wxPGSpinButton::OnMouseEvent) );
129 Connect( wxEVT_MOUSE_CAPTURE_LOST,
130 wxMouseCaptureLostEventHandler(wxPGSpinButton::OnMouseCaptureLost) );
131 }
132
133 int GetSpins() const
134 {
135 return m_spins;
136 }
137
138 private:
139 wxPoint m_ptPosition;
140
141 // Having a separate spins variable allows us to handle validation etc. for
142 // multiple spin events at once (with quick mouse movements there could be
143 // hundreds of 'spins' being done at once). Technically things like this
144 // should be stored in event (wxSpinEvent in this case), but there probably
145 // isn't anything there that can be reliably reused.
146 int m_spins;
147
148 bool m_bLeftDown;
149
150 // SpinButton seems to be a special for mouse capture, so we may need track
151 // privately whether mouse is actually captured.
152 bool m_hasCapture;
153
154 void Capture()
155 {
156 if ( !m_hasCapture )
157 {
158 CaptureMouse();
159 m_hasCapture = true;
160 }
161
162 SetCursor(wxCURSOR_SIZENS);
163 }
164 void Release()
165 {
166 m_bLeftDown = false;
167
168 if ( m_hasCapture )
169 {
170 ReleaseMouse();
171 m_hasCapture = false;
172 }
173
174 wxWindow *parent = GetParent();
175 if ( parent )
176 SetCursor(parent->GetCursor());
177 else
178 SetCursor(wxNullCursor);
179 }
180
181 void OnMouseEvent(wxMouseEvent& event)
182 {
183 if ( event.GetEventType() == wxEVT_LEFT_DOWN )
184 {
185 m_bLeftDown = true;
186 m_ptPosition = event.GetPosition();
187 }
188 else if ( event.GetEventType() == wxEVT_LEFT_UP )
189 {
190 Release();
191 m_bLeftDown = false;
192 }
193 else if ( event.GetEventType() == wxEVT_MOTION )
194 {
195 if ( m_bLeftDown )
196 {
197 int dy = m_ptPosition.y - event.GetPosition().y;
198 if ( dy )
199 {
200 Capture();
201 m_ptPosition = event.GetPosition();
202
203 wxSpinEvent evtscroll( (dy >= 0) ? wxEVT_SCROLL_LINEUP :
204 wxEVT_SCROLL_LINEDOWN,
205 GetId() );
206 evtscroll.SetEventObject(this);
207
208 wxASSERT( m_spins == 1 );
209
210 m_spins = abs(dy);
211 GetEventHandler()->ProcessEvent(evtscroll);
212 m_spins = 1;
213 }
214 }
215 }
216
217 event.Skip();
218 }
219 void OnMouseCaptureLost(wxMouseCaptureLostEvent& WXUNUSED(event))
220 {
221 Release();
222 }
223 };
224
225
226 WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(SpinCtrl,
227 wxPGSpinCtrlEditor,
228 wxPGEditor)
229
230
231 // Trivial destructor.
232 wxPGSpinCtrlEditor::~wxPGSpinCtrlEditor()
233 {
234 }
235
236
237 // Create controls and initialize event handling.
238 wxPGWindowList wxPGSpinCtrlEditor::CreateControls( wxPropertyGrid* propgrid, wxPGProperty* property,
239 const wxPoint& pos, const wxSize& sz ) const
240 {
241 const int margin = 1;
242 wxSize butSz(18, sz.y);
243 wxSize tcSz(sz.x - butSz.x - margin, sz.y);
244 wxPoint butPos(pos.x + tcSz.x + margin, pos.y);
245
246 wxSpinButton* wnd2;
247
248 wnd2 = new wxPGSpinButton();
249
250 #ifdef __WXMSW__
251 wnd2->Hide();
252 #endif
253 wnd2->Create( propgrid->GetPanel(), wxPG_SUBID2, butPos, butSz, wxSP_VERTICAL );
254
255 wnd2->SetRange( INT_MIN, INT_MAX );
256 wnd2->SetValue( 0 );
257
258 // Let's add validator to make sure only numbers can be entered
259 wxTextValidator validator(wxFILTER_NUMERIC, &m_tempString);
260
261 wxTextCtrl* wnd1 = (wxTextCtrl*) wxPGTextCtrlEditor::CreateControls( propgrid, property, pos, tcSz ).m_primary;
262 wnd1->SetValidator(validator);
263
264 return wxPGWindowList(wnd1, wnd2);
265 }
266
267 // Control's events are redirected here
268 bool wxPGSpinCtrlEditor::OnEvent( wxPropertyGrid* propgrid, wxPGProperty* property,
269 wxWindow* wnd, wxEvent& event ) const
270 {
271 int evtType = event.GetEventType();
272 int keycode = -1;
273 int spins = 1;
274 bool bigStep = false;
275
276 if ( evtType == wxEVT_KEY_DOWN )
277 {
278 wxKeyEvent& keyEvent = (wxKeyEvent&)event;
279 keycode = keyEvent.GetKeyCode();
280
281 if ( keycode == WXK_UP )
282 evtType = wxEVT_SCROLL_LINEUP;
283 else if ( keycode == WXK_DOWN )
284 evtType = wxEVT_SCROLL_LINEDOWN;
285 else if ( keycode == WXK_PAGEUP )
286 {
287 evtType = wxEVT_SCROLL_LINEUP;
288 bigStep = true;
289 }
290 else if ( keycode == WXK_PAGEDOWN )
291 {
292 evtType = wxEVT_SCROLL_LINEDOWN;
293 bigStep = true;
294 }
295 }
296
297 if ( evtType == wxEVT_SCROLL_LINEUP || evtType == wxEVT_SCROLL_LINEDOWN )
298 {
299 wxPGSpinButton* spinButton =
300 (wxPGSpinButton*) propgrid->GetEditorControlSecondary();
301
302 if ( spinButton )
303 spins = spinButton->GetSpins();
304
305 wxString s;
306 // Can't use wnd since it might be clipper window
307 wxTextCtrl* tc = wxDynamicCast(propgrid->GetEditorControl(), wxTextCtrl);
308
309 if ( tc )
310 s = tc->GetValue();
311 else
312 s = property->GetValueAsString(wxPG_FULL_VALUE);
313
314 int mode = wxPG_PROPERTY_VALIDATION_SATURATE;
315
316 if ( property->GetAttributeAsLong(wxT("Wrap"), 0) )
317 mode = wxPG_PROPERTY_VALIDATION_WRAP;
318
319 if ( property->GetValueType() == wxT("double") )
320 {
321 double v_d;
322 double step = property->GetAttributeAsDouble(wxT("Step"), 1.0);
323
324 // Try double
325 if ( s.ToDouble(&v_d) )
326 {
327 if ( bigStep )
328 step *= 10.0;
329
330 step *= (double) spins;
331
332 if ( evtType == wxEVT_SCROLL_LINEUP ) v_d += step;
333 else v_d -= step;
334
335 // Min/Max check
336 wxFloatProperty::DoValidation(property, v_d, NULL, mode);
337
338 wxPropertyGrid::DoubleToString(s, v_d, 6, true, NULL);
339 }
340 else
341 {
342 return false;
343 }
344 }
345 else
346 {
347 wxLongLong_t v_ll;
348 wxLongLong_t step = property->GetAttributeAsLong(wxT("Step"), 1);
349
350 // Try (long) long
351 if ( s.ToLongLong(&v_ll, 10) )
352 {
353 if ( bigStep )
354 step *= 10;
355
356 step *= spins;
357
358 if ( evtType == wxEVT_SCROLL_LINEUP ) v_ll += step;
359 else v_ll -= step;
360
361 // Min/Max check
362 wxIntProperty::DoValidation(property, v_ll, NULL, mode);
363
364 s = wxLongLong(v_ll).ToString();
365 }
366 else
367 {
368 return false;
369 }
370 }
371
372 if ( tc )
373 {
374 int ip = tc->GetInsertionPoint();
375 int lp = tc->GetLastPosition();
376 tc->SetValue(s);
377 tc->SetInsertionPoint(ip+(tc->GetLastPosition()-lp));
378 }
379
380 return true;
381 }
382
383 return wxPGTextCtrlEditor::OnEvent(propgrid,property,wnd,event);
384 }
385
386 #endif // wxUSE_SPINBTN
387
388
389 // -----------------------------------------------------------------------
390 // wxDatePickerCtrl-based property editor
391 // -----------------------------------------------------------------------
392
393 #if wxUSE_DATEPICKCTRL
394
395
396 #include "wx/datectrl.h"
397 #include "wx/dateevt.h"
398
399 class wxPGDatePickerCtrlEditor : public wxPGEditor
400 {
401 DECLARE_DYNAMIC_CLASS(wxPGDatePickerCtrlEditor)
402 public:
403 virtual ~wxPGDatePickerCtrlEditor();
404
405 wxString GetName() const;
406 virtual wxPGWindowList CreateControls(wxPropertyGrid* propgrid,
407 wxPGProperty* property,
408 const wxPoint& pos,
409 const wxSize& size) const;
410 virtual void UpdateControl( wxPGProperty* property, wxWindow* wnd ) const;
411 virtual bool OnEvent( wxPropertyGrid* propgrid, wxPGProperty* property,
412 wxWindow* wnd, wxEvent& event ) const;
413 virtual bool GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* wnd ) const;
414 virtual void SetValueToUnspecified( wxPGProperty* WXUNUSED(property), wxWindow* wnd ) const;
415 };
416
417
418 WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(DatePickerCtrl,
419 wxPGDatePickerCtrlEditor,
420 wxPGEditor)
421
422
423 wxPGDatePickerCtrlEditor::~wxPGDatePickerCtrlEditor()
424 {
425 }
426
427 wxPGWindowList wxPGDatePickerCtrlEditor::CreateControls( wxPropertyGrid* propgrid,
428 wxPGProperty* property,
429 const wxPoint& pos,
430 const wxSize& sz ) const
431 {
432 wxCHECK_MSG( property->IsKindOf(CLASSINFO(wxDateProperty)),
433 NULL,
434 wxT("DatePickerCtrl editor can only be used with wxDateProperty or derivative.") );
435
436 wxDateProperty* prop = (wxDateProperty*) property;
437
438 // Use two stage creation to allow cleaner display on wxMSW
439 wxDatePickerCtrl* ctrl = new wxDatePickerCtrl();
440 #ifdef __WXMSW__
441 ctrl->Hide();
442 wxSize useSz = wxDefaultSize;
443 useSz.x = sz.x;
444 #else
445 wxSize useSz = sz;
446 #endif
447
448 wxDateTime dateValue(wxInvalidDateTime);
449
450 wxVariant value = prop->GetValue();
451 if ( value.GetType() == wxT("datetime") )
452 dateValue = value.GetDateTime();
453
454 ctrl->Create(propgrid->GetPanel(),
455 wxPG_SUBID1,
456 dateValue,
457 pos,
458 useSz,
459 prop->GetDatePickerStyle() | wxNO_BORDER);
460
461 #ifdef __WXMSW__
462 ctrl->Show();
463 #endif
464
465 return ctrl;
466 }
467
468 // Copies value from property to control
469 void wxPGDatePickerCtrlEditor::UpdateControl( wxPGProperty* property, wxWindow* wnd ) const
470 {
471 wxDatePickerCtrl* ctrl = (wxDatePickerCtrl*) wnd;
472 wxASSERT( ctrl && ctrl->IsKindOf(CLASSINFO(wxDatePickerCtrl)) );
473
474 // We assume that property's data type is 'int' (or something similar),
475 // thus allowing us to get raw, unchecked value via DoGetValue.
476 ctrl->SetValue( property->GetValue().GetDateTime() );
477 }
478
479 // Control's events are redirected here
480 bool wxPGDatePickerCtrlEditor::OnEvent( wxPropertyGrid* WXUNUSED(propgrid),
481 wxPGProperty* WXUNUSED(property),
482 wxWindow* WXUNUSED(wnd),
483 wxEvent& event ) const
484 {
485 if ( event.GetEventType() == wxEVT_DATE_CHANGED )
486 return true;
487
488 return false;
489 }
490
491 bool wxPGDatePickerCtrlEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* WXUNUSED(property), wxWindow* wnd ) const
492 {
493 wxDatePickerCtrl* ctrl = (wxDatePickerCtrl*) wnd;
494 wxASSERT( ctrl && ctrl->IsKindOf(CLASSINFO(wxDatePickerCtrl)) );
495
496 variant = ctrl->GetValue();
497
498 return true;
499 }
500
501 void wxPGDatePickerCtrlEditor::SetValueToUnspecified( wxPGProperty* WXUNUSED(property), wxWindow* WXUNUSED(wnd) ) const
502 {
503 // TODO?
504 //wxDateProperty* prop = (wxDateProperty*) property;
505 //ctrl->SetValue(?);
506 }
507
508 #endif // wxUSE_DATEPICKCTRL
509
510
511 // -----------------------------------------------------------------------
512 // wxFontProperty
513 // -----------------------------------------------------------------------
514
515 #include "wx/fontdlg.h"
516 #include "wx/fontenum.h"
517
518 static const wxChar* gs_fp_es_family_labels[] = {
519 wxT("Default"), wxT("Decorative"),
520 wxT("Roman"), wxT("Script"),
521 wxT("Swiss"), wxT("Modern"),
522 (const wxChar*) NULL
523 };
524
525 static long gs_fp_es_family_values[] = {
526 wxDEFAULT, wxDECORATIVE,
527 wxROMAN, wxSCRIPT,
528 wxSWISS, wxMODERN
529 };
530
531 static const wxChar* gs_fp_es_style_labels[] = {
532 wxT("Normal"),
533 wxT("Slant"),
534 wxT("Italic"),
535 (const wxChar*) NULL
536 };
537
538 static long gs_fp_es_style_values[] = {
539 wxNORMAL,
540 wxSLANT,
541 wxITALIC
542 };
543
544 static const wxChar* gs_fp_es_weight_labels[] = {
545 wxT("Normal"),
546 wxT("Light"),
547 wxT("Bold"),
548 (const wxChar*) NULL
549 };
550
551 static long gs_fp_es_weight_values[] = {
552 wxNORMAL,
553 wxLIGHT,
554 wxBOLD
555 };
556
557 // Class body is in advprops.h
558
559
560 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFontProperty,wxPGProperty,
561 wxFont,const wxFont&,TextCtrlAndButton)
562
563
564 wxFontProperty::wxFontProperty( const wxString& label, const wxString& name,
565 const wxFont& value )
566 : wxPGProperty(label,name)
567 {
568 SetValue(WXVARIANT(value));
569
570 // Initialize font family choices list
571 if ( !wxPGGlobalVars->m_fontFamilyChoices )
572 {
573 wxArrayString faceNames = wxFontEnumerator::GetFacenames();
574
575 faceNames.Sort();
576
577 wxPGGlobalVars->m_fontFamilyChoices = new wxPGChoices(faceNames);
578 }
579
580 wxString emptyString(wxEmptyString);
581
582 wxFont font;
583 font << m_value;
584
585 SetParentalType(wxPG_PROP_AGGREGATE);
586
587 AddChild( new wxIntProperty( _("Point Size"), wxS("Point Size"),(long)font.GetPointSize() ) );
588
589 AddChild( new wxEnumProperty(_("Family"), wxS("PointSize"),
590 gs_fp_es_family_labels,gs_fp_es_family_values,
591 font.GetFamily()) );
592
593 wxString faceName = font.GetFaceName();
594 // If font was not in there, add it now
595 if ( faceName.length() &&
596 wxPGGlobalVars->m_fontFamilyChoices->Index(faceName) == wxNOT_FOUND )
597 wxPGGlobalVars->m_fontFamilyChoices->AddAsSorted(faceName);
598
599 wxPGProperty* p = new wxEnumProperty(_("Face Name"), wxS("Face Name"),
600 *wxPGGlobalVars->m_fontFamilyChoices);
601
602 p->SetValueFromString(faceName, wxPG_FULL_VALUE);
603
604 AddChild( p );
605
606 AddChild( new wxEnumProperty(_("Style"), wxS("Style"),
607 gs_fp_es_style_labels,gs_fp_es_style_values,font.GetStyle()) );
608
609 AddChild( new wxEnumProperty(_("Weight"), wxS("Weight"),
610 gs_fp_es_weight_labels,gs_fp_es_weight_values,font.GetWeight()) );
611
612 AddChild( new wxBoolProperty(_("Underlined"), wxS("Underlined"),
613 font.GetUnderlined()) );
614 }
615
616 wxFontProperty::~wxFontProperty() { }
617
618 void wxFontProperty::OnSetValue()
619 {
620 wxFont font;
621 font << m_value;
622
623 if ( !font.Ok() )
624 {
625 font = wxFont(10,wxSWISS,wxNORMAL,wxNORMAL);
626 m_value << font;
627 }
628 }
629
630 wxString wxFontProperty::ValueToString( wxVariant& value,
631 int argFlags ) const
632 {
633 return wxPGProperty::ValueToString(value, argFlags);
634 }
635
636 bool wxFontProperty::OnEvent( wxPropertyGrid* propgrid, wxWindow* WXUNUSED(primary),
637 wxEvent& event )
638 {
639 if ( propgrid->IsMainButtonEvent(event) )
640 {
641 // Update value from last minute changes
642 wxVariant useValue = propgrid->GetUncommittedPropertyValue();
643
644 wxFontData data;
645 wxFont font;
646 font << useValue;
647 data.SetInitialFont( font );
648 data.SetColour(*wxBLACK);
649
650 wxFontDialog dlg(propgrid, data);
651 if ( dlg.ShowModal() == wxID_OK )
652 {
653 propgrid->EditorsValueWasModified();
654
655 wxVariant variant;
656 variant << dlg.GetFontData().GetChosenFont();
657 SetValueInEvent( variant );
658 return true;
659 }
660 }
661 return false;
662 }
663
664 void wxFontProperty::RefreshChildren()
665 {
666 if ( !GetChildCount() ) return;
667 wxFont font;
668 font << m_value;
669 Item(0)->SetValue( (long)font.GetPointSize() );
670 Item(1)->SetValue( (long)font.GetFamily() );
671 Item(2)->SetValueFromString( font.GetFaceName(), wxPG_FULL_VALUE );
672 Item(3)->SetValue( (long)font.GetStyle() );
673 Item(4)->SetValue( (long)font.GetWeight() );
674 Item(5)->SetValue( font.GetUnderlined() );
675 }
676
677 void wxFontProperty::ChildChanged( wxVariant& thisValue, int ind, wxVariant& childValue ) const
678 {
679 wxFont font;
680 font << thisValue;
681
682 if ( ind == 0 )
683 {
684 font.SetPointSize( wxPGVariantToInt(childValue) );
685 }
686 else if ( ind == 1 )
687 {
688 int fam = childValue.GetLong();
689 if ( fam < wxDEFAULT ||
690 fam > wxTELETYPE )
691 fam = wxDEFAULT;
692 font.SetFamily( fam );
693 }
694 else if ( ind == 2 )
695 {
696 wxString faceName;
697 int faceIndex = childValue.GetLong();
698
699 if ( faceIndex >= 0 )
700 faceName = wxPGGlobalVars->m_fontFamilyChoices->GetLabel(faceIndex);
701
702 font.SetFaceName( faceName );
703 }
704 else if ( ind == 3 )
705 {
706 int st = childValue.GetLong();
707 if ( st != wxFONTSTYLE_NORMAL &&
708 st != wxFONTSTYLE_SLANT &&
709 st != wxFONTSTYLE_ITALIC )
710 st = wxFONTWEIGHT_NORMAL;
711 font.SetStyle( st );
712 }
713 else if ( ind == 4 )
714 {
715 int wt = childValue.GetLong();
716 if ( wt != wxFONTWEIGHT_NORMAL &&
717 wt != wxFONTWEIGHT_LIGHT &&
718 wt != wxFONTWEIGHT_BOLD )
719 wt = wxFONTWEIGHT_NORMAL;
720 font.SetWeight( wt );
721 }
722 else if ( ind == 5 )
723 {
724 font.SetUnderlined( childValue.GetBool() );
725 }
726
727 thisValue << font;
728 }
729
730 /*
731 wxSize wxFontProperty::OnMeasureImage() const
732 {
733 return wxSize(-1,-1);
734 }
735
736 void wxFontProperty::OnCustomPaint(wxDC& dc,
737 const wxRect& rect,
738 wxPGPaintData& paintData)
739 {
740 wxString drawFace;
741 if ( paintData.m_choiceItem >= 0 )
742 drawFace = wxPGGlobalVars->m_fontFamilyChoices->GetLabel(paintData.m_choiceItem);
743 else
744 drawFace = m_value_wxFont.GetFaceName();
745
746 if ( drawFace.length() )
747 {
748 // Draw the background
749 dc.SetBrush( wxColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)) );
750 //dc.SetBrush( *wxWHITE_BRUSH );
751 //dc.SetPen( *wxMEDIUM_GREY_PEN );
752 dc.DrawRectangle( rect );
753
754 wxFont oldFont = dc.GetFont();
755 wxFont drawFont(oldFont.GetPointSize(),
756 wxDEFAULT,wxNORMAL,wxBOLD,false,drawFace);
757 dc.SetFont(drawFont);
758
759 dc.SetTextForeground( wxSystemSettings::GetColour(wxSYS_COLOUR_BTNTEXT) );
760 dc.DrawText( wxT("Aa"), rect.x+2, rect.y+1 );
761
762 dc.SetFont(oldFont);
763 }
764 else
765 {
766 // No file - just draw a white box
767 dc.SetBrush ( *wxWHITE_BRUSH );
768 dc.DrawRectangle ( rect );
769 }
770 }
771 */
772
773
774 // -----------------------------------------------------------------------
775 // wxSystemColourProperty
776 // -----------------------------------------------------------------------
777
778 // wxEnumProperty based classes cannot use wxPG_PROP_CLASS_SPECIFIC_1
779 #define wxPG_PROP_HIDE_CUSTOM_COLOUR wxPG_PROP_CLASS_SPECIFIC_2
780
781 #include "wx/colordlg.h"
782
783 //#define wx_cp_es_syscolours_len 25
784 static const wxChar* gs_cp_es_syscolour_labels[] = {
785 wxT("AppWorkspace"),
786 wxT("ActiveBorder"),
787 wxT("ActiveCaption"),
788 wxT("ButtonFace"),
789 wxT("ButtonHighlight"),
790 wxT("ButtonShadow"),
791 wxT("ButtonText"),
792 wxT("CaptionText"),
793 wxT("ControlDark"),
794 wxT("ControlLight"),
795 wxT("Desktop"),
796 wxT("GrayText"),
797 wxT("Highlight"),
798 wxT("HighlightText"),
799 wxT("InactiveBorder"),
800 wxT("InactiveCaption"),
801 wxT("InactiveCaptionText"),
802 wxT("Menu"),
803 wxT("Scrollbar"),
804 wxT("Tooltip"),
805 wxT("TooltipText"),
806 wxT("Window"),
807 wxT("WindowFrame"),
808 wxT("WindowText"),
809 wxT("Custom"),
810 (const wxChar*) NULL
811 };
812
813 static long gs_cp_es_syscolour_values[] = {
814 wxSYS_COLOUR_APPWORKSPACE,
815 wxSYS_COLOUR_ACTIVEBORDER,
816 wxSYS_COLOUR_ACTIVECAPTION,
817 wxSYS_COLOUR_BTNFACE,
818 wxSYS_COLOUR_BTNHIGHLIGHT,
819 wxSYS_COLOUR_BTNSHADOW,
820 wxSYS_COLOUR_BTNTEXT ,
821 wxSYS_COLOUR_CAPTIONTEXT,
822 wxSYS_COLOUR_3DDKSHADOW,
823 wxSYS_COLOUR_3DLIGHT,
824 wxSYS_COLOUR_BACKGROUND,
825 wxSYS_COLOUR_GRAYTEXT,
826 wxSYS_COLOUR_HIGHLIGHT,
827 wxSYS_COLOUR_HIGHLIGHTTEXT,
828 wxSYS_COLOUR_INACTIVEBORDER,
829 wxSYS_COLOUR_INACTIVECAPTION,
830 wxSYS_COLOUR_INACTIVECAPTIONTEXT,
831 wxSYS_COLOUR_MENU,
832 wxSYS_COLOUR_SCROLLBAR,
833 wxSYS_COLOUR_INFOBK,
834 wxSYS_COLOUR_INFOTEXT,
835 wxSYS_COLOUR_WINDOW,
836 wxSYS_COLOUR_WINDOWFRAME,
837 wxSYS_COLOUR_WINDOWTEXT,
838 wxPG_COLOUR_CUSTOM
839 };
840
841
842 IMPLEMENT_VARIANT_OBJECT_EXPORTED_SHALLOWCMP(wxColourPropertyValue, WXDLLIMPEXP_PROPGRID)
843
844
845 // Class body is in advprops.h
846
847 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxSystemColourProperty,wxEnumProperty,
848 wxColourPropertyValue,const wxColourPropertyValue&,Choice)
849
850
851 void wxSystemColourProperty::Init( int type, const wxColour& colour )
852 {
853 wxColourPropertyValue cpv;
854
855 if ( colour.Ok() )
856 cpv.Init( type, colour );
857 else
858 cpv.Init( type, *wxWHITE );
859
860 m_flags |= wxPG_PROP_STATIC_CHOICES; // Colour selection cannot be changed.
861
862 m_value << cpv;
863
864 OnSetValue();
865 }
866
867
868 static wxPGChoices gs_wxSystemColourProperty_choicesCache;
869
870
871 wxSystemColourProperty::wxSystemColourProperty( const wxString& label, const wxString& name,
872 const wxColourPropertyValue& value )
873 : wxEnumProperty( label,
874 name,
875 gs_cp_es_syscolour_labels,
876 gs_cp_es_syscolour_values,
877 &gs_wxSystemColourProperty_choicesCache )
878 {
879 if ( &value )
880 Init( value.m_type, value.m_colour );
881 else
882 Init( wxPG_COLOUR_CUSTOM, *wxWHITE );
883 }
884
885
886 wxSystemColourProperty::wxSystemColourProperty( const wxString& label, const wxString& name,
887 const wxChar** labels, const long* values, wxPGChoices* choicesCache,
888 const wxColourPropertyValue& value )
889 : wxEnumProperty( label, name, labels, values, choicesCache )
890 {
891 if ( &value )
892 Init( value.m_type, value.m_colour );
893 else
894 Init( wxPG_COLOUR_CUSTOM, *wxWHITE );
895 }
896
897
898 wxSystemColourProperty::wxSystemColourProperty( const wxString& label, const wxString& name,
899 const wxChar** labels, const long* values, wxPGChoices* choicesCache,
900 const wxColour& value )
901 : wxEnumProperty( label, name, labels, values, choicesCache )
902 {
903 if ( &value )
904 Init( wxPG_COLOUR_CUSTOM, value );
905 else
906 Init( wxPG_COLOUR_CUSTOM, *wxWHITE );
907 }
908
909
910 wxSystemColourProperty::~wxSystemColourProperty() { }
911
912
913 wxColourPropertyValue wxSystemColourProperty::GetVal( const wxVariant* pVariant ) const
914 {
915 if ( !pVariant )
916 pVariant = &m_value;
917
918 if ( pVariant->IsNull() )
919 return wxColourPropertyValue(wxPG_COLOUR_UNSPECIFIED, wxColour());
920
921 if ( pVariant->GetType() == wxS("wxColourPropertyValue") )
922 {
923 wxColourPropertyValue v;
924 v << *pVariant;
925 return v;
926 }
927
928 wxColour col;
929 bool variantProcessed = true;
930
931 if ( pVariant->GetType() == wxS("wxColour*") )
932 {
933 wxColour* pCol = wxStaticCast(pVariant->GetWxObjectPtr(), wxColour);
934 col = *pCol;
935 }
936 else if ( pVariant->GetType() == wxS("wxColour") )
937 {
938 col << *pVariant;
939 }
940 else if ( pVariant->GetType() == wxArrayInt_VariantType )
941 {
942 // This code is mostly needed for wxPython bindings, which
943 // may offer tuple of integers as colour value.
944 wxArrayInt arr;
945 arr << *pVariant;
946
947 if ( arr.size() >= 3 )
948 {
949 int r, g, b;
950 int a = 255;
951
952 r = arr[0];
953 g = arr[1];
954 b = arr[2];
955 if ( arr.size() >= 4 )
956 a = arr[3];
957
958 col = wxColour(r, g, b, a);
959 }
960 else
961 {
962 variantProcessed = false;
963 }
964 }
965 else
966 {
967 variantProcessed = false;
968 }
969
970 if ( !variantProcessed )
971 return wxColourPropertyValue(wxPG_COLOUR_UNSPECIFIED, wxColour());
972
973 wxColourPropertyValue v2( wxPG_COLOUR_CUSTOM, col );
974
975 int colInd = ColToInd(col);
976 if ( colInd != wxNOT_FOUND )
977 v2.m_type = colInd;
978
979 return v2;
980 }
981
982 wxVariant wxSystemColourProperty::DoTranslateVal( wxColourPropertyValue& v ) const
983 {
984 wxVariant variant;
985 variant << v;
986 return variant;
987 }
988
989 int wxSystemColourProperty::ColToInd( const wxColour& colour ) const
990 {
991 size_t i;
992 size_t i_max = m_choices.GetCount() - 1;
993
994 for ( i=0; i<i_max; i++ )
995 {
996 int ind = m_choices[i].GetValue();
997
998 if ( colour == GetColour(ind) )
999 {
1000 /*wxLogDebug(wxT("%s(%s): Index %i for ( getcolour(%i,%i,%i), colour(%i,%i,%i))"),
1001 GetClassName(),GetLabel().c_str(),
1002 (int)i,(int)GetColour(ind).Red(),(int)GetColour(ind).Green(),(int)GetColour(ind).Blue(),
1003 (int)colour.Red(),(int)colour.Green(),(int)colour.Blue());*/
1004 return ind;
1005 }
1006 }
1007 return wxNOT_FOUND;
1008 }
1009
1010 void wxSystemColourProperty::OnSetValue()
1011 {
1012 // Convert from generic wxobject ptr to wxPGVariantDataColour
1013 if ( m_value.GetType() == wxS("wxColour*") )
1014 {
1015 wxColour* pCol = wxStaticCast(m_value.GetWxObjectPtr(), wxColour);
1016 m_value << *pCol;
1017 }
1018
1019 wxColourPropertyValue val = GetVal(&m_value);
1020
1021 if ( val.m_type == wxPG_COLOUR_UNSPECIFIED )
1022 {
1023 m_value.MakeNull();
1024 return;
1025 }
1026 else
1027 {
1028
1029 if ( val.m_type < wxPG_COLOUR_WEB_BASE )
1030 val.m_colour = GetColour( val.m_type );
1031
1032 m_value = TranslateVal(val);
1033 }
1034
1035 int ind = wxNOT_FOUND;
1036
1037 if ( m_value.GetType() == wxS("wxColourPropertyValue") )
1038 {
1039 wxColourPropertyValue cpv;
1040 cpv << m_value;
1041 wxColour col = cpv.m_colour;
1042
1043 if ( !col.Ok() )
1044 {
1045 SetValueToUnspecified();
1046 SetIndex(wxNOT_FOUND);
1047 return;
1048 }
1049
1050 if ( cpv.m_type < wxPG_COLOUR_WEB_BASE )
1051 {
1052 ind = GetIndexForValue(cpv.m_type);
1053 }
1054 else
1055 {
1056 cpv.m_type = wxPG_COLOUR_CUSTOM;
1057 ind = GetCustomColourIndex();
1058 }
1059 }
1060 else
1061 {
1062 wxColour col;
1063 col << m_value;
1064
1065 if ( !col.Ok() )
1066 {
1067 SetValueToUnspecified();
1068 SetIndex(wxNOT_FOUND);
1069 return;
1070 }
1071
1072 ind = ColToInd(col);
1073
1074 if ( ind == wxNOT_FOUND )
1075 ind = GetCustomColourIndex();
1076 }
1077
1078 SetIndex(ind);
1079 }
1080
1081
1082 wxColour wxSystemColourProperty::GetColour( int index ) const
1083 {
1084 return wxSystemSettings::GetColour( (wxSystemColour)index );
1085 }
1086
1087 wxString wxSystemColourProperty::ColourToString( const wxColour& col, int index ) const
1088 {
1089 if ( index == wxNOT_FOUND )
1090 return wxString::Format(wxT("(%i,%i,%i)"),
1091 (int)col.Red(),
1092 (int)col.Green(),
1093 (int)col.Blue());
1094 else
1095 return m_choices.GetLabel(index);
1096 }
1097
1098 wxString wxSystemColourProperty::ValueToString( wxVariant& value,
1099 int argFlags ) const
1100 {
1101 wxColourPropertyValue val = GetVal(&value);
1102
1103 int index;
1104
1105 if ( argFlags & wxPG_VALUE_IS_CURRENT )
1106 {
1107 // GetIndex() only works reliably if wxPG_VALUE_IS_CURRENT flag is set,
1108 // but we should use it whenever possible.
1109 index = GetIndex();
1110
1111 // If custom colour was selected, use invalid index, so that
1112 // ColourToString() will return properly formatted colour text.
1113 if ( index == GetCustomColourIndex() )
1114 index = wxNOT_FOUND;
1115 }
1116 else
1117 {
1118 index = m_choices.Index(val.m_type);
1119 }
1120
1121 return ColourToString(val.m_colour, index);
1122 }
1123
1124
1125 wxSize wxSystemColourProperty::OnMeasureImage( int ) const
1126 {
1127 return wxPG_DEFAULT_IMAGE_SIZE;
1128 }
1129
1130
1131 int wxSystemColourProperty::GetCustomColourIndex() const
1132 {
1133 return m_choices.GetCount() - 1;
1134 }
1135
1136
1137 bool wxSystemColourProperty::QueryColourFromUser( wxVariant& variant ) const
1138 {
1139 wxASSERT( m_value.GetType() != wxPG_VARIANT_TYPE_STRING );
1140 bool res = false;
1141
1142 wxPropertyGrid* propgrid = GetGrid();
1143 wxASSERT( propgrid );
1144
1145 // Must only occur when user triggers event
1146 if ( !(propgrid->GetInternalFlags() & wxPG_FL_IN_HANDLECUSTOMEDITOREVENT) )
1147 return res;
1148
1149 wxColourPropertyValue val = GetVal();
1150
1151 val.m_type = wxPG_COLOUR_CUSTOM;
1152
1153 wxColourData data;
1154 data.SetChooseFull(true);
1155 data.SetColour(val.m_colour);
1156 int i;
1157 for ( i = 0; i < 16; i++)
1158 {
1159 wxColour colour(i*16, i*16, i*16);
1160 data.SetCustomColour(i, colour);
1161 }
1162
1163 wxColourDialog dialog(propgrid, &data);
1164 if ( dialog.ShowModal() == wxID_OK )
1165 {
1166 wxColourData retData = dialog.GetColourData();
1167 val.m_colour = retData.GetColour();
1168
1169 variant = DoTranslateVal(val);
1170
1171 SetValueInEvent(variant);
1172
1173 res = true;
1174 }
1175
1176 return res;
1177 }
1178
1179
1180 bool wxSystemColourProperty::IntToValue( wxVariant& variant, int number, int WXUNUSED(argFlags) ) const
1181 {
1182 int index = number;
1183 int type = m_choices.GetValue(index);
1184
1185 if ( type == wxPG_COLOUR_CUSTOM )
1186 {
1187 QueryColourFromUser(variant);
1188 }
1189 else
1190 {
1191 variant = TranslateVal( type, GetColour(type) );
1192 }
1193
1194 return true;
1195 }
1196
1197 // Need to do some extra event handling.
1198 bool wxSystemColourProperty::OnEvent( wxPropertyGrid* propgrid, wxWindow* WXUNUSED(primary), wxEvent& event )
1199 {
1200 if ( propgrid->IsMainButtonEvent(event) )
1201 {
1202 // We need to handle button click in case editor has been
1203 // switched to one that has wxButton as well.
1204 wxVariant variant;
1205 if ( QueryColourFromUser(variant) )
1206 return true;
1207 }
1208 return false;
1209 }
1210
1211 /*class wxPGColourPropertyRenderer : public wxPGDefaultRenderer
1212 {
1213 public:
1214 virtual void Render( wxDC& dc, const wxRect& rect,
1215 const wxPropertyGrid* propertyGrid, wxPGProperty* property,
1216 int WXUNUSED(column), int item, int WXUNUSED(flags) ) const
1217 {
1218 wxASSERT( property->IsKindOf(CLASSINFO(wxSystemColourProperty)) );
1219 wxSystemColourProperty* prop = wxStaticCast(property, wxSystemColourProperty);
1220
1221 dc.SetPen(*wxBLACK_PEN);
1222 if ( item >= 0 &&
1223 ( item < (int)(GetCustomColourIndex) || (prop->HasFlag(wxPG_PROP_HIDE_CUSTOM_COLOUR)))
1224 )
1225 {
1226 int colInd;
1227 const wxArrayInt& values = prop->GetValues();
1228 if ( values.GetChildCount() )
1229 colInd = values[item];
1230 else
1231 colInd = item;
1232 dc.SetBrush( wxColour( prop->GetColour( colInd ) ) );
1233 }
1234 else if ( !prop->IsValueUnspecified() )
1235 dc.SetBrush( prop->GetVal().m_colour );
1236 else
1237 dc.SetBrush( *wxWHITE );
1238
1239 wxRect imageRect = propertyGrid->GetImageRect(property, item);
1240 wxLogDebug(wxT("%i, %i"),imageRect.x,imageRect.y);
1241 dc.DrawRectangle( rect.x+imageRect.x, rect.y+imageRect.y,
1242 imageRect.width, imageRect.height );
1243
1244 wxString text;
1245 if ( item == -1 )
1246 text = property->GetValueAsString();
1247 else
1248 text = property->GetChoiceString(item);
1249 DrawText( dc, rect, imageRect.width, text );
1250 }
1251 protected:
1252 };
1253
1254 wxPGColourPropertyRenderer g_wxPGColourPropertyRenderer;
1255
1256 wxPGCellRenderer* wxSystemColourProperty::GetCellRenderer( int column ) const
1257 {
1258 if ( column == 1 )
1259 return &g_wxPGColourPropertyRenderer;
1260 return wxEnumProperty::GetCellRenderer(column);
1261 }*/
1262
1263 void wxSystemColourProperty::OnCustomPaint( wxDC& dc, const wxRect& rect,
1264 wxPGPaintData& paintdata )
1265 {
1266 wxColour col;
1267
1268 if ( paintdata.m_choiceItem >= 0 && paintdata.m_choiceItem < (int)m_choices.GetCount() &&
1269 paintdata.m_choiceItem != GetCustomColourIndex() )
1270 {
1271 int colInd = m_choices[paintdata.m_choiceItem].GetValue();
1272 col = GetColour( colInd );
1273 }
1274 else if ( !IsValueUnspecified() )
1275 {
1276 col = GetVal().m_colour;
1277 }
1278
1279 if ( col.Ok() )
1280 {
1281 dc.SetBrush(col);
1282 dc.DrawRectangle(rect);
1283 }
1284 }
1285
1286
1287 bool wxSystemColourProperty::StringToValue( wxVariant& value, const wxString& text, int argFlags ) const
1288 {
1289 //
1290 // Accept colour format "[Name] [(R,G,B)]"
1291 // Name takes precedence.
1292 //
1293 wxString colourName;
1294 wxString colourRGB;
1295
1296 int ppos = text.Find(wxT("("));
1297
1298 if ( ppos == wxNOT_FOUND )
1299 {
1300 colourName = text;
1301 }
1302 else
1303 {
1304 colourName = text.substr(0, ppos);
1305 colourRGB = text.substr(ppos, text.length()-ppos);
1306 }
1307
1308 // Strip spaces from extremities
1309 colourName.Trim(true);
1310 colourName.Trim(false);
1311 colourRGB.Trim(true);
1312
1313 // Validate colourRGB string - (1,1,1) is shortest allowed
1314 if ( colourRGB.length() < 7 )
1315 colourRGB.clear();
1316
1317 if ( colourRGB.length() == 0 && m_choices.GetCount() &&
1318 colourName == m_choices.GetLabel(GetCustomColourIndex()) )
1319 {
1320 if ( !(argFlags & wxPG_EDITABLE_VALUE ))
1321 {
1322 // This really should not occurr...
1323 // wxASSERT(false);
1324 ResetNextIndex();
1325 return false;
1326 }
1327
1328 QueryColourFromUser(value);
1329 }
1330 else
1331 {
1332 wxColourPropertyValue val;
1333
1334 bool done = false;
1335
1336 if ( colourName.length() )
1337 {
1338 // Try predefined colour first
1339 bool res = wxEnumProperty::StringToValue(value, colourName, argFlags);
1340 if ( res && GetIndex() >= 0 )
1341 {
1342 val.m_type = GetIndex();
1343 if ( val.m_type < m_choices.GetCount() )
1344 val.m_type = m_choices[val.m_type].GetValue();
1345
1346 // Get proper colour for type.
1347 val.m_colour = GetColour(val.m_type);
1348
1349 done = true;
1350 }
1351 }
1352 if ( colourRGB.length() && !done )
1353 {
1354 // Then check custom colour.
1355 val.m_type = wxPG_COLOUR_CUSTOM;
1356
1357 int r = -1, g = -1, b = -1;
1358 wxSscanf(colourRGB.c_str(),wxT("(%i,%i,%i)"),&r,&g,&b);
1359
1360 if ( r >= 0 && r <= 255 &&
1361 g >= 0 && g <= 255 &&
1362 b >= 0 && b <= 255 )
1363 {
1364 val.m_colour.Set(r,g,b);
1365
1366 done = true;
1367 }
1368 }
1369
1370 if ( !done )
1371 {
1372 ResetNextIndex();
1373 return false;
1374 }
1375
1376 value = DoTranslateVal(val);
1377 }
1378
1379 return true;
1380 }
1381
1382
1383 bool wxSystemColourProperty::DoSetAttribute( const wxString& name, wxVariant& value )
1384 {
1385 if ( name == wxPG_COLOUR_ALLOW_CUSTOM )
1386 {
1387 int ival = wxPGVariantToInt(value);
1388
1389 SetChoicesExclusive(); // Make sure we don't corrupt colour lists of other properties
1390
1391 if ( ival && (m_flags & wxPG_PROP_HIDE_CUSTOM_COLOUR) )
1392 {
1393 // Show custom choice
1394 m_choices.Insert(wxT("Custom"), GetCustomColourIndex(), wxPG_COLOUR_CUSTOM);
1395 m_flags &= ~(wxPG_PROP_HIDE_CUSTOM_COLOUR);
1396 }
1397 else if ( !ival && !(m_flags & wxPG_PROP_HIDE_CUSTOM_COLOUR) )
1398 {
1399 // Hide custom choice
1400 m_choices.RemoveAt(GetCustomColourIndex());
1401 m_flags |= wxPG_PROP_HIDE_CUSTOM_COLOUR;
1402 }
1403 return true;
1404 }
1405 return false;
1406 }
1407
1408
1409 // -----------------------------------------------------------------------
1410 // wxColourProperty
1411 // -----------------------------------------------------------------------
1412
1413 static const wxChar* gs_cp_es_normcolour_labels[] = {
1414 wxT("Black"),
1415 wxT("Maroon"),
1416 wxT("Navy"),
1417 wxT("Purple"),
1418 wxT("Teal"),
1419 wxT("Gray"),
1420 wxT("Green"),
1421 wxT("Olive"),
1422 wxT("Brown"),
1423 wxT("Blue"),
1424 wxT("Fuchsia"),
1425 wxT("Red"),
1426 wxT("Orange"),
1427 wxT("Silver"),
1428 wxT("Lime"),
1429 wxT("Aqua"),
1430 wxT("Yellow"),
1431 wxT("White"),
1432 wxT("Custom"),
1433 (const wxChar*) NULL
1434 };
1435
1436 static unsigned long gs_cp_es_normcolour_colours[] = {
1437 wxPG_COLOUR(0,0,0),
1438 wxPG_COLOUR(128,0,0),
1439 wxPG_COLOUR(0,0,128),
1440 wxPG_COLOUR(128,0,128),
1441 wxPG_COLOUR(0,128,128),
1442 wxPG_COLOUR(128,128,128),
1443 wxPG_COLOUR(0,128,0),
1444 wxPG_COLOUR(128,128,0),
1445 wxPG_COLOUR(166,124,81),
1446 wxPG_COLOUR(0,0,255),
1447 wxPG_COLOUR(255,0,255),
1448 wxPG_COLOUR(255,0,0),
1449 wxPG_COLOUR(247,148,28),
1450 wxPG_COLOUR(192,192,192),
1451 wxPG_COLOUR(0,255,0),
1452 wxPG_COLOUR(0,255,255),
1453 wxPG_COLOUR(255,255,0),
1454 wxPG_COLOUR(255,255,255),
1455 wxPG_COLOUR(0,0,0)
1456 };
1457
1458 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxColourProperty, wxSystemColourProperty,
1459 wxColour, const wxColour&, TextCtrlAndButton)
1460
1461 static wxPGChoices gs_wxColourProperty_choicesCache;
1462
1463 wxColourProperty::wxColourProperty( const wxString& label,
1464 const wxString& name,
1465 const wxColour& value )
1466 : wxSystemColourProperty(label, name, gs_cp_es_normcolour_labels,
1467 NULL,
1468 &gs_wxColourProperty_choicesCache, value )
1469 {
1470 Init( value );
1471
1472 m_flags |= wxPG_PROP_TRANSLATE_CUSTOM;
1473 }
1474
1475 wxColourProperty::~wxColourProperty()
1476 {
1477 }
1478
1479 void wxColourProperty::Init( wxColour colour )
1480 {
1481 if ( !colour.Ok() )
1482 colour = *wxWHITE;
1483 wxVariant variant;
1484 variant << colour;
1485 m_value = variant;
1486 int ind = ColToInd(colour);
1487 if ( ind < 0 )
1488 ind = m_choices.GetCount() - 1;
1489 SetIndex( ind );
1490 }
1491
1492 wxString wxColourProperty::ValueToString( wxVariant& value,
1493 int argFlags ) const
1494 {
1495 const wxPGEditor* editor = GetEditorClass();
1496 if ( editor != wxPGEditor_Choice &&
1497 editor != wxPGEditor_ChoiceAndButton &&
1498 editor != wxPGEditor_ComboBox )
1499 argFlags |= wxPG_PROPERTY_SPECIFIC;
1500
1501 return wxSystemColourProperty::ValueToString(value, argFlags);
1502 }
1503
1504 wxColour wxColourProperty::GetColour( int index ) const
1505 {
1506 return gs_cp_es_normcolour_colours[m_choices.GetValue(index)];
1507 }
1508
1509 wxVariant wxColourProperty::DoTranslateVal( wxColourPropertyValue& v ) const
1510 {
1511 wxVariant variant;
1512 variant << v.m_colour;
1513 return variant;
1514 }
1515
1516 // -----------------------------------------------------------------------
1517 // wxCursorProperty
1518 // -----------------------------------------------------------------------
1519
1520 #define wxPG_CURSOR_IMAGE_WIDTH 32
1521
1522 #define NUM_CURSORS 28
1523
1524 //#define wx_cp_es_syscursors_len 28
1525 static const wxChar* gs_cp_es_syscursors_labels[NUM_CURSORS+1] = {
1526 wxT("Default"),
1527 wxT("Arrow"),
1528 wxT("Right Arrow"),
1529 wxT("Blank"),
1530 wxT("Bullseye"),
1531 wxT("Character"),
1532 wxT("Cross"),
1533 wxT("Hand"),
1534 wxT("I-Beam"),
1535 wxT("Left Button"),
1536 wxT("Magnifier"),
1537 wxT("Middle Button"),
1538 wxT("No Entry"),
1539 wxT("Paint Brush"),
1540 wxT("Pencil"),
1541 wxT("Point Left"),
1542 wxT("Point Right"),
1543 wxT("Question Arrow"),
1544 wxT("Right Button"),
1545 wxT("Sizing NE-SW"),
1546 wxT("Sizing N-S"),
1547 wxT("Sizing NW-SE"),
1548 wxT("Sizing W-E"),
1549 wxT("Sizing"),
1550 wxT("Spraycan"),
1551 wxT("Wait"),
1552 wxT("Watch"),
1553 wxT("Wait Arrow"),
1554 (const wxChar*) NULL
1555 };
1556
1557 static long gs_cp_es_syscursors_values[NUM_CURSORS] = {
1558 wxCURSOR_NONE,
1559 wxCURSOR_ARROW,
1560 wxCURSOR_RIGHT_ARROW,
1561 wxCURSOR_BLANK,
1562 wxCURSOR_BULLSEYE,
1563 wxCURSOR_CHAR,
1564 wxCURSOR_CROSS,
1565 wxCURSOR_HAND,
1566 wxCURSOR_IBEAM,
1567 wxCURSOR_LEFT_BUTTON,
1568 wxCURSOR_MAGNIFIER,
1569 wxCURSOR_MIDDLE_BUTTON,
1570 wxCURSOR_NO_ENTRY,
1571 wxCURSOR_PAINT_BRUSH,
1572 wxCURSOR_PENCIL,
1573 wxCURSOR_POINT_LEFT,
1574 wxCURSOR_POINT_RIGHT,
1575 wxCURSOR_QUESTION_ARROW,
1576 wxCURSOR_RIGHT_BUTTON,
1577 wxCURSOR_SIZENESW,
1578 wxCURSOR_SIZENS,
1579 wxCURSOR_SIZENWSE,
1580 wxCURSOR_SIZEWE,
1581 wxCURSOR_SIZING,
1582 wxCURSOR_SPRAYCAN,
1583 wxCURSOR_WAIT,
1584 wxCURSOR_WATCH,
1585 wxCURSOR_ARROWWAIT
1586 };
1587
1588 IMPLEMENT_DYNAMIC_CLASS(wxCursorProperty, wxEnumProperty)
1589
1590 wxCursorProperty::wxCursorProperty( const wxString& label, const wxString& name,
1591 int value )
1592 : wxEnumProperty( label,
1593 name,
1594 gs_cp_es_syscursors_labels,
1595 gs_cp_es_syscursors_values,
1596 value )
1597 {
1598 m_flags |= wxPG_PROP_STATIC_CHOICES; // Cursor selection cannot be changed.
1599 }
1600
1601 wxCursorProperty::~wxCursorProperty()
1602 {
1603 }
1604
1605 wxSize wxCursorProperty::OnMeasureImage( int item ) const
1606 {
1607 #if wxPG_CAN_DRAW_CURSOR
1608 if ( item != -1 && item < NUM_CURSORS )
1609 return wxSize(wxPG_CURSOR_IMAGE_WIDTH,wxPG_CURSOR_IMAGE_WIDTH);
1610 #else
1611 wxUnusedVar(item);
1612 #endif
1613 return wxSize(0,0);
1614 }
1615
1616 #if wxPG_CAN_DRAW_CURSOR
1617
1618 void wxCursorProperty::OnCustomPaint( wxDC& dc,
1619 const wxRect& rect,
1620 wxPGPaintData& paintdata )
1621 {
1622 // Background brush
1623 dc.SetBrush( wxSystemSettings::GetColour( wxSYS_COLOUR_BTNFACE ) );
1624
1625 if ( paintdata.m_choiceItem >= 0 )
1626 {
1627 dc.DrawRectangle( rect );
1628
1629 if ( paintdata.m_choiceItem < NUM_CURSORS )
1630 {
1631 wxStockCursor cursorIndex =
1632 (wxStockCursor) gs_cp_es_syscursors_values[paintdata.m_choiceItem];
1633
1634 {
1635 if ( cursorIndex == wxCURSOR_NONE )
1636 cursorIndex = wxCURSOR_ARROW;
1637
1638 wxCursor cursor( cursorIndex );
1639
1640 #ifdef __WXMSW__
1641 HDC hDc = (HDC)((const wxMSWDCImpl *)dc.GetImpl())->GetHDC();
1642 ::DrawIconEx( hDc,
1643 rect.x,
1644 rect.y,
1645 (HICON)cursor.GetHandle(),
1646 0,
1647 0,
1648 0,
1649 NULL,
1650 #if !defined(__WXWINCE__)
1651 DI_COMPAT | DI_DEFAULTSIZE |
1652 #endif
1653 DI_NORMAL
1654 );
1655 #endif
1656 }
1657 }
1658 }
1659 }
1660
1661 #else
1662 void wxCursorProperty::OnCustomPaint( wxDC&, const wxRect&, wxPGPaintData& ) { }
1663 /*wxPGCellRenderer* wxCursorProperty::GetCellRenderer( int column ) const
1664 {
1665 return wxEnumProperty::GetCellRenderer(column);
1666 }*/
1667 #endif
1668
1669 // -----------------------------------------------------------------------
1670 // wxImageFileProperty
1671 // -----------------------------------------------------------------------
1672
1673 #if wxUSE_IMAGE
1674
1675 const wxString& wxPGGetDefaultImageWildcard()
1676 {
1677 // Form the wildcard, if not done yet
1678 if ( !wxPGGlobalVars->m_pDefaultImageWildcard.length() )
1679 {
1680
1681 wxString str;
1682
1683 // TODO: This section may require locking (using global).
1684
1685 wxList& handlers = wxImage::GetHandlers();
1686
1687 wxList::iterator node;
1688
1689 // Let's iterate over the image handler list.
1690 //for ( wxList::Node *node = handlers.GetFirst(); node; node = node->GetNext() )
1691 for ( node = handlers.begin(); node != handlers.end(); ++node )
1692 {
1693 wxImageHandler *handler = (wxImageHandler*)*node;
1694
1695 wxString ext_lo = handler->GetExtension();
1696 wxString ext_up = ext_lo.Upper();
1697
1698 str.append( ext_up );
1699 str.append( wxT(" files (*.") );
1700 str.append( ext_up );
1701 str.append( wxT(")|*.") );
1702 str.append( ext_lo );
1703 str.append( wxT("|") );
1704 }
1705
1706 str.append ( wxT("All files (*.*)|*.*") );
1707
1708 wxPGGlobalVars->m_pDefaultImageWildcard = str;
1709 }
1710
1711 return wxPGGlobalVars->m_pDefaultImageWildcard;
1712 }
1713
1714 IMPLEMENT_DYNAMIC_CLASS(wxImageFileProperty, wxFileProperty)
1715
1716 wxImageFileProperty::wxImageFileProperty( const wxString& label, const wxString& name,
1717 const wxString& value )
1718 : wxFileProperty(label,name,value)
1719 {
1720 SetAttribute( wxPG_FILE_WILDCARD, wxPGGetDefaultImageWildcard() );
1721
1722 m_pImage = (wxImage*) NULL;
1723 m_pBitmap = (wxBitmap*) NULL;
1724 }
1725
1726 wxImageFileProperty::~wxImageFileProperty()
1727 {
1728 if ( m_pBitmap )
1729 delete m_pBitmap;
1730 if ( m_pImage )
1731 delete m_pImage;
1732 }
1733
1734 void wxImageFileProperty::OnSetValue()
1735 {
1736 wxFileProperty::OnSetValue();
1737
1738 // Delete old image
1739 if ( m_pImage )
1740 {
1741 delete m_pImage;
1742 m_pImage = NULL;
1743 }
1744 if ( m_pBitmap )
1745 {
1746 delete m_pBitmap;
1747 m_pBitmap = NULL;
1748 }
1749
1750 wxFileName filename = GetFileName();
1751
1752 // Create the image thumbnail
1753 if ( filename.FileExists() )
1754 {
1755 m_pImage = new wxImage( filename.GetFullPath() );
1756 }
1757 }
1758
1759 wxSize wxImageFileProperty::OnMeasureImage( int ) const
1760 {
1761 return wxPG_DEFAULT_IMAGE_SIZE;
1762 }
1763
1764 void wxImageFileProperty::OnCustomPaint( wxDC& dc,
1765 const wxRect& rect,
1766 wxPGPaintData& )
1767 {
1768 if ( m_pBitmap || (m_pImage && m_pImage->Ok() ) )
1769 {
1770 // Draw the thumbnail
1771
1772 // Create the bitmap here because required size is not known in OnSetValue().
1773 if ( !m_pBitmap )
1774 {
1775 m_pImage->Rescale( rect.width, rect.height );
1776 m_pBitmap = new wxBitmap( *m_pImage );
1777 delete m_pImage;
1778 m_pImage = NULL;
1779 }
1780
1781 dc.DrawBitmap( *m_pBitmap, rect.x, rect.y, false );
1782 }
1783 else
1784 {
1785 // No file - just draw a white box
1786 dc.SetBrush( *wxWHITE_BRUSH );
1787 dc.DrawRectangle ( rect );
1788 }
1789 }
1790
1791 #endif // wxUSE_IMAGE
1792
1793 // -----------------------------------------------------------------------
1794 // wxMultiChoiceProperty
1795 // -----------------------------------------------------------------------
1796
1797 #if wxUSE_CHOICEDLG
1798
1799 #include "wx/choicdlg.h"
1800
1801 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxMultiChoiceProperty,wxPGProperty,
1802 wxArrayInt,const wxArrayInt&,TextCtrlAndButton)
1803
1804 wxMultiChoiceProperty::wxMultiChoiceProperty( const wxString& label,
1805 const wxString& name,
1806 const wxPGChoices& choices,
1807 const wxArrayString& value)
1808 : wxPGProperty(label,name)
1809 {
1810 m_choices.Assign(choices);
1811 SetValue(value);
1812 }
1813
1814 wxMultiChoiceProperty::wxMultiChoiceProperty( const wxString& label,
1815 const wxString& name,
1816 const wxArrayString& strings,
1817 const wxArrayString& value)
1818 : wxPGProperty(label,name)
1819 {
1820 m_choices.Set(strings);
1821 SetValue(value);
1822 }
1823
1824 wxMultiChoiceProperty::wxMultiChoiceProperty( const wxString& label,
1825 const wxString& name,
1826 const wxArrayString& value)
1827 : wxPGProperty(label,name)
1828 {
1829 wxArrayString strings;
1830 m_choices.Set(strings);
1831 SetValue(value);
1832 }
1833
1834 wxMultiChoiceProperty::~wxMultiChoiceProperty()
1835 {
1836 }
1837
1838 void wxMultiChoiceProperty::OnSetValue()
1839 {
1840 GenerateValueAsString(m_value, &m_display);
1841 }
1842
1843 wxString wxMultiChoiceProperty::ValueToString( wxVariant& value,
1844 int argFlags ) const
1845 {
1846 // If possible, use cached string
1847 if ( argFlags & wxPG_VALUE_IS_CURRENT )
1848 return m_display;
1849
1850 wxString s;
1851 GenerateValueAsString(value, &s);
1852 return s;
1853 }
1854
1855 void wxMultiChoiceProperty::GenerateValueAsString( wxVariant& value,
1856 wxString* target ) const
1857 {
1858 wxArrayString strings;
1859
1860 if ( value.GetType() == wxPG_VARIANT_TYPE_ARRSTRING )
1861 strings = value.GetArrayString();
1862
1863 wxString& tempStr = *target;
1864 unsigned int i;
1865 unsigned int itemCount = strings.size();
1866
1867 tempStr.Empty();
1868
1869 if ( itemCount )
1870 tempStr.append( wxT("\"") );
1871
1872 for ( i = 0; i < itemCount; i++ )
1873 {
1874 tempStr.append( strings[i] );
1875 tempStr.append( wxT("\"") );
1876 if ( i < (itemCount-1) )
1877 tempStr.append ( wxT(" \"") );
1878 }
1879 }
1880
1881 wxArrayInt wxMultiChoiceProperty::GetValueAsIndices() const
1882 {
1883 const wxArrayInt& valueArr = wxArrayIntRefFromVariant(GetValue());
1884 unsigned int i;
1885
1886 // Translate values to string indices.
1887 wxArrayInt selections;
1888
1889 if ( !m_choices.IsOk() || !m_choices.GetCount() || !(&valueArr) )
1890 {
1891 for ( i=0; i<valueArr.size(); i++ )
1892 selections.Add(-1);
1893 }
1894 else
1895 {
1896 for ( i=0; i<valueArr.size(); i++ )
1897 {
1898 int sIndex = m_choices.Index(valueArr[i]);
1899 if ( sIndex >= 0 )
1900 selections.Add(sIndex);
1901 }
1902 }
1903
1904 return selections;
1905 }
1906
1907 bool wxMultiChoiceProperty::OnEvent( wxPropertyGrid* propgrid,
1908 wxWindow* WXUNUSED(primary),
1909 wxEvent& event )
1910 {
1911 if ( propgrid->IsMainButtonEvent(event) )
1912 {
1913 // Update the value
1914 wxVariant useValue = propgrid->GetUncommittedPropertyValue();
1915
1916 wxArrayString labels = m_choices.GetLabels();
1917 unsigned int choiceCount;
1918
1919 if ( m_choices.IsOk() )
1920 choiceCount = m_choices.GetCount();
1921 else
1922 choiceCount = 0;
1923
1924 // launch editor dialog
1925 wxMultiChoiceDialog dlg( propgrid,
1926 _("Make a selection:"),
1927 m_label,
1928 choiceCount,
1929 choiceCount?&labels[0]:NULL,
1930 wxCHOICEDLG_STYLE );
1931
1932 dlg.Move( propgrid->GetGoodEditorDialogPosition(this,dlg.GetSize()) );
1933
1934 wxArrayString strings = useValue.GetArrayString();
1935 wxArrayString extraStrings;
1936
1937 dlg.SetSelections(m_choices.GetIndicesForStrings(strings, &extraStrings));
1938
1939 if ( dlg.ShowModal() == wxID_OK && choiceCount )
1940 {
1941 int userStringMode = GetAttributeAsLong(wxT("UserStringMode"), 0);
1942
1943 wxArrayInt arrInt = dlg.GetSelections();
1944
1945 wxVariant variant;
1946
1947 // Strings that were not in list of choices
1948 wxArrayString value;
1949
1950 // Translate string indices to strings
1951
1952 unsigned int n;
1953 if ( userStringMode == 1 )
1954 {
1955 for (n=0;n<extraStrings.size();n++)
1956 value.push_back(extraStrings[n]);
1957 }
1958
1959 unsigned int i;
1960 for ( i=0; i<arrInt.size(); i++ )
1961 value.Add(m_choices.GetLabel(arrInt.Item(i)));
1962
1963 if ( userStringMode == 2 )
1964 {
1965 for (n=0;n<extraStrings.size();n++)
1966 value.push_back(extraStrings[n]);
1967 }
1968
1969 variant = WXVARIANT(value);
1970
1971 SetValueInEvent(variant);
1972
1973 return true;
1974 }
1975 }
1976 return false;
1977 }
1978
1979 bool wxMultiChoiceProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const
1980 {
1981 wxArrayString arr;
1982
1983 int userStringMode = GetAttributeAsLong(wxT("UserStringMode"), 0);
1984
1985 WX_PG_TOKENIZER2_BEGIN(text,wxT('"'))
1986 if ( userStringMode > 0 || (m_choices.IsOk() && m_choices.Index( token ) != wxNOT_FOUND) )
1987 arr.Add(token);
1988 WX_PG_TOKENIZER2_END()
1989
1990 wxVariant v( WXVARIANT(arr) );
1991 variant = v;
1992
1993 return true;
1994 }
1995
1996 #endif // wxUSE_CHOICEDLG
1997
1998
1999 // -----------------------------------------------------------------------
2000 // wxDateProperty
2001 // -----------------------------------------------------------------------
2002
2003 #if wxUSE_DATETIME
2004
2005
2006 #if wxUSE_DATEPICKCTRL
2007 #define dtCtrl DatePickerCtrl
2008 #else
2009 #define dtCtrl TextCtrl
2010 #endif
2011
2012 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxDateProperty,
2013 wxPGProperty,
2014 wxDateTime,
2015 const wxDateTime&,
2016 dtCtrl)
2017
2018
2019 wxString wxDateProperty::ms_defaultDateFormat;
2020
2021
2022 wxDateProperty::wxDateProperty( const wxString& label,
2023 const wxString& name,
2024 const wxDateTime& value )
2025 : wxPGProperty(label,name)
2026 {
2027 //wxPGRegisterDefaultValueType(wxDateTime)
2028
2029 #if wxUSE_DATEPICKCTRL
2030 wxPGRegisterEditorClass(DatePickerCtrl);
2031
2032 m_dpStyle = wxDP_DEFAULT | wxDP_SHOWCENTURY;
2033 #else
2034 m_dpStyle = 0;
2035 #endif
2036
2037 SetValue( value );
2038 }
2039
2040 wxDateProperty::~wxDateProperty()
2041 {
2042 }
2043
2044 bool wxDateProperty::StringToValue( wxVariant& variant, const wxString& text,
2045 int WXUNUSED(argFlags) ) const
2046 {
2047 wxDateTime dt;
2048
2049 const char* c = dt.ParseFormat(text, wxString(wxDefaultDateTimeFormat), wxDefaultDateTime, NULL);
2050
2051 if ( c )
2052 {
2053 variant = dt;
2054 return true;
2055 }
2056
2057 return false;
2058 }
2059
2060 wxString wxDateProperty::ValueToString( wxVariant& value,
2061 int argFlags ) const
2062 {
2063 const wxChar* format = (const wxChar*) NULL;
2064
2065 wxDateTime dateTime = value.GetDateTime();
2066
2067 if ( !dateTime.IsValid() )
2068 return wxT("Invalid");
2069
2070 if ( !ms_defaultDateFormat.length() )
2071 {
2072 #if wxUSE_DATEPICKCTRL
2073 bool showCentury = m_dpStyle & wxDP_SHOWCENTURY ? true : false;
2074 #else
2075 bool showCentury = true;
2076 #endif
2077 ms_defaultDateFormat = DetermineDefaultDateFormat( showCentury );
2078 }
2079
2080 if ( m_format.length() &&
2081 !(argFlags & wxPG_FULL_VALUE) )
2082 format = m_format.c_str();
2083
2084 // Determine default from locale
2085 // NB: This is really simple stuff, but can't figure anything
2086 // better without proper support in wxLocale
2087 if ( !format )
2088 format = ms_defaultDateFormat.c_str();
2089
2090 return dateTime.Format(format);
2091 }
2092
2093 wxString wxDateProperty::DetermineDefaultDateFormat( bool showCentury )
2094 {
2095 // This code is basicly copied from datectlg.cpp's SetFormat
2096 //
2097 wxString format;
2098
2099 wxDateTime dt;
2100 dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
2101 wxString str(dt.Format(wxT("%x")));
2102
2103 const wxChar *p = str.c_str();
2104 while ( *p )
2105 {
2106 int n=wxAtoi(p);
2107 if (n == dt.GetDay())
2108 {
2109 format.Append(wxT("%d"));
2110 p += 2;
2111 }
2112 else if (n == (int)dt.GetMonth()+1)
2113 {
2114 format.Append(wxT("%m"));
2115 p += 2;
2116 }
2117 else if (n == dt.GetYear())
2118 {
2119 format.Append(wxT("%Y"));
2120 p += 4;
2121 }
2122 else if (n == (dt.GetYear() % 100))
2123 {
2124 if (showCentury)
2125 format.Append(wxT("%Y"));
2126 else
2127 format.Append(wxT("%y"));
2128 p += 2;
2129 }
2130 else
2131 format.Append(*p++);
2132 }
2133
2134 return format;
2135 }
2136
2137 bool wxDateProperty::DoSetAttribute( const wxString& name, wxVariant& value )
2138 {
2139 if ( name == wxPG_DATE_FORMAT )
2140 {
2141 m_format = value.GetString();
2142 return true;
2143 }
2144 else if ( name == wxPG_DATE_PICKER_STYLE )
2145 {
2146 m_dpStyle = value.GetLong();
2147 ms_defaultDateFormat.clear(); // This may need recalculation
2148 return true;
2149 }
2150 return false;
2151 }
2152
2153 #endif // wxUSE_DATETIME
2154
2155
2156 // -----------------------------------------------------------------------
2157 // wxPropertyGridInterface
2158 // -----------------------------------------------------------------------
2159
2160 void wxPropertyGridInterface::InitAllTypeHandlers()
2161 {
2162 }
2163
2164 // -----------------------------------------------------------------------
2165
2166 void wxPropertyGridInterface::RegisterAdditionalEditors()
2167 {
2168 // Register editor classes, if necessary.
2169 if ( wxPGGlobalVars->m_mapEditorClasses.empty() )
2170 wxPropertyGrid::RegisterDefaultEditors();
2171
2172 #if wxUSE_SPINBTN
2173 wxPGRegisterEditorClass(SpinCtrl);
2174 #endif
2175 #if wxUSE_DATEPICKCTRL
2176 wxPGRegisterEditorClass(DatePickerCtrl);
2177 #endif
2178 }
2179
2180 // -----------------------------------------------------------------------
2181
2182 #endif // wxPG_INCLUDE_ADVPROPS
2183
2184 #endif // wxUSE_PROPGRID
2185