1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/propgrid/editors.cpp
3 // Purpose:     wxPropertyGrid editors
4 // Author:      Jaakko Salli
5 // Modified by:
6 // Created:     2007-04-14
7 // Copyright:   (c) Jaakko Salli
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // For compilers that support precompilation, includes "wx/wx.h".
12 #include "wx/wxprec.h"
13 
14 #ifdef __BORLANDC__
15     #pragma hdrstop
16 #endif
17 
18 #if wxUSE_PROPGRID
19 
20 #ifndef WX_PRECOMP
21     #include "wx/defs.h"
22     #include "wx/object.h"
23     #include "wx/hash.h"
24     #include "wx/string.h"
25     #include "wx/log.h"
26     #include "wx/event.h"
27     #include "wx/window.h"
28     #include "wx/panel.h"
29     #include "wx/dc.h"
30     #include "wx/dcclient.h"
31     #include "wx/dcmemory.h"
32     #include "wx/button.h"
33     #include "wx/pen.h"
34     #include "wx/brush.h"
35     #include "wx/cursor.h"
36     #include "wx/dialog.h"
37     #include "wx/settings.h"
38     #include "wx/msgdlg.h"
39     #include "wx/choice.h"
40     #include "wx/stattext.h"
41     #include "wx/scrolwin.h"
42     #include "wx/dirdlg.h"
43     #include "wx/sizer.h"
44     #include "wx/textdlg.h"
45     #include "wx/filedlg.h"
46     #include "wx/statusbr.h"
47     #include "wx/intl.h"
48     #include "wx/frame.h"
49 #endif
50 
51 
52 #include "wx/timer.h"
53 #include "wx/dcbuffer.h"
54 #include "wx/bmpbuttn.h"
55 
56 
57 // This define is necessary to prevent macro clearing
58 #define __wxPG_SOURCE_FILE__
59 
60 #include "wx/propgrid/propgrid.h"
61 #include "wx/propgrid/editors.h"
62 #include "wx/propgrid/props.h"
63 
64 #if wxPG_USE_RENDERER_NATIVE
65     #include "wx/renderer.h"
66 #endif
67 
68 // How many pixels between textctrl and button
69 #ifdef __WXMAC__
70     #define wxPG_TEXTCTRL_AND_BUTTON_SPACING        4
71 #else
72     #define wxPG_TEXTCTRL_AND_BUTTON_SPACING        2
73 #endif
74 
75 #define wxPG_BUTTON_SIZEDEC                         0
76 
77 #include "wx/odcombo.h"
78 
79 // -----------------------------------------------------------------------
80 
81 #if defined(__WXMSW__)
82     // tested
83     #define wxPG_NAT_BUTTON_BORDER_ANY          1
84     #define wxPG_NAT_BUTTON_BORDER_X            1
85     #define wxPG_NAT_BUTTON_BORDER_Y            1
86 
87     #define wxPG_CHECKMARK_XADJ                 1
88     #define wxPG_CHECKMARK_YADJ                 (-1)
89     #define wxPG_CHECKMARK_WADJ                 0
90     #define wxPG_CHECKMARK_HADJ                 0
91     #define wxPG_CHECKMARK_DEFLATE              0
92 
93     #define wxPG_TEXTCTRLYADJUST                (m_spacingy+0)
94 
95 #elif defined(__WXGTK__)
96     // tested
97     #define wxPG_CHECKMARK_XADJ                 1
98     #define wxPG_CHECKMARK_YADJ                 1
99     #define wxPG_CHECKMARK_WADJ                 (-2)
100     #define wxPG_CHECKMARK_HADJ                 (-2)
101     #define wxPG_CHECKMARK_DEFLATE              3
102 
103     #define wxPG_NAT_BUTTON_BORDER_ANY      1
104     #define wxPG_NAT_BUTTON_BORDER_X        1
105     #define wxPG_NAT_BUTTON_BORDER_Y        1
106 
107     #define wxPG_TEXTCTRLYADJUST            0
108 
109 #elif defined(__WXMAC__)
110     // *not* tested
111     #define wxPG_CHECKMARK_XADJ                 4
112     #define wxPG_CHECKMARK_YADJ                 4
113     #define wxPG_CHECKMARK_WADJ                 -6
114     #define wxPG_CHECKMARK_HADJ                 -6
115     #define wxPG_CHECKMARK_DEFLATE              0
116 
117     #define wxPG_NAT_BUTTON_BORDER_ANY      0
118     #define wxPG_NAT_BUTTON_BORDER_X        0
119     #define wxPG_NAT_BUTTON_BORDER_Y        0
120 
121     #define wxPG_TEXTCTRLYADJUST            0
122 
123 #else
124     // defaults
125     #define wxPG_CHECKMARK_XADJ                 0
126     #define wxPG_CHECKMARK_YADJ                 0
127     #define wxPG_CHECKMARK_WADJ                 0
128     #define wxPG_CHECKMARK_HADJ                 0
129     #define wxPG_CHECKMARK_DEFLATE              0
130 
131     #define wxPG_NAT_BUTTON_BORDER_ANY      0
132     #define wxPG_NAT_BUTTON_BORDER_X        0
133     #define wxPG_NAT_BUTTON_BORDER_Y        0
134 
135     #define wxPG_TEXTCTRLYADJUST            0
136 
137 #endif
138 
139 // for odcombo
140 #ifdef __WXMAC__
141 #define wxPG_CHOICEXADJUST           -3 // required because wxComboCtrl reserves 3pixels for wxTextCtrl's focus ring
142 #define wxPG_CHOICEYADJUST           -3
143 #else
144 #define wxPG_CHOICEXADJUST           0
145 #define wxPG_CHOICEYADJUST           0
146 #endif
147 
148 // Number added to image width for SetCustomPaintWidth
149 #define ODCB_CUST_PAINT_MARGIN               6
150 
151 // Milliseconds to wait for two mouse-ups after focus in order
152 // to trigger a double-click.
153 #define DOUBLE_CLICK_CONVERSION_TRESHOLD        500
154 
155 // -----------------------------------------------------------------------
156 // wxPGEditor
157 // -----------------------------------------------------------------------
158 
IMPLEMENT_ABSTRACT_CLASS(wxPGEditor,wxObject)159 IMPLEMENT_ABSTRACT_CLASS(wxPGEditor, wxObject)
160 
161 
162 wxPGEditor::~wxPGEditor()
163 {
164 }
165 
GetName() const166 wxString wxPGEditor::GetName() const
167 {
168     return GetClassInfo()->GetClassName();
169 }
170 
DrawValue(wxDC & dc,const wxRect & rect,wxPGProperty * WXUNUSED (property),const wxString & text) const171 void wxPGEditor::DrawValue( wxDC& dc, const wxRect& rect,
172                             wxPGProperty* WXUNUSED(property),
173                             const wxString& text ) const
174 {
175     dc.DrawText( text, rect.x+wxPG_XBEFORETEXT, rect.y );
176 }
177 
GetValueFromControl(wxVariant &,wxPGProperty *,wxWindow *) const178 bool wxPGEditor::GetValueFromControl( wxVariant&, wxPGProperty*, wxWindow* ) const
179 {
180     return false;
181 }
182 
SetControlStringValue(wxPGProperty * WXUNUSED (property),wxWindow *,const wxString &) const183 void wxPGEditor::SetControlStringValue( wxPGProperty* WXUNUSED(property), wxWindow*, const wxString& ) const
184 {
185 }
186 
187 
SetControlIntValue(wxPGProperty * WXUNUSED (property),wxWindow *,int) const188 void wxPGEditor::SetControlIntValue( wxPGProperty* WXUNUSED(property), wxWindow*, int ) const
189 {
190 }
191 
192 
InsertItem(wxWindow *,const wxString &,int) const193 int wxPGEditor::InsertItem( wxWindow*, const wxString&, int ) const
194 {
195     return -1;
196 }
197 
198 
DeleteItem(wxWindow *,int) const199 void wxPGEditor::DeleteItem( wxWindow*, int ) const
200 {
201     return;
202 }
203 
204 
OnFocus(wxPGProperty *,wxWindow *) const205 void wxPGEditor::OnFocus( wxPGProperty*, wxWindow* ) const
206 {
207 }
208 
SetControlAppearance(wxPropertyGrid * pg,wxPGProperty * property,wxWindow * ctrl,const wxPGCell & cell,const wxPGCell & oCell,bool unspecified) const209 void wxPGEditor::SetControlAppearance( wxPropertyGrid* pg,
210                                        wxPGProperty* property,
211                                        wxWindow* ctrl,
212                                        const wxPGCell& cell,
213                                        const wxPGCell& oCell,
214                                        bool unspecified ) const
215 {
216     // Get old editor appearance
217     wxTextCtrl* tc = NULL;
218     wxComboCtrl* cb = NULL;
219     if ( wxDynamicCast(ctrl, wxTextCtrl) )
220     {
221         tc = (wxTextCtrl*) ctrl;
222     }
223     else
224     {
225         if ( wxDynamicCast(ctrl, wxComboCtrl) )
226         {
227             cb = (wxComboCtrl*) ctrl;
228             tc = cb->GetTextCtrl();
229         }
230     }
231 
232     if ( tc || cb )
233     {
234         wxString tcText;
235         bool changeText = false;
236 
237         if ( cell.HasText() && !pg->IsEditorFocused() )
238         {
239             tcText = cell.GetText();
240             changeText = true;
241         }
242         else if ( oCell.HasText() )
243         {
244             tcText = property->GetValueAsString(
245                 property->HasFlag(wxPG_PROP_READONLY)?0:wxPG_EDITABLE_VALUE);
246             changeText = true;
247         }
248 
249         if ( changeText )
250         {
251             // This prevents value from being modified
252             if ( tc )
253             {
254                 pg->SetupTextCtrlValue(tcText);
255                 tc->SetValue(tcText);
256             }
257             else
258             {
259                 cb->SetText(tcText);
260             }
261         }
262     }
263 
264     // Do not make the mistake of calling GetClassDefaultAttributes()
265     // here. It is static, while GetDefaultAttributes() is virtual
266     // and the correct one to use.
267     wxVisualAttributes vattrs = ctrl->GetDefaultAttributes();
268 
269     // Foreground colour
270     const wxColour& fgCol = cell.GetFgCol();
271     if ( fgCol.IsOk() )
272     {
273         ctrl->SetForegroundColour(fgCol);
274     }
275     else if ( oCell.GetFgCol().IsOk() )
276     {
277         ctrl->SetForegroundColour(vattrs.colFg);
278     }
279 
280     // Background colour
281     const wxColour& bgCol = cell.GetBgCol();
282     if ( bgCol.IsOk() )
283     {
284         ctrl->SetBackgroundColour(bgCol);
285     }
286     else if ( oCell.GetBgCol().IsOk() )
287     {
288         ctrl->SetBackgroundColour(vattrs.colBg);
289     }
290 
291     // Font
292     const wxFont& font = cell.GetFont();
293     if ( font.IsOk() )
294     {
295         ctrl->SetFont(font);
296     }
297     else if ( oCell.GetFont().IsOk() )
298     {
299         ctrl->SetFont(vattrs.font);
300     }
301 
302     // Also call the old SetValueToUnspecified()
303     if ( unspecified )
304         SetValueToUnspecified(property, ctrl);
305 }
306 
SetValueToUnspecified(wxPGProperty * WXUNUSED (property),wxWindow * WXUNUSED (ctrl)) const307 void wxPGEditor::SetValueToUnspecified( wxPGProperty* WXUNUSED(property),
308                                         wxWindow* WXUNUSED(ctrl) ) const
309 {
310 }
311 
CanContainCustomImage() const312 bool wxPGEditor::CanContainCustomImage() const
313 {
314     return false;
315 }
316 
317 // -----------------------------------------------------------------------
318 // wxPGTextCtrlEditor
319 // -----------------------------------------------------------------------
320 
WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(TextCtrl,wxPGTextCtrlEditor,wxPGEditor)321 WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(TextCtrl,wxPGTextCtrlEditor,wxPGEditor)
322 
323 
324 wxPGWindowList wxPGTextCtrlEditor::CreateControls( wxPropertyGrid* propGrid,
325                                                    wxPGProperty* property,
326                                                    const wxPoint& pos,
327                                                    const wxSize& sz ) const
328 {
329     wxString text;
330 
331     //
332     // If has children, and limited editing is specified, then don't create.
333     if ( (property->GetFlags() & wxPG_PROP_NOEDITOR) &&
334          property->GetChildCount() )
335         return NULL;
336 
337     int argFlags = 0;
338     if ( !property->HasFlag(wxPG_PROP_READONLY) &&
339          !property->IsValueUnspecified() )
340         argFlags |= wxPG_EDITABLE_VALUE;
341     text = property->GetValueAsString(argFlags);
342 
343     int flags = 0;
344     if ( (property->GetFlags() & wxPG_PROP_PASSWORD) &&
345          wxDynamicCast(property, wxStringProperty) )
346         flags |= wxTE_PASSWORD;
347 
348     wxWindow* wnd = propGrid->GenerateEditorTextCtrl(pos,sz,text,NULL,flags,
349                                                      property->GetMaxLength());
350 
351     return wnd;
352 }
353 
354 #if 0
355 void wxPGTextCtrlEditor::DrawValue( wxDC& dc, wxPGProperty* property, const wxRect& rect ) const
356 {
357     if ( !property->IsValueUnspecified() )
358     {
359         wxString drawStr = property->GetDisplayedString();
360 
361         // Code below should no longer be needed, as the obfuscation
362         // is now done in GetValueAsString.
363         /*if ( (property->GetFlags() & wxPG_PROP_PASSWORD) &&
364              property->IsKindOf(WX_PG_CLASSINFO(wxStringProperty)) )
365         {
366             size_t a = drawStr.length();
367             drawStr.Empty();
368             drawStr.Append(wxS('*'),a);
369         }*/
370         dc.DrawText( drawStr, rect.x+wxPG_XBEFORETEXT, rect.y );
371     }
372 }
373 #endif
374 
UpdateControl(wxPGProperty * property,wxWindow * ctrl) const375 void wxPGTextCtrlEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
376 {
377     wxTextCtrl* tc = wxDynamicCast(ctrl, wxTextCtrl);
378     if (!tc) return;
379 
380     wxString s;
381 
382     if ( tc->HasFlag(wxTE_PASSWORD) )
383         s = property->GetValueAsString(wxPG_FULL_VALUE);
384     else
385         s = property->GetDisplayedString();
386 
387     wxPropertyGrid* pg = property->GetGrid();
388 
389     pg->SetupTextCtrlValue(s);
390     tc->SetValue(s);
391 
392     //
393     // Fix indentation, just in case (change in font boldness is one good
394     // reason).
395     tc->SetMargins(0);
396 }
397 
398 // Provided so that, for example, ComboBox editor can use the same code
399 // (multiple inheritance would get way too messy).
OnTextCtrlEvent(wxPropertyGrid * propGrid,wxPGProperty * WXUNUSED (property),wxWindow * ctrl,wxEvent & event)400 bool wxPGTextCtrlEditor::OnTextCtrlEvent( wxPropertyGrid* propGrid,
401                                           wxPGProperty* WXUNUSED(property),
402                                           wxWindow* ctrl,
403                                           wxEvent& event )
404 {
405     if ( !ctrl )
406         return false;
407 
408     if ( event.GetEventType() == wxEVT_TEXT_ENTER )
409     {
410         if ( propGrid->IsEditorsValueModified() )
411         {
412             return true;
413         }
414     }
415     else if ( event.GetEventType() == wxEVT_TEXT )
416     {
417         //
418         // Pass this event outside wxPropertyGrid so that,
419         // if necessary, program can tell when user is editing
420         // a textctrl.
421         // FIXME: Is it safe to change event id in the middle of event
422         //        processing (seems to work, but...)?
423         event.Skip();
424         event.SetId(propGrid->GetId());
425 
426         propGrid->EditorsValueWasModified();
427     }
428     return false;
429 }
430 
431 
OnEvent(wxPropertyGrid * propGrid,wxPGProperty * property,wxWindow * ctrl,wxEvent & event) const432 bool wxPGTextCtrlEditor::OnEvent( wxPropertyGrid* propGrid,
433                                   wxPGProperty* property,
434                                   wxWindow* ctrl,
435                                   wxEvent& event ) const
436 {
437     return wxPGTextCtrlEditor::OnTextCtrlEvent(propGrid,property,ctrl,event);
438 }
439 
440 
GetTextCtrlValueFromControl(wxVariant & variant,wxPGProperty * property,wxWindow * ctrl)441 bool wxPGTextCtrlEditor::GetTextCtrlValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl )
442 {
443     wxTextCtrl* tc = wxStaticCast(ctrl, wxTextCtrl);
444     wxString textVal = tc->GetValue();
445 
446     if ( property->UsesAutoUnspecified() && textVal.empty() )
447     {
448         variant.MakeNull();
449         return true;
450     }
451 
452     bool res = property->StringToValue(variant, textVal, wxPG_EDITABLE_VALUE);
453 
454     // Changing unspecified always causes event (returning
455     // true here should be enough to trigger it).
456     // TODO: Move to propgrid.cpp
457     if ( !res && variant.IsNull() )
458         res = true;
459 
460     return res;
461 }
462 
463 
GetValueFromControl(wxVariant & variant,wxPGProperty * property,wxWindow * ctrl) const464 bool wxPGTextCtrlEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
465 {
466     return wxPGTextCtrlEditor::GetTextCtrlValueFromControl(variant, property, ctrl);
467 }
468 
469 
SetControlStringValue(wxPGProperty * property,wxWindow * ctrl,const wxString & txt) const470 void wxPGTextCtrlEditor::SetControlStringValue( wxPGProperty* property, wxWindow* ctrl, const wxString& txt ) const
471 {
472     wxTextCtrl* tc = wxStaticCast(ctrl, wxTextCtrl);
473 
474     wxPropertyGrid* pg = property->GetGrid();
475     wxASSERT(pg);  // Really, property grid should exist if editor does
476     if ( pg )
477     {
478         pg->SetupTextCtrlValue(txt);
479         tc->SetValue(txt);
480     }
481 }
482 
483 
wxPGTextCtrlEditor_OnFocus(wxPGProperty * property,wxTextCtrl * tc)484 void wxPGTextCtrlEditor_OnFocus( wxPGProperty* property,
485                                  wxTextCtrl* tc )
486 {
487     // Make sure there is correct text (instead of unspecified value
488     // indicator or hint text)
489     int flags = property->HasFlag(wxPG_PROP_READONLY) ?
490         0 : wxPG_EDITABLE_VALUE;
491     wxString correctText = property->GetValueAsString(flags);
492 
493     if ( tc->GetValue() != correctText )
494     {
495         property->GetGrid()->SetupTextCtrlValue(correctText);
496         tc->SetValue(correctText);
497     }
498 
499     tc->SelectAll();
500 }
501 
OnFocus(wxPGProperty * property,wxWindow * wnd) const502 void wxPGTextCtrlEditor::OnFocus( wxPGProperty* property,
503                                   wxWindow* wnd ) const
504 {
505     wxTextCtrl* tc = wxStaticCast(wnd, wxTextCtrl);
506     wxPGTextCtrlEditor_OnFocus(property, tc);
507 }
508 
~wxPGTextCtrlEditor()509 wxPGTextCtrlEditor::~wxPGTextCtrlEditor()
510 {
511     // Reset the global pointer. Useful when wxPropertyGrid is accessed
512     // from an external main loop.
513     wxPG_EDITOR(TextCtrl) = NULL;
514 }
515 
516 
517 // -----------------------------------------------------------------------
518 // wxPGChoiceEditor
519 // -----------------------------------------------------------------------
520 
521 
522 WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(Choice,wxPGChoiceEditor,wxPGEditor)
523 
524 
525 // This is a special enhanced double-click processor class.
526 // In essence, it allows for double-clicks for which the
527 // first click "created" the control.
528 class wxPGDoubleClickProcessor : public wxEvtHandler
529 {
530 public:
531 
wxPGDoubleClickProcessor(wxOwnerDrawnComboBox * combo,wxPGProperty * property)532     wxPGDoubleClickProcessor( wxOwnerDrawnComboBox* combo, wxPGProperty* property )
533         : wxEvtHandler()
534     {
535         m_timeLastMouseUp = 0;
536         m_combo = combo;
537         m_property = property;
538         m_downReceived = false;
539     }
540 
541 protected:
542 
OnMouseEvent(wxMouseEvent & event)543     void OnMouseEvent( wxMouseEvent& event )
544     {
545         wxLongLong t = ::wxGetLocalTimeMillis();
546         int evtType = event.GetEventType();
547 
548         if ( m_property->HasFlag(wxPG_PROP_USE_DCC) &&
549              wxDynamicCast(m_property, wxBoolProperty) &&
550              !m_combo->IsPopupShown() )
551         {
552             // Just check that it is in the text area
553             wxPoint pt = event.GetPosition();
554             if ( m_combo->GetTextRect().Contains(pt) )
555             {
556                 if ( evtType == wxEVT_LEFT_DOWN )
557                 {
558                     // Set value to avoid up-events without corresponding downs
559                     m_downReceived = true;
560                 }
561                 else if ( evtType == wxEVT_LEFT_DCLICK )
562                 {
563                     // We'll make our own double-clicks
564                     event.SetEventType(0);
565                     return;
566                 }
567                 else if ( evtType == wxEVT_LEFT_UP )
568                 {
569                     if ( m_downReceived || m_timeLastMouseUp == 1 )
570                     {
571                         wxLongLong timeFromLastUp = (t-m_timeLastMouseUp);
572 
573                         if ( timeFromLastUp < DOUBLE_CLICK_CONVERSION_TRESHOLD )
574                         {
575                             event.SetEventType(wxEVT_LEFT_DCLICK);
576                             m_timeLastMouseUp = 1;
577                         }
578                         else
579                         {
580                             m_timeLastMouseUp = t;
581                         }
582                     }
583                 }
584             }
585         }
586 
587         event.Skip();
588     }
589 
OnSetFocus(wxFocusEvent & event)590     void OnSetFocus( wxFocusEvent& event )
591     {
592         m_timeLastMouseUp = ::wxGetLocalTimeMillis();
593         event.Skip();
594     }
595 
596 private:
597     wxLongLong                  m_timeLastMouseUp;
598     wxOwnerDrawnComboBox*       m_combo;
599     wxPGProperty*               m_property;  // Selected property
600     bool                        m_downReceived;
601 
602     DECLARE_EVENT_TABLE()
603 };
604 
605 BEGIN_EVENT_TABLE(wxPGDoubleClickProcessor, wxEvtHandler)
606     EVT_MOUSE_EVENTS(wxPGDoubleClickProcessor::OnMouseEvent)
607     EVT_SET_FOCUS(wxPGDoubleClickProcessor::OnSetFocus)
608 END_EVENT_TABLE()
609 
610 
611 
612 class wxPGComboBox : public wxOwnerDrawnComboBox
613 {
614 public:
615 
wxPGComboBox()616     wxPGComboBox()
617         : wxOwnerDrawnComboBox()
618     {
619         m_dclickProcessor = NULL;
620         m_sizeEventCalled = false;
621     }
622 
~wxPGComboBox()623     ~wxPGComboBox()
624     {
625         if ( m_dclickProcessor )
626         {
627             RemoveEventHandler(m_dclickProcessor);
628             delete m_dclickProcessor;
629         }
630     }
631 
Create(wxWindow * parent,wxWindowID id,const wxString & value,const wxPoint & pos,const wxSize & size,const wxArrayString & choices,long style=0,const wxValidator & validator=wxDefaultValidator,const wxString & name=wxS ("wxOwnerDrawnComboBox"))632     bool Create(wxWindow *parent,
633                 wxWindowID id,
634                 const wxString& value,
635                 const wxPoint& pos,
636                 const wxSize& size,
637                 const wxArrayString& choices,
638                 long style = 0,
639                 const wxValidator& validator = wxDefaultValidator,
640                 const wxString& name = wxS("wxOwnerDrawnComboBox"))
641     {
642         if ( !wxOwnerDrawnComboBox::Create( parent,
643                                             id,
644                                             value,
645                                             pos,
646                                             size,
647                                             choices,
648                                             style,
649                                             validator,
650                                             name ) )
651             return false;
652 
653         m_dclickProcessor = new
654             wxPGDoubleClickProcessor( this, GetGrid()->GetSelection() );
655 
656         PushEventHandler(m_dclickProcessor);
657 
658         return true;
659     }
660 
OnDrawItem(wxDC & dc,const wxRect & rect,int item,int flags) const661     virtual void OnDrawItem( wxDC& dc,
662                              const wxRect& rect,
663                              int item,
664                              int flags ) const
665     {
666         wxPropertyGrid* pg = GetGrid();
667 
668         // Handle hint text via super class
669         if ( (flags & wxODCB_PAINTING_CONTROL) &&
670              ShouldUseHintText(flags) )
671         {
672             wxOwnerDrawnComboBox::OnDrawItem(dc, rect, item, flags);
673         }
674         else
675         {
676             pg->OnComboItemPaint( this, item, &dc, (wxRect&)rect, flags );
677         }
678     }
679 
OnMeasureItem(size_t item) const680     virtual wxCoord OnMeasureItem( size_t item ) const
681     {
682         wxPropertyGrid* pg = GetGrid();
683         wxRect rect;
684         rect.x = -1;
685         rect.width = 0;
686         pg->OnComboItemPaint( this, item, NULL, rect, 0 );
687         return rect.height;
688     }
689 
GetGrid() const690     wxPropertyGrid* GetGrid() const
691     {
692         wxPropertyGrid* pg = wxDynamicCast(GetParent(),
693                                            wxPropertyGrid);
694         wxASSERT(pg);
695         return pg;
696     }
697 
OnMeasureItemWidth(size_t item) const698     virtual wxCoord OnMeasureItemWidth( size_t item ) const
699     {
700         wxPropertyGrid* pg = GetGrid();
701         wxRect rect;
702         rect.x = -1;
703         rect.width = -1;
704         pg->OnComboItemPaint( this, item, NULL, rect, 0 );
705         return rect.width;
706     }
707 
PositionTextCtrl(int textCtrlXAdjust,int WXUNUSED (textCtrlYAdjust))708     virtual void PositionTextCtrl( int textCtrlXAdjust,
709                                    int WXUNUSED(textCtrlYAdjust) )
710     {
711     #ifdef wxPG_TEXTCTRLXADJUST
712         textCtrlXAdjust = wxPG_TEXTCTRLXADJUST -
713                           (wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1) - 1,
714     #endif
715         wxOwnerDrawnComboBox::PositionTextCtrl(
716             textCtrlXAdjust,
717             0 // Under MSW vertical position is already properly adjusted.
718               // (This parameter is not used by other ports.)
719         );
720     }
721 
722 private:
723     wxPGDoubleClickProcessor*   m_dclickProcessor;
724     bool                        m_sizeEventCalled;
725 };
726 
727 
OnComboItemPaint(const wxPGComboBox * pCb,int item,wxDC * pDc,wxRect & rect,int flags)728 void wxPropertyGrid::OnComboItemPaint( const wxPGComboBox* pCb,
729                                        int item,
730                                        wxDC* pDc,
731                                        wxRect& rect,
732                                        int flags )
733 {
734     wxPGProperty* p = GetSelection();
735     wxString text;
736 
737     const wxPGChoices& choices = p->GetChoices();
738     const wxPGCommonValue* comVal = NULL;
739     int comVals = p->GetDisplayedCommonValueCount();
740     int comValIndex = -1;
741 
742     int choiceCount = 0;
743     if ( choices.IsOk() )
744         choiceCount = choices.GetCount();
745 
746     if ( item >= choiceCount && comVals > 0 )
747     {
748         comValIndex = item - choiceCount;
749         comVal = GetCommonValue(comValIndex);
750         if ( !p->IsValueUnspecified() )
751             text = comVal->GetLabel();
752     }
753     else
754     {
755         if ( !(flags & wxODCB_PAINTING_CONTROL) )
756         {
757             text = pCb->GetString(item);
758         }
759         else
760         {
761             if ( !p->IsValueUnspecified() )
762                 text = p->GetValueAsString(0);
763         }
764     }
765 
766     if ( item < 0 )
767         return;
768 
769     wxSize cis;
770 
771     const wxBitmap* itemBitmap = NULL;
772 
773     if ( item >= 0 && choices.IsOk() && choices.Item(item).GetBitmap().IsOk() && comValIndex == -1 )
774         itemBitmap = &choices.Item(item).GetBitmap();
775 
776     //
777     // Decide what custom image size to use
778     if ( itemBitmap )
779     {
780         cis.x = itemBitmap->GetWidth();
781         cis.y = itemBitmap->GetHeight();
782     }
783     else
784     {
785         cis = GetImageSize(p, item);
786     }
787 
788     if ( rect.x < 0 )
789     {
790         // Default measure behaviour (no flexible, custom paint image only)
791         if ( rect.width < 0 )
792         {
793             wxCoord x, y;
794             pCb->GetTextExtent(text, &x, &y, 0, 0);
795             rect.width = cis.x + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2 + 9 + x;
796         }
797 
798         rect.height = cis.y + 2;
799         return;
800     }
801 
802     wxPGPaintData paintdata;
803     paintdata.m_parent = NULL;
804     paintdata.m_choiceItem = item;
805 
806     // This is by the current (1.0.0b) spec - if painting control, item is -1
807     if ( (flags & wxODCB_PAINTING_CONTROL) )
808         paintdata.m_choiceItem = -1;
809 
810     if ( pDc )
811         pDc->SetBrush(*wxWHITE_BRUSH);
812 
813     wxPGCellRenderer* renderer = NULL;
814     const wxPGChoiceEntry* cell = NULL;
815 
816     if ( rect.x >= 0 )
817     {
818         //
819         // DrawItem call
820         wxDC& dc = *pDc;
821 
822         wxPoint pt(rect.x + wxPG_CONTROL_MARGIN - wxPG_CHOICEXADJUST - 1,
823                    rect.y + 1);
824 
825         int renderFlags = wxPGCellRenderer::DontUseCellColours;
826         bool useCustomPaintProcedure;
827 
828         // If custom image had some size, we will start from the assumption
829         // that custom paint procedure is required
830         if ( cis.x > 0 )
831             useCustomPaintProcedure = true;
832         else
833             useCustomPaintProcedure = false;
834 
835         if ( flags & wxODCB_PAINTING_SELECTED )
836             renderFlags |= wxPGCellRenderer::Selected;
837 
838         if ( flags & wxODCB_PAINTING_CONTROL )
839         {
840             renderFlags |= wxPGCellRenderer::Control;
841 
842             // If wxPG_PROP_CUSTOMIMAGE was set, then that means any custom
843             // image will not appear on the control row (it may be too
844             // large to fit, for instance). Also do not draw custom image
845             // if no choice was selected.
846             if ( !p->HasFlag(wxPG_PROP_CUSTOMIMAGE) || item < 0 )
847                 useCustomPaintProcedure = false;
848         }
849         else
850         {
851             renderFlags |= wxPGCellRenderer::ChoicePopup;
852 
853             // For consistency, always use normal font when drawing drop down
854             // items
855             dc.SetFont(GetFont());
856         }
857 
858         // If not drawing a selected popup item, then give property's
859         // m_valueBitmap a chance.
860         if ( p->m_valueBitmap && item != pCb->GetSelection() )
861             useCustomPaintProcedure = false;
862         // If current choice had a bitmap set by the application, then
863         // use it instead of any custom paint procedure.
864         else if ( itemBitmap )
865             useCustomPaintProcedure = false;
866 
867         if ( useCustomPaintProcedure )
868         {
869             pt.x += wxCC_CUSTOM_IMAGE_MARGIN1;
870             wxRect r(pt.x,pt.y,cis.x,cis.y);
871 
872             if ( flags & wxODCB_PAINTING_CONTROL )
873             {
874                 //r.width = cis.x;
875                 r.height = wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight);
876             }
877 
878             paintdata.m_drawnWidth = r.width;
879 
880             dc.SetPen(m_colPropFore);
881             if ( comValIndex >= 0 )
882             {
883                 const wxPGCommonValue* cv = GetCommonValue(comValIndex);
884                 renderer = cv->GetRenderer();
885                 r.width = rect.width;
886                 renderer->Render( dc, r, this, p, m_selColumn, comValIndex, renderFlags );
887                 return;
888             }
889             else if ( item >= 0 )
890             {
891                 p->OnCustomPaint( dc, r, paintdata );
892             }
893             else
894             {
895                 dc.DrawRectangle( r );
896             }
897 
898             pt.x += paintdata.m_drawnWidth + wxCC_CUSTOM_IMAGE_MARGIN2 - 1;
899         }
900         else
901         {
902             // TODO: This aligns text so that it seems to be horizontally
903             //       on the same line as property values. Not really
904             //       sure if its needed, but seems to not cause any harm.
905             pt.x -= 1;
906 
907             if ( item < 0 && (flags & wxODCB_PAINTING_CONTROL) )
908                 item = pCb->GetSelection();
909 
910             if ( choices.IsOk() && item >= 0 && comValIndex < 0 )
911             {
912                 cell = &choices.Item(item);
913                 renderer = wxPGGlobalVars->m_defaultRenderer;
914                 int imageOffset = renderer->PreDrawCell(dc, rect, *cell,
915                                                         renderFlags );
916                 if ( imageOffset )
917                     imageOffset += wxCC_CUSTOM_IMAGE_MARGIN1 +
918                                    wxCC_CUSTOM_IMAGE_MARGIN2;
919                 pt.x += imageOffset;
920             }
921         }
922 
923         //
924         // Draw text
925         //
926 
927         pt.y += (rect.height-m_fontHeight)/2 - 1;
928 
929         pt.x += 1;
930 
931         dc.DrawText( text, pt.x + wxPG_XBEFORETEXT, pt.y );
932 
933         if ( renderer )
934             renderer->PostDrawCell(dc, this, *cell, renderFlags);
935     }
936     else
937     {
938         //
939         // MeasureItem call
940         wxDC& dc = *pDc;
941 
942         p->OnCustomPaint( dc, rect, paintdata );
943         rect.height = paintdata.m_drawnHeight + 2;
944         rect.width = cis.x + wxCC_CUSTOM_IMAGE_MARGIN1 + wxCC_CUSTOM_IMAGE_MARGIN2 + 9;
945     }
946 }
947 
wxPGChoiceEditor_SetCustomPaintWidth(wxPropertyGrid * propGrid,wxPGComboBox * cb,int cmnVal)948 bool wxPGChoiceEditor_SetCustomPaintWidth( wxPropertyGrid* propGrid, wxPGComboBox* cb, int cmnVal )
949 {
950     wxPGProperty* property = propGrid->GetSelectedProperty();
951     wxASSERT( property );
952 
953     wxSize imageSize;
954     bool res;
955 
956     // TODO: Do this always when cell has custom text.
957     if ( property->IsValueUnspecified() )
958     {
959         cb->SetCustomPaintWidth( 0 );
960         return true;
961     }
962 
963     if ( cmnVal >= 0 )
964     {
965         // Yes, a common value is being selected
966         property->SetCommonValue( cmnVal );
967         imageSize = propGrid->GetCommonValue(cmnVal)->
968                             GetRenderer()->GetImageSize(property, 1, cmnVal);
969         res = false;
970     }
971     else
972     {
973         imageSize = propGrid->GetImageSize(property, -1);
974         res = true;
975     }
976 
977     if ( imageSize.x )
978         imageSize.x += ODCB_CUST_PAINT_MARGIN;
979     cb->SetCustomPaintWidth( imageSize.x );
980 
981     return res;
982 }
983 
984 // CreateControls calls this with CB_READONLY in extraStyle
CreateControlsBase(wxPropertyGrid * propGrid,wxPGProperty * property,const wxPoint & pos,const wxSize & sz,long extraStyle) const985 wxWindow* wxPGChoiceEditor::CreateControlsBase( wxPropertyGrid* propGrid,
986                                                 wxPGProperty* property,
987                                                 const wxPoint& pos,
988                                                 const wxSize& sz,
989                                                 long extraStyle ) const
990 {
991     // Since it is not possible (yet) to create a read-only combo box in
992     // the same sense that wxTextCtrl is read-only, simply do not create
993     // the control in this case.
994     if ( property->HasFlag(wxPG_PROP_READONLY) )
995         return NULL;
996 
997     const wxPGChoices& choices = property->GetChoices();
998     wxString defString;
999     int index = property->GetChoiceSelection();
1000 
1001     int argFlags = 0;
1002     if ( !property->HasFlag(wxPG_PROP_READONLY) &&
1003          !property->IsValueUnspecified() )
1004         argFlags |= wxPG_EDITABLE_VALUE;
1005     defString = property->GetValueAsString(argFlags);
1006 
1007     wxArrayString labels = choices.GetLabels();
1008 
1009     wxPGComboBox* cb;
1010 
1011     wxPoint po(pos);
1012     wxSize si(sz);
1013     po.y += wxPG_CHOICEYADJUST;
1014     si.y -= (wxPG_CHOICEYADJUST*2);
1015 
1016     po.x += wxPG_CHOICEXADJUST;
1017     si.x -= wxPG_CHOICEXADJUST;
1018     wxWindow* ctrlParent = propGrid->GetPanel();
1019 
1020     int odcbFlags = extraStyle | wxBORDER_NONE | wxTE_PROCESS_ENTER;
1021 
1022     if ( (property->GetFlags() & wxPG_PROP_USE_DCC) &&
1023          wxDynamicCast(property, wxBoolProperty) )
1024         odcbFlags |= wxODCB_DCLICK_CYCLES;
1025 
1026     //
1027     // If common value specified, use appropriate index
1028     unsigned int cmnVals = property->GetDisplayedCommonValueCount();
1029     if ( cmnVals )
1030     {
1031         if ( !property->IsValueUnspecified() )
1032         {
1033             int cmnVal = property->GetCommonValue();
1034             if ( cmnVal >= 0 )
1035             {
1036                 index = labels.size() + cmnVal;
1037             }
1038         }
1039 
1040         unsigned int i;
1041         for ( i=0; i<cmnVals; i++ )
1042             labels.Add(propGrid->GetCommonValueLabel(i));
1043     }
1044 
1045     cb = new wxPGComboBox();
1046 #ifdef __WXMSW__
1047     cb->Hide();
1048 #endif
1049     cb->Create(ctrlParent,
1050                wxPG_SUBID1,
1051                wxString(),
1052                po,
1053                si,
1054                labels,
1055                odcbFlags);
1056 
1057     cb->SetButtonPosition(si.y,0,wxRIGHT);
1058     cb->SetMargins(wxPG_XBEFORETEXT-1);
1059 
1060     cb->SetBackgroundColour(propGrid->GetCellBackgroundColour());
1061 
1062     // Set hint text
1063     cb->SetHint(property->GetHintText());
1064 
1065     wxPGChoiceEditor_SetCustomPaintWidth( propGrid, cb,
1066                                           property->GetCommonValue() );
1067 
1068     if ( index >= 0 && index < (int)cb->GetCount() )
1069     {
1070         cb->SetSelection( index );
1071         if ( !defString.empty() )
1072             cb->SetText( defString );
1073     }
1074     else if ( !(extraStyle & wxCB_READONLY) && !defString.empty() )
1075     {
1076         propGrid->SetupTextCtrlValue(defString);
1077         cb->SetValue( defString );
1078     }
1079     else
1080     {
1081         cb->SetSelection( -1 );
1082     }
1083 
1084 #ifdef __WXMSW__
1085     cb->Show();
1086 #endif
1087 
1088     return (wxWindow*) cb;
1089 }
1090 
1091 
UpdateControl(wxPGProperty * property,wxWindow * ctrl) const1092 void wxPGChoiceEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
1093 {
1094     wxASSERT( ctrl );
1095     wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1096     wxASSERT( wxDynamicCast(cb, wxOwnerDrawnComboBox));
1097     int ind = property->GetChoiceSelection();
1098     cb->SetSelection(ind);
1099 }
1100 
CreateControls(wxPropertyGrid * propGrid,wxPGProperty * property,const wxPoint & pos,const wxSize & sz) const1101 wxPGWindowList wxPGChoiceEditor::CreateControls( wxPropertyGrid* propGrid, wxPGProperty* property,
1102         const wxPoint& pos, const wxSize& sz ) const
1103 {
1104     return CreateControlsBase(propGrid,property,pos,sz,wxCB_READONLY);
1105 }
1106 
1107 
InsertItem(wxWindow * ctrl,const wxString & label,int index) const1108 int wxPGChoiceEditor::InsertItem( wxWindow* ctrl, const wxString& label, int index ) const
1109 {
1110     wxASSERT( ctrl );
1111     wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1112     wxASSERT( wxDynamicCast(cb, wxOwnerDrawnComboBox));
1113 
1114     if (index < 0)
1115         index = cb->GetCount();
1116 
1117     return cb->Insert(label,index);
1118 }
1119 
1120 
DeleteItem(wxWindow * ctrl,int index) const1121 void wxPGChoiceEditor::DeleteItem( wxWindow* ctrl, int index ) const
1122 {
1123     wxASSERT( ctrl );
1124     wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1125     wxASSERT( wxDynamicCast(cb, wxOwnerDrawnComboBox));
1126 
1127     cb->Delete(index);
1128 }
1129 
OnEvent(wxPropertyGrid * propGrid,wxPGProperty * property,wxWindow * ctrl,wxEvent & event) const1130 bool wxPGChoiceEditor::OnEvent( wxPropertyGrid* propGrid, wxPGProperty* property,
1131     wxWindow* ctrl, wxEvent& event ) const
1132 {
1133     if ( event.GetEventType() == wxEVT_COMBOBOX )
1134     {
1135         wxPGComboBox* cb = (wxPGComboBox*)ctrl;
1136         int index = cb->GetSelection();
1137         int cmnValIndex = -1;
1138         int cmnVals = property->GetDisplayedCommonValueCount();
1139         int items = cb->GetCount();
1140 
1141         if ( index >= (items-cmnVals) )
1142         {
1143             // Yes, a common value is being selected
1144             cmnValIndex = index - (items-cmnVals);
1145             property->SetCommonValue( cmnValIndex );
1146 
1147             // Truly set value to unspecified?
1148             if ( propGrid->GetUnspecifiedCommonValue() == cmnValIndex )
1149             {
1150                 if ( !property->IsValueUnspecified() )
1151                     propGrid->SetInternalFlag(wxPG_FL_VALUE_CHANGE_IN_EVENT);
1152                 property->SetValueToUnspecified();
1153                 if ( !cb->HasFlag(wxCB_READONLY) )
1154                 {
1155                     wxString unspecValueText;
1156                     unspecValueText = propGrid->GetUnspecifiedValueText();
1157                     propGrid->SetupTextCtrlValue(unspecValueText);
1158                     cb->GetTextCtrl()->SetValue(unspecValueText);
1159                 }
1160                 return false;
1161             }
1162         }
1163         return wxPGChoiceEditor_SetCustomPaintWidth( propGrid, cb, cmnValIndex );
1164     }
1165     return false;
1166 }
1167 
1168 
GetValueFromControl(wxVariant & variant,wxPGProperty * property,wxWindow * ctrl) const1169 bool wxPGChoiceEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
1170 {
1171     wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1172 
1173     int index = cb->GetSelection();
1174 
1175     if ( index != property->GetChoiceSelection() ||
1176         // Changing unspecified always causes event (returning
1177         // true here should be enough to trigger it).
1178          property->IsValueUnspecified()
1179        )
1180     {
1181         return property->IntToValue(variant, index, wxPG_PROPERTY_SPECIFIC);
1182     }
1183     return false;
1184 }
1185 
1186 
SetControlStringValue(wxPGProperty * property,wxWindow * ctrl,const wxString & txt) const1187 void wxPGChoiceEditor::SetControlStringValue( wxPGProperty* property,
1188                                               wxWindow* ctrl,
1189                                               const wxString& txt ) const
1190 {
1191     wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1192     wxASSERT( cb );
1193     property->GetGrid()->SetupTextCtrlValue(txt);
1194     cb->SetValue(txt);
1195 }
1196 
1197 
SetControlIntValue(wxPGProperty * WXUNUSED (property),wxWindow * ctrl,int value) const1198 void wxPGChoiceEditor::SetControlIntValue( wxPGProperty* WXUNUSED(property), wxWindow* ctrl, int value ) const
1199 {
1200     wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1201     wxASSERT( cb );
1202     cb->SetSelection(value);
1203 }
1204 
1205 
SetValueToUnspecified(wxPGProperty * WXUNUSED (property),wxWindow * ctrl) const1206 void wxPGChoiceEditor::SetValueToUnspecified( wxPGProperty* WXUNUSED(property),
1207                                               wxWindow* ctrl ) const
1208 {
1209     wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1210 
1211     if ( cb->HasFlag(wxCB_READONLY) )
1212         cb->SetSelection(-1);
1213 }
1214 
1215 
CanContainCustomImage() const1216 bool wxPGChoiceEditor::CanContainCustomImage() const
1217 {
1218     return true;
1219 }
1220 
1221 
~wxPGChoiceEditor()1222 wxPGChoiceEditor::~wxPGChoiceEditor()
1223 {
1224     wxPG_EDITOR(Choice) = NULL;
1225 }
1226 
1227 
1228 // -----------------------------------------------------------------------
1229 // wxPGComboBoxEditor
1230 // -----------------------------------------------------------------------
1231 
1232 
WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(ComboBox,wxPGComboBoxEditor,wxPGChoiceEditor)1233 WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(ComboBox,
1234                                       wxPGComboBoxEditor,
1235                                       wxPGChoiceEditor)
1236 
1237 
1238 void wxPGComboBoxEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
1239 {
1240     wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1241     wxString s = property->GetValueAsString(wxPG_EDITABLE_VALUE);
1242     property->GetGrid()->SetupTextCtrlValue(s);
1243     cb->SetValue(s);
1244 
1245     // TODO: If string matches any selection, then select that.
1246 }
1247 
1248 
CreateControls(wxPropertyGrid * propGrid,wxPGProperty * property,const wxPoint & pos,const wxSize & sz) const1249 wxPGWindowList wxPGComboBoxEditor::CreateControls( wxPropertyGrid* propGrid,
1250                                                    wxPGProperty* property,
1251                                                    const wxPoint& pos,
1252                                                    const wxSize& sz ) const
1253 {
1254     return CreateControlsBase(propGrid,property,pos,sz,0);
1255 }
1256 
1257 
OnEvent(wxPropertyGrid * propGrid,wxPGProperty * property,wxWindow * ctrl,wxEvent & event) const1258 bool wxPGComboBoxEditor::OnEvent( wxPropertyGrid* propGrid,
1259                                   wxPGProperty* property,
1260                                   wxWindow* ctrl,
1261                                   wxEvent& event ) const
1262 {
1263     wxOwnerDrawnComboBox* cb = NULL;
1264     wxWindow* textCtrl = NULL;
1265 
1266     if ( ctrl )
1267     {
1268         cb = (wxOwnerDrawnComboBox*)ctrl;
1269         textCtrl = cb->GetTextCtrl();
1270     }
1271 
1272     if ( wxPGTextCtrlEditor::OnTextCtrlEvent(propGrid,property,textCtrl,event) )
1273         return true;
1274 
1275     return wxPGChoiceEditor::OnEvent(propGrid,property,ctrl,event);
1276 }
1277 
1278 
GetValueFromControl(wxVariant & variant,wxPGProperty * property,wxWindow * ctrl) const1279 bool wxPGComboBoxEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
1280 {
1281     wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1282     wxString textVal = cb->GetValue();
1283 
1284     if ( property->UsesAutoUnspecified() && textVal.empty() )
1285     {
1286         variant.MakeNull();
1287         return true;
1288     }
1289 
1290     bool res = property->StringToValue(variant, textVal, wxPG_EDITABLE_VALUE|wxPG_PROPERTY_SPECIFIC);
1291 
1292     // Changing unspecified always causes event (returning
1293     // true here should be enough to trigger it).
1294     if ( !res && variant.IsNull() )
1295         res = true;
1296 
1297     return res;
1298 }
1299 
1300 
OnFocus(wxPGProperty * property,wxWindow * ctrl) const1301 void wxPGComboBoxEditor::OnFocus( wxPGProperty* property,
1302                                   wxWindow* ctrl ) const
1303 {
1304     wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1305     wxPGTextCtrlEditor_OnFocus(property, cb->GetTextCtrl());
1306 }
1307 
1308 
~wxPGComboBoxEditor()1309 wxPGComboBoxEditor::~wxPGComboBoxEditor()
1310 {
1311     wxPG_EDITOR(ComboBox) = NULL;
1312 }
1313 
1314 
1315 
1316 // -----------------------------------------------------------------------
1317 // wxPGChoiceAndButtonEditor
1318 // -----------------------------------------------------------------------
1319 
1320 
WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(ChoiceAndButton,wxPGChoiceAndButtonEditor,wxPGChoiceEditor)1321 WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(ChoiceAndButton,
1322                                       wxPGChoiceAndButtonEditor,
1323                                       wxPGChoiceEditor)
1324 
1325 
1326 wxPGWindowList wxPGChoiceAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
1327                                                           wxPGProperty* property,
1328                                                           const wxPoint& pos,
1329                                                           const wxSize& sz ) const
1330 {
1331     // Use one two units smaller to match size of the combo's dropbutton.
1332     // (normally a bigger button is used because it looks better)
1333     int bt_wid = sz.y;
1334     bt_wid -= 2;
1335     wxSize bt_sz(bt_wid,bt_wid);
1336 
1337     // Position of button.
1338     wxPoint bt_pos(pos.x+sz.x-bt_sz.x,pos.y);
1339 #ifdef __WXMAC__
1340     bt_pos.y -= 1;
1341 #else
1342     bt_pos.y += 1;
1343 #endif
1344 
1345     wxWindow* bt = propGrid->GenerateEditorButton( bt_pos, bt_sz );
1346 
1347     // Size of choice.
1348     wxSize ch_sz(sz.x-bt->GetSize().x,sz.y);
1349 
1350 #ifdef __WXMAC__
1351     ch_sz.x -= wxPG_TEXTCTRL_AND_BUTTON_SPACING;
1352 #endif
1353 
1354     wxWindow* ch = wxPGEditor_Choice->CreateControls(propGrid,property,
1355         pos,ch_sz).m_primary;
1356 
1357 #ifdef __WXMSW__
1358     bt->Show();
1359 #endif
1360 
1361     return wxPGWindowList(ch, bt);
1362 }
1363 
1364 
~wxPGChoiceAndButtonEditor()1365 wxPGChoiceAndButtonEditor::~wxPGChoiceAndButtonEditor()
1366 {
1367     wxPG_EDITOR(ChoiceAndButton) = NULL;
1368 }
1369 
1370 // -----------------------------------------------------------------------
1371 // wxPGTextCtrlAndButtonEditor
1372 // -----------------------------------------------------------------------
1373 
WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(TextCtrlAndButton,wxPGTextCtrlAndButtonEditor,wxPGTextCtrlEditor)1374 WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(TextCtrlAndButton,
1375                                       wxPGTextCtrlAndButtonEditor,
1376                                       wxPGTextCtrlEditor)
1377 
1378 
1379 wxPGWindowList wxPGTextCtrlAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
1380                                                             wxPGProperty* property,
1381                                                             const wxPoint& pos,
1382                                                             const wxSize& sz ) const
1383 {
1384     wxWindow* wnd2;
1385     wxWindow* wnd = propGrid->GenerateEditorTextCtrlAndButton( pos, sz, &wnd2,
1386         property->GetFlags() & wxPG_PROP_NOEDITOR, property);
1387 
1388     return wxPGWindowList(wnd, wnd2);
1389 }
1390 
1391 
~wxPGTextCtrlAndButtonEditor()1392 wxPGTextCtrlAndButtonEditor::~wxPGTextCtrlAndButtonEditor()
1393 {
1394     wxPG_EDITOR(TextCtrlAndButton) = NULL;
1395 }
1396 
1397 // -----------------------------------------------------------------------
1398 // wxPGCheckBoxEditor
1399 // -----------------------------------------------------------------------
1400 
1401 #if wxPG_INCLUDE_CHECKBOX
1402 
1403 WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(CheckBox,
1404                                       wxPGCheckBoxEditor,
1405                                       wxPGEditor)
1406 
1407 
1408 // Check box state flags
1409 enum
1410 {
1411     wxSCB_STATE_UNCHECKED   = 0,
1412     wxSCB_STATE_CHECKED     = 1,
1413     wxSCB_STATE_BOLD        = 2,
1414     wxSCB_STATE_UNSPECIFIED = 4
1415 };
1416 
1417 const int wxSCB_SETVALUE_CYCLE = 2;
1418 
1419 
DrawSimpleCheckBox(wxDC & dc,const wxRect & rect,int box_hei,int state)1420 static void DrawSimpleCheckBox( wxDC& dc, const wxRect& rect, int box_hei,
1421                                 int state )
1422 {
1423     // Box rectangle.
1424     wxRect r(rect.x+wxPG_XBEFORETEXT,rect.y+((rect.height-box_hei)/2),
1425              box_hei,box_hei);
1426     wxColour useCol = dc.GetTextForeground();
1427 
1428     if ( state & wxSCB_STATE_UNSPECIFIED )
1429     {
1430         useCol = wxColour(220, 220, 220);
1431     }
1432 
1433     // Draw check mark first because it is likely to overdraw the
1434     // surrounding rectangle.
1435     if ( state & wxSCB_STATE_CHECKED )
1436     {
1437         wxRect r2(r.x+wxPG_CHECKMARK_XADJ,
1438                   r.y+wxPG_CHECKMARK_YADJ,
1439                   r.width+wxPG_CHECKMARK_WADJ,
1440                   r.height+wxPG_CHECKMARK_HADJ);
1441     #if wxPG_CHECKMARK_DEFLATE
1442         r2.Deflate(wxPG_CHECKMARK_DEFLATE);
1443     #endif
1444         dc.DrawCheckMark(r2);
1445 
1446         // This would draw a simple cross check mark.
1447         // dc.DrawLine(r.x,r.y,r.x+r.width-1,r.y+r.height-1);
1448         // dc.DrawLine(r.x,r.y+r.height-1,r.x+r.width-1,r.y);
1449     }
1450 
1451     if ( !(state & wxSCB_STATE_BOLD) )
1452     {
1453         // Pen for thin rectangle.
1454         dc.SetPen(useCol);
1455     }
1456     else
1457     {
1458         // Pen for bold rectangle.
1459         wxPen linepen(useCol,2,wxSOLID);
1460         linepen.SetJoin(wxJOIN_MITER); // This prevents round edges.
1461         dc.SetPen(linepen);
1462         r.x++;
1463         r.y++;
1464         r.width--;
1465         r.height--;
1466     }
1467 
1468     dc.SetBrush(*wxTRANSPARENT_BRUSH);
1469 
1470     dc.DrawRectangle(r);
1471     dc.SetPen(*wxTRANSPARENT_PEN);
1472 }
1473 
1474 //
1475 // Real simple custom-drawn checkbox-without-label class.
1476 //
1477 class wxSimpleCheckBox : public wxControl
1478 {
1479 public:
1480 
1481     void SetValue( int value );
1482 
wxSimpleCheckBox(wxWindow * parent,wxWindowID id,const wxPoint & pos=wxDefaultPosition,const wxSize & size=wxDefaultSize)1483     wxSimpleCheckBox( wxWindow* parent,
1484                       wxWindowID id,
1485                       const wxPoint& pos = wxDefaultPosition,
1486                       const wxSize& size = wxDefaultSize )
1487         : wxControl(parent,id,pos,size,wxBORDER_NONE|wxWANTS_CHARS)
1488     {
1489         // Due to SetOwnFont stuff necessary for GTK+ 1.2, we need to have this
1490         SetFont( parent->GetFont() );
1491 
1492         m_state = 0;
1493         m_boxHeight = 12;
1494 
1495         SetBackgroundStyle( wxBG_STYLE_CUSTOM );
1496     }
1497 
1498     virtual ~wxSimpleCheckBox();
1499 
1500     int m_state;
1501     int m_boxHeight;
1502 
1503 private:
1504     void OnPaint( wxPaintEvent& event );
1505     void OnLeftClick( wxMouseEvent& event );
1506     void OnKeyDown( wxKeyEvent& event );
1507 
OnResize(wxSizeEvent & event)1508     void OnResize( wxSizeEvent& event )
1509     {
1510         Refresh();
1511         event.Skip();
1512     }
1513 
1514     static wxBitmap* ms_doubleBuffer;
1515 
1516     DECLARE_EVENT_TABLE()
1517 };
1518 
BEGIN_EVENT_TABLE(wxSimpleCheckBox,wxControl)1519 BEGIN_EVENT_TABLE(wxSimpleCheckBox, wxControl)
1520     EVT_PAINT(wxSimpleCheckBox::OnPaint)
1521     EVT_LEFT_DOWN(wxSimpleCheckBox::OnLeftClick)
1522     EVT_LEFT_DCLICK(wxSimpleCheckBox::OnLeftClick)
1523     EVT_KEY_DOWN(wxSimpleCheckBox::OnKeyDown)
1524     EVT_SIZE(wxSimpleCheckBox::OnResize)
1525 END_EVENT_TABLE()
1526 
1527 wxSimpleCheckBox::~wxSimpleCheckBox()
1528 {
1529     wxDELETE(ms_doubleBuffer);
1530 }
1531 
1532 wxBitmap* wxSimpleCheckBox::ms_doubleBuffer = NULL;
1533 
OnPaint(wxPaintEvent & WXUNUSED (event))1534 void wxSimpleCheckBox::OnPaint( wxPaintEvent& WXUNUSED(event) )
1535 {
1536     wxSize clientSize = GetClientSize();
1537     wxAutoBufferedPaintDC dc(this);
1538 
1539     dc.Clear();
1540     wxRect rect(0,0,clientSize.x,clientSize.y);
1541     rect.y += 1;
1542     rect.width += 1;
1543 
1544     wxColour bgcol = GetBackgroundColour();
1545     dc.SetBrush( bgcol );
1546     dc.SetPen( bgcol );
1547     dc.DrawRectangle( rect );
1548 
1549     dc.SetTextForeground(GetForegroundColour());
1550 
1551     int state = m_state;
1552     if ( !(state & wxSCB_STATE_UNSPECIFIED) &&
1553          GetFont().GetWeight() == wxBOLD )
1554         state |= wxSCB_STATE_BOLD;
1555 
1556     DrawSimpleCheckBox(dc, rect, m_boxHeight, state);
1557 }
1558 
OnLeftClick(wxMouseEvent & event)1559 void wxSimpleCheckBox::OnLeftClick( wxMouseEvent& event )
1560 {
1561     if ( (event.m_x > (wxPG_XBEFORETEXT-2)) &&
1562          (event.m_x <= (wxPG_XBEFORETEXT-2+m_boxHeight)) )
1563     {
1564         SetValue(wxSCB_SETVALUE_CYCLE);
1565     }
1566 }
1567 
OnKeyDown(wxKeyEvent & event)1568 void wxSimpleCheckBox::OnKeyDown( wxKeyEvent& event )
1569 {
1570     if ( event.GetKeyCode() == WXK_SPACE )
1571     {
1572         SetValue(wxSCB_SETVALUE_CYCLE);
1573     }
1574 }
1575 
SetValue(int value)1576 void wxSimpleCheckBox::SetValue( int value )
1577 {
1578     if ( value == wxSCB_SETVALUE_CYCLE )
1579     {
1580         if ( m_state & wxSCB_STATE_CHECKED )
1581             m_state &= ~wxSCB_STATE_CHECKED;
1582         else
1583             m_state |= wxSCB_STATE_CHECKED;
1584     }
1585     else
1586     {
1587         m_state = value;
1588     }
1589     Refresh();
1590 
1591     wxCommandEvent evt(wxEVT_CHECKBOX,GetParent()->GetId());
1592 
1593     wxPropertyGrid* propGrid = (wxPropertyGrid*) GetParent();
1594     wxASSERT( wxDynamicCast(propGrid, wxPropertyGrid) );
1595     propGrid->HandleCustomEditorEvent(evt);
1596 }
1597 
CreateControls(wxPropertyGrid * propGrid,wxPGProperty * property,const wxPoint & pos,const wxSize & size) const1598 wxPGWindowList wxPGCheckBoxEditor::CreateControls( wxPropertyGrid* propGrid,
1599                                                    wxPGProperty* property,
1600                                                    const wxPoint& pos,
1601                                                    const wxSize& size ) const
1602 {
1603     if ( property->HasFlag(wxPG_PROP_READONLY) )
1604         return NULL;
1605 
1606     wxPoint pt = pos;
1607     pt.x -= wxPG_XBEFOREWIDGET;
1608     wxSize sz = size;
1609     sz.x = propGrid->GetFontHeight() + (wxPG_XBEFOREWIDGET*2) + 4;
1610 
1611     wxSimpleCheckBox* cb = new wxSimpleCheckBox(propGrid->GetPanel(),
1612                                                 wxPG_SUBID1, pt, sz);
1613 
1614     cb->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
1615 
1616     UpdateControl(property, cb);
1617 
1618     if ( !property->IsValueUnspecified() )
1619     {
1620         // If mouse cursor was on the item, toggle the value now.
1621         if ( propGrid->GetInternalFlags() & wxPG_FL_ACTIVATION_BY_CLICK )
1622         {
1623             wxPoint point = cb->ScreenToClient(::wxGetMousePosition());
1624             if ( point.x <= (wxPG_XBEFORETEXT-2+cb->m_boxHeight) )
1625             {
1626                 if ( cb->m_state & wxSCB_STATE_CHECKED )
1627                     cb->m_state &= ~wxSCB_STATE_CHECKED;
1628                 else
1629                     cb->m_state |= wxSCB_STATE_CHECKED;
1630 
1631                 // Makes sure wxPG_EVT_CHANGING etc. is sent for this initial
1632                 // click
1633                 propGrid->ChangePropertyValue(property,
1634                                               wxPGVariant_Bool(cb->m_state));
1635             }
1636         }
1637     }
1638 
1639     propGrid->SetInternalFlag( wxPG_FL_FIXED_WIDTH_EDITOR );
1640 
1641     return cb;
1642 }
1643 
DrawValue(wxDC & dc,const wxRect & rect,wxPGProperty * property,const wxString & WXUNUSED (text)) const1644 void wxPGCheckBoxEditor::DrawValue( wxDC& dc, const wxRect& rect,
1645                                     wxPGProperty* property,
1646                                     const wxString& WXUNUSED(text) ) const
1647 {
1648     int state = wxSCB_STATE_UNCHECKED;
1649 
1650     if ( !property->IsValueUnspecified() )
1651     {
1652         state = property->GetChoiceSelection();
1653         if ( dc.GetFont().GetWeight() == wxBOLD )
1654             state |= wxSCB_STATE_BOLD;
1655     }
1656     else
1657     {
1658         state |= wxSCB_STATE_UNSPECIFIED;
1659     }
1660 
1661     DrawSimpleCheckBox(dc, rect, dc.GetCharHeight(), state);
1662 }
1663 
UpdateControl(wxPGProperty * property,wxWindow * ctrl) const1664 void wxPGCheckBoxEditor::UpdateControl( wxPGProperty* property,
1665                                         wxWindow* ctrl ) const
1666 {
1667     wxSimpleCheckBox* cb = (wxSimpleCheckBox*) ctrl;
1668     wxASSERT( cb );
1669 
1670     if ( !property->IsValueUnspecified() )
1671         cb->m_state = property->GetChoiceSelection();
1672     else
1673         cb->m_state = wxSCB_STATE_UNSPECIFIED;
1674 
1675     wxPropertyGrid* propGrid = property->GetGrid();
1676     cb->m_boxHeight = propGrid->GetFontHeight();
1677 
1678     cb->Refresh();
1679 }
1680 
OnEvent(wxPropertyGrid * WXUNUSED (propGrid),wxPGProperty * WXUNUSED (property),wxWindow * WXUNUSED (ctrl),wxEvent & event) const1681 bool wxPGCheckBoxEditor::OnEvent( wxPropertyGrid* WXUNUSED(propGrid), wxPGProperty* WXUNUSED(property),
1682     wxWindow* WXUNUSED(ctrl), wxEvent& event ) const
1683 {
1684     if ( event.GetEventType() == wxEVT_CHECKBOX )
1685     {
1686         return true;
1687     }
1688     return false;
1689 }
1690 
1691 
GetValueFromControl(wxVariant & variant,wxPGProperty * property,wxWindow * ctrl) const1692 bool wxPGCheckBoxEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
1693 {
1694     wxSimpleCheckBox* cb = (wxSimpleCheckBox*)ctrl;
1695 
1696     int index = cb->m_state;
1697 
1698     if ( index != property->GetChoiceSelection() ||
1699          // Changing unspecified always causes event (returning
1700          // true here should be enough to trigger it).
1701          property->IsValueUnspecified()
1702        )
1703     {
1704         return property->IntToValue(variant, index, wxPG_PROPERTY_SPECIFIC);
1705     }
1706     return false;
1707 }
1708 
1709 
SetControlIntValue(wxPGProperty * WXUNUSED (property),wxWindow * ctrl,int value) const1710 void wxPGCheckBoxEditor::SetControlIntValue( wxPGProperty* WXUNUSED(property), wxWindow* ctrl, int value ) const
1711 {
1712     if ( value != 0 ) value = 1;
1713     ((wxSimpleCheckBox*)ctrl)->m_state = value;
1714     ctrl->Refresh();
1715 }
1716 
1717 
SetValueToUnspecified(wxPGProperty * WXUNUSED (property),wxWindow * ctrl) const1718 void wxPGCheckBoxEditor::SetValueToUnspecified( wxPGProperty* WXUNUSED(property), wxWindow* ctrl ) const
1719 {
1720     ((wxSimpleCheckBox*)ctrl)->m_state = wxSCB_STATE_UNSPECIFIED;
1721     ctrl->Refresh();
1722 }
1723 
1724 
~wxPGCheckBoxEditor()1725 wxPGCheckBoxEditor::~wxPGCheckBoxEditor()
1726 {
1727     wxPG_EDITOR(CheckBox) = NULL;
1728 }
1729 
1730 #endif // wxPG_INCLUDE_CHECKBOX
1731 
1732 // -----------------------------------------------------------------------
1733 
GetEditorControl() const1734 wxWindow* wxPropertyGrid::GetEditorControl() const
1735 {
1736     wxWindow* ctrl = m_wndEditor;
1737 
1738     if ( !ctrl )
1739         return ctrl;
1740 
1741     return ctrl;
1742 }
1743 
1744 // -----------------------------------------------------------------------
1745 
CorrectEditorWidgetSizeX()1746 void wxPropertyGrid::CorrectEditorWidgetSizeX()
1747 {
1748     int secWid = 0;
1749 
1750     // Use fixed selColumn 1 for main editor widgets
1751     int newSplitterx = m_pState->DoGetSplitterPosition(0);
1752     int newWidth = newSplitterx + m_pState->m_colWidths[1];
1753 
1754     if ( m_wndEditor2 )
1755     {
1756         // if width change occurred, move secondary wnd by that amount
1757         wxRect r = m_wndEditor2->GetRect();
1758         secWid = r.width;
1759         r.x = newWidth - secWid;
1760 
1761         m_wndEditor2->SetSize( r );
1762 
1763         // if primary is textctrl, then we have to add some extra space
1764 #ifdef __WXMAC__
1765         if ( m_wndEditor )
1766 #else
1767         if ( wxDynamicCast(m_wndEditor, wxTextCtrl) )
1768 #endif
1769             secWid += wxPG_TEXTCTRL_AND_BUTTON_SPACING;
1770     }
1771 
1772     if ( m_wndEditor )
1773     {
1774         wxRect r = m_wndEditor->GetRect();
1775 
1776         r.x = newSplitterx+m_ctrlXAdjust;
1777 
1778         if ( !(m_iFlags & wxPG_FL_FIXED_WIDTH_EDITOR) )
1779             r.width = newWidth - r.x - secWid;
1780 
1781         m_wndEditor->SetSize(r);
1782     }
1783 
1784     if ( m_wndEditor2 )
1785         m_wndEditor2->Refresh();
1786 }
1787 
1788 // -----------------------------------------------------------------------
1789 
CorrectEditorWidgetPosY()1790 void wxPropertyGrid::CorrectEditorWidgetPosY()
1791 {
1792     wxPGProperty* selected = GetSelection();
1793 
1794     if ( selected )
1795     {
1796         if ( m_labelEditor )
1797         {
1798             wxRect r = GetEditorWidgetRect(selected, m_selColumn);
1799             wxPoint pos = m_labelEditor->GetPosition();
1800 
1801             // Calculate y offset
1802             int offset = pos.y % m_lineHeight;
1803 
1804             m_labelEditor->Move(pos.x, r.y + offset);
1805         }
1806 
1807         if ( m_wndEditor || m_wndEditor2 )
1808         {
1809             wxRect r = GetEditorWidgetRect(selected, 1);
1810 
1811             if ( m_wndEditor )
1812             {
1813                 wxPoint pos = m_wndEditor->GetPosition();
1814 
1815                 // Calculate y offset
1816                 int offset = pos.y % m_lineHeight;
1817 
1818                 m_wndEditor->Move(pos.x, r.y + offset);
1819             }
1820 
1821             if ( m_wndEditor2 )
1822             {
1823                 wxPoint pos = m_wndEditor2->GetPosition();
1824 
1825                 m_wndEditor2->Move(pos.x, r.y);
1826             }
1827         }
1828     }
1829 }
1830 
1831 // -----------------------------------------------------------------------
1832 
1833 // Fixes position of wxTextCtrl-like control (wxSpinCtrl usually
1834 // fits into that category as well).
FixPosForTextCtrl(wxWindow * ctrl,unsigned int WXUNUSED (forColumn),const wxPoint & offset)1835 void wxPropertyGrid::FixPosForTextCtrl( wxWindow* ctrl,
1836                                         unsigned int WXUNUSED(forColumn),
1837                                         const wxPoint& offset )
1838 {
1839     // Center the control vertically
1840     wxRect finalPos = ctrl->GetRect();
1841     int y_adj = (m_lineHeight - finalPos.height)/2 + wxPG_TEXTCTRLYADJUST;
1842 
1843     // Prevent over-sized control
1844     int sz_dec = (y_adj + finalPos.height) - m_lineHeight;
1845     if ( sz_dec < 0 ) sz_dec = 0;
1846 
1847     finalPos.y += y_adj;
1848     finalPos.height -= (y_adj+sz_dec);
1849 
1850 #ifndef wxPG_TEXTCTRLXADJUST
1851     int textCtrlXAdjust = wxPG_XBEFORETEXT - 1;
1852 
1853     wxTextCtrl* tc = static_cast<wxTextCtrl*>(ctrl);
1854     tc->SetMargins(0);
1855 #else
1856     int textCtrlXAdjust = wxPG_TEXTCTRLXADJUST;
1857 #endif
1858 
1859     finalPos.x += textCtrlXAdjust;
1860     finalPos.width -= textCtrlXAdjust;
1861 
1862     finalPos.x += offset.x;
1863     finalPos.y += offset.y;
1864 
1865     ctrl->SetSize(finalPos);
1866 }
1867 
1868 // -----------------------------------------------------------------------
1869 
GenerateEditorTextCtrl(const wxPoint & pos,const wxSize & sz,const wxString & value,wxWindow * secondary,int extraStyle,int maxLen,unsigned int forColumn)1870 wxWindow* wxPropertyGrid::GenerateEditorTextCtrl( const wxPoint& pos,
1871                                                   const wxSize& sz,
1872                                                   const wxString& value,
1873                                                   wxWindow* secondary,
1874                                                   int extraStyle,
1875                                                   int maxLen,
1876                                                   unsigned int forColumn )
1877 {
1878     wxWindowID id = wxPG_SUBID1;
1879     wxPGProperty* prop = GetSelection();
1880     wxASSERT(prop);
1881 
1882     int tcFlags = wxTE_PROCESS_ENTER | extraStyle;
1883 
1884     if ( prop->HasFlag(wxPG_PROP_READONLY) && forColumn == 1 )
1885         tcFlags |= wxTE_READONLY;
1886 
1887     wxPoint p(pos.x,pos.y);
1888     wxSize s(sz.x,sz.y);
1889 
1890    // Need to reduce width of text control on Mac
1891 #if defined(__WXMAC__)
1892     s.x -= 8;
1893 #endif
1894 
1895     // For label editors, trim the size to allow better splitter grabbing
1896     if ( forColumn != 1 )
1897         s.x -= 2;
1898 
1899     // Take button into acccount
1900     if ( secondary )
1901     {
1902         s.x -= (secondary->GetSize().x + wxPG_TEXTCTRL_AND_BUTTON_SPACING);
1903         m_iFlags &= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE);
1904     }
1905 
1906     // If the height is significantly higher, then use border, and fill the rect exactly.
1907     bool hasSpecialSize = false;
1908 
1909     if ( (sz.y - m_lineHeight) > 5 )
1910         hasSpecialSize = true;
1911 
1912     wxWindow* ctrlParent = GetPanel();
1913 
1914     if ( !hasSpecialSize )
1915         tcFlags |= wxBORDER_NONE;
1916 
1917     wxTextCtrl* tc = new wxTextCtrl();
1918 
1919 #if defined(__WXMSW__)
1920     tc->Hide();
1921 #endif
1922     SetupTextCtrlValue(value);
1923     tc->Create(ctrlParent,id,value, p, s,tcFlags);
1924 
1925 #if defined(__WXMSW__)
1926     // On Windows, we need to override read-only text ctrl's background
1927     // colour to white. One problem with native 'grey' background is that
1928     // tc->GetBackgroundColour() doesn't seem to return correct value
1929     // for it.
1930     if ( tcFlags & wxTE_READONLY )
1931     {
1932         wxVisualAttributes vattrs = tc->GetDefaultAttributes();
1933         tc->SetBackgroundColour(vattrs.colBg);
1934     }
1935 #endif
1936 
1937     // This code is repeated from DoSelectProperty(). However, font boldness
1938     // must be set before margin is set up below in FixPosForTextCtrl().
1939     if ( forColumn == 1 &&
1940          prop->HasFlag(wxPG_PROP_MODIFIED) &&
1941          HasFlag(wxPG_BOLD_MODIFIED) )
1942          tc->SetFont( m_captionFont );
1943 
1944     // Center the control vertically
1945     if ( !hasSpecialSize )
1946         FixPosForTextCtrl(tc, forColumn);
1947 
1948     if ( forColumn != 1 )
1949     {
1950         tc->SetBackgroundColour(m_colSelBack);
1951         tc->SetForegroundColour(m_colSelFore);
1952     }
1953 
1954 #ifdef __WXMSW__
1955     tc->Show();
1956     if ( secondary )
1957         secondary->Show();
1958 #endif
1959 
1960     // Set maximum length
1961     if ( maxLen > 0 )
1962         tc->SetMaxLength( maxLen );
1963 
1964     wxVariant attrVal = prop->GetAttribute(wxPG_ATTR_AUTOCOMPLETE);
1965     if ( !attrVal.IsNull() )
1966     {
1967         wxASSERT(attrVal.GetType() == wxS("arrstring"));
1968         tc->AutoComplete(attrVal.GetArrayString());
1969     }
1970 
1971     // Set hint text
1972     tc->SetHint(prop->GetHintText());
1973 
1974     return tc;
1975 }
1976 
1977 // -----------------------------------------------------------------------
1978 
GenerateEditorButton(const wxPoint & pos,const wxSize & sz)1979 wxWindow* wxPropertyGrid::GenerateEditorButton( const wxPoint& pos, const wxSize& sz )
1980 {
1981     wxWindowID id = wxPG_SUBID2;
1982     wxPGProperty* selected = GetSelection();
1983     wxASSERT(selected);
1984 
1985 #ifdef __WXMAC__
1986    // Decorations are chunky on Mac, and we can't make the button square, so
1987    // do things a bit differently on this platform.
1988 
1989    wxPoint p(pos.x+sz.x,
1990              pos.y+wxPG_BUTTON_SIZEDEC-wxPG_NAT_BUTTON_BORDER_Y);
1991    wxSize s(25, -1);
1992 
1993    wxButton* but = new wxButton();
1994    but->Create(GetPanel(),id,wxS("..."),p,s,wxWANTS_CHARS);
1995 
1996    // Now that we know the size, move to the correct position
1997    p.x = pos.x + sz.x - but->GetSize().x - 2;
1998    but->Move(p);
1999 
2000 #else
2001     wxSize s(sz.y-(wxPG_BUTTON_SIZEDEC*2)+(wxPG_NAT_BUTTON_BORDER_Y*2),
2002         sz.y-(wxPG_BUTTON_SIZEDEC*2)+(wxPG_NAT_BUTTON_BORDER_Y*2));
2003 
2004     // Reduce button width to lineheight
2005     if ( s.x > m_lineHeight )
2006         s.x = m_lineHeight;
2007 
2008 #ifdef __WXGTK__
2009     // On wxGTK, take fixed button margins into account
2010     if ( s.x < 25 )
2011         s.x = 25;
2012 #endif
2013 
2014     wxPoint p(pos.x+sz.x-s.x,
2015         pos.y+wxPG_BUTTON_SIZEDEC-wxPG_NAT_BUTTON_BORDER_Y);
2016 
2017     wxButton* but = new wxButton();
2018   #ifdef __WXMSW__
2019     but->Hide();
2020   #endif
2021     but->Create(GetPanel(),id,wxS("..."),p,s,wxWANTS_CHARS);
2022 
2023   #ifdef __WXGTK__
2024     wxFont font = GetFont();
2025     font.SetPointSize(font.GetPointSize()-2);
2026     but->SetFont(font);
2027   #else
2028     but->SetFont(GetFont());
2029   #endif
2030 #endif
2031 
2032     if ( selected->HasFlag(wxPG_PROP_READONLY) )
2033         but->Disable();
2034 
2035     return but;
2036 }
2037 
2038 // -----------------------------------------------------------------------
2039 
GenerateEditorTextCtrlAndButton(const wxPoint & pos,const wxSize & sz,wxWindow ** psecondary,int limitedEditing,wxPGProperty * property)2040 wxWindow* wxPropertyGrid::GenerateEditorTextCtrlAndButton( const wxPoint& pos,
2041                                                            const wxSize& sz,
2042                                                            wxWindow** psecondary,
2043                                                            int limitedEditing,
2044                                                            wxPGProperty* property )
2045 {
2046     wxButton* but = (wxButton*)GenerateEditorButton(pos,sz);
2047     *psecondary = (wxWindow*)but;
2048 
2049     if ( limitedEditing )
2050     {
2051     #ifdef __WXMSW__
2052         // There is button Show in GenerateEditorTextCtrl as well
2053         but->Show();
2054     #endif
2055         return NULL;
2056     }
2057 
2058     wxString text;
2059 
2060     if ( !property->IsValueUnspecified() )
2061         text = property->GetValueAsString(property->HasFlag(wxPG_PROP_READONLY)?0:wxPG_EDITABLE_VALUE);
2062 
2063     return GenerateEditorTextCtrl(pos,sz,text,but,property->m_maxLen);
2064 }
2065 
2066 // -----------------------------------------------------------------------
2067 
SetEditorAppearance(const wxPGCell & cell,bool unspecified)2068 void wxPropertyGrid::SetEditorAppearance( const wxPGCell& cell,
2069                                           bool unspecified )
2070 {
2071     wxPGProperty* property = GetSelection();
2072     if ( !property )
2073         return;
2074     wxWindow* ctrl = GetEditorControl();
2075     if ( !ctrl )
2076         return;
2077 
2078     property->GetEditorClass()->SetControlAppearance( this,
2079                                                       property,
2080                                                       ctrl,
2081                                                       cell,
2082                                                       m_editorAppearance,
2083                                                       unspecified );
2084 
2085     m_editorAppearance = cell;
2086 }
2087 
2088 // -----------------------------------------------------------------------
2089 
GetEditorTextCtrl() const2090 wxTextCtrl* wxPropertyGrid::GetEditorTextCtrl() const
2091 {
2092     wxWindow* wnd = GetEditorControl();
2093 
2094     if ( !wnd )
2095         return NULL;
2096 
2097     if ( wxDynamicCast(wnd, wxTextCtrl) )
2098         return wxStaticCast(wnd, wxTextCtrl);
2099 
2100     if ( wxDynamicCast(wnd, wxOwnerDrawnComboBox) )
2101     {
2102         wxOwnerDrawnComboBox* cb = wxStaticCast(wnd, wxOwnerDrawnComboBox);
2103         return cb->GetTextCtrl();
2104     }
2105 
2106     return NULL;
2107 }
2108 
2109 // -----------------------------------------------------------------------
2110 
GetEditorByName(const wxString & editorName)2111 wxPGEditor* wxPropertyGridInterface::GetEditorByName( const wxString& editorName )
2112 {
2113     wxPGHashMapS2P::const_iterator it;
2114 
2115     it = wxPGGlobalVars->m_mapEditorClasses.find(editorName);
2116     if ( it == wxPGGlobalVars->m_mapEditorClasses.end() )
2117         return NULL;
2118     return (wxPGEditor*) it->second;
2119 }
2120 
2121 // -----------------------------------------------------------------------
2122 // wxPGEditorDialogAdapter
2123 // -----------------------------------------------------------------------
2124 
IMPLEMENT_ABSTRACT_CLASS(wxPGEditorDialogAdapter,wxObject)2125 IMPLEMENT_ABSTRACT_CLASS(wxPGEditorDialogAdapter, wxObject)
2126 
2127 bool wxPGEditorDialogAdapter::ShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
2128 {
2129     if ( !propGrid->EditorValidate() )
2130         return false;
2131 
2132     bool res = DoShowDialog( propGrid, property );
2133 
2134     if ( res )
2135     {
2136         propGrid->ValueChangeInEvent( m_value );
2137         return true;
2138     }
2139 
2140     return false;
2141 }
2142 
2143 // -----------------------------------------------------------------------
2144 // wxPGMultiButton
2145 // -----------------------------------------------------------------------
2146 
wxPGMultiButton(wxPropertyGrid * pg,const wxSize & sz)2147 wxPGMultiButton::wxPGMultiButton( wxPropertyGrid* pg, const wxSize& sz )
2148     : wxWindow( pg->GetPanel(), wxPG_SUBID2, wxPoint(-100,-100), wxSize(0, sz.y) ),
2149       m_fullEditorSize(sz), m_buttonsWidth(0)
2150 {
2151     SetBackgroundColour(pg->GetCellBackgroundColour());
2152 }
2153 
Finalize(wxPropertyGrid * WXUNUSED (propGrid),const wxPoint & pos)2154 void wxPGMultiButton::Finalize( wxPropertyGrid* WXUNUSED(propGrid),
2155                                 const wxPoint& pos )
2156 {
2157     Move( pos.x + m_fullEditorSize.x - m_buttonsWidth, pos.y );
2158 }
2159 
GenId(int itemid) const2160 int wxPGMultiButton::GenId( int itemid ) const
2161 {
2162     if ( itemid < -1 )
2163     {
2164         if ( m_buttons.size() )
2165             itemid = GetButton(m_buttons.size()-1)->GetId() + 1;
2166         else
2167             itemid = wxPG_SUBID2;
2168     }
2169     return itemid;
2170 }
2171 
2172 #if wxUSE_BMPBUTTON
Add(const wxBitmap & bitmap,int itemid)2173 void wxPGMultiButton::Add( const wxBitmap& bitmap, int itemid )
2174 {
2175     itemid = GenId(itemid);
2176     wxSize sz = GetSize();
2177     wxButton* button = new wxBitmapButton( this, itemid, bitmap,
2178                                            wxPoint(sz.x, 0),
2179                                            wxSize(sz.y, sz.y) );
2180     DoAddButton( button, sz );
2181 }
2182 #endif
2183 
Add(const wxString & label,int itemid)2184 void wxPGMultiButton::Add( const wxString& label, int itemid )
2185 {
2186     itemid = GenId(itemid);
2187     wxSize sz = GetSize();
2188     wxButton* button = new wxButton( this, itemid, label, wxPoint(sz.x, 0),
2189                                      wxSize(sz.y, sz.y) );
2190     DoAddButton( button, sz );
2191 }
2192 
DoAddButton(wxWindow * button,const wxSize & sz)2193 void wxPGMultiButton::DoAddButton( wxWindow* button,
2194                                    const wxSize& sz )
2195 {
2196     m_buttons.push_back(button);
2197     int bw = button->GetSize().x;
2198     SetSize(wxSize(sz.x+bw,sz.y));
2199     m_buttonsWidth += bw;
2200 }
2201 
2202 // -----------------------------------------------------------------------
2203 
2204 #endif  // wxUSE_PROPGRID
2205