1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/propgrid/editors.cpp
3 // Purpose: wxPropertyGrid editors
4 // Author: Jaakko Salli
8 // Copyright: (c) Jaakko Salli
9 // Licence: wxWindows license
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
23 #include "wx/object.h"
25 #include "wx/string.h"
28 #include "wx/window.h"
31 #include "wx/dcclient.h"
32 #include "wx/dcmemory.h"
33 #include "wx/button.h"
36 #include "wx/cursor.h"
37 #include "wx/dialog.h"
38 #include "wx/settings.h"
39 #include "wx/msgdlg.h"
40 #include "wx/choice.h"
41 #include "wx/stattext.h"
42 #include "wx/scrolwin.h"
43 #include "wx/dirdlg.h"
44 #include "wx/layout.h"
46 #include "wx/textdlg.h"
47 #include "wx/filedlg.h"
48 #include "wx/statusbr.h"
55 #include "wx/dcbuffer.h"
56 #include "wx/bmpbuttn.h"
59 // This define is necessary to prevent macro clearing
60 #define __wxPG_SOURCE_FILE__
62 #include <wx/propgrid/propgrid.h>
63 #include <wx/propgrid/editors.h>
64 #include <wx/propgrid/props.h>
66 #if wxPG_USE_RENDERER_NATIVE
67 #include <wx/renderer.h>
70 // How many pixels between textctrl and button
72 #define wxPG_TEXTCTRL_AND_BUTTON_SPACING 8
74 #define wxPG_TEXTCTRL_AND_BUTTON_SPACING 2
77 #define wxPG_BUTTON_SIZEDEC 0
79 #include <wx/odcombo.h>
82 #include <wx/msw/private.h>
85 // -----------------------------------------------------------------------
87 #if defined(__WXMSW__)
89 #define wxPG_NAT_TEXTCTRL_BORDER_X 0 // Unremovable border of native textctrl.
90 #define wxPG_NAT_TEXTCTRL_BORDER_Y 0 // Unremovable border of native textctrl.
92 #define wxPG_NAT_BUTTON_BORDER_ANY 1
93 #define wxPG_NAT_BUTTON_BORDER_X 1
94 #define wxPG_NAT_BUTTON_BORDER_Y 1
96 #define wxPG_CHECKMARK_XADJ 1
97 #define wxPG_CHECKMARK_YADJ (-1)
98 #define wxPG_CHECKMARK_WADJ 0
99 #define wxPG_CHECKMARK_HADJ 0
100 #define wxPG_CHECKMARK_DEFLATE 0
102 #define wxPG_TEXTCTRLYADJUST (m_spacingy+0)
104 #elif defined(__WXGTK__)
106 #define wxPG_CHECKMARK_XADJ 0
107 #define wxPG_CHECKMARK_YADJ 0
108 #define wxPG_CHECKMARK_WADJ (-1)
109 #define wxPG_CHECKMARK_HADJ (-1)
110 #define wxPG_CHECKMARK_DEFLATE 3
112 #define wxPG_NAT_TEXTCTRL_BORDER_X 3 // Unremovable border of native textctrl.
113 #define wxPG_NAT_TEXTCTRL_BORDER_Y 3 // Unremovable border of native textctrl.
115 #define wxPG_NAT_BUTTON_BORDER_ANY 1
116 #define wxPG_NAT_BUTTON_BORDER_X 1
117 #define wxPG_NAT_BUTTON_BORDER_Y 1
119 #define wxPG_TEXTCTRLYADJUST 0
121 #elif defined(__WXMAC__)
123 #define wxPG_CHECKMARK_XADJ 0
124 #define wxPG_CHECKMARK_YADJ 0
125 #define wxPG_CHECKMARK_WADJ 0
126 #define wxPG_CHECKMARK_HADJ 0
127 #define wxPG_CHECKMARK_DEFLATE 0
129 #define wxPG_NAT_TEXTCTRL_BORDER_X 0 // Unremovable border of native textctrl.
130 #define wxPG_NAT_TEXTCTRL_BORDER_Y 0 // Unremovable border of native textctrl.
132 #define wxPG_NAT_BUTTON_BORDER_ANY 0
133 #define wxPG_NAT_BUTTON_BORDER_X 0
134 #define wxPG_NAT_BUTTON_BORDER_Y 0
136 #define wxPG_TEXTCTRLYADJUST 3
140 #define wxPG_CHECKMARK_XADJ 0
141 #define wxPG_CHECKMARK_YADJ 0
142 #define wxPG_CHECKMARK_WADJ 0
143 #define wxPG_CHECKMARK_HADJ 0
144 #define wxPG_CHECKMARK_DEFLATE 0
146 #define wxPG_NAT_TEXTCTRL_BORDER_X 0 // Unremovable border of native textctrl.
147 #define wxPG_NAT_TEXTCTRL_BORDER_Y 0 // Unremovable border of native textctrl.
149 #define wxPG_NAT_BUTTON_BORDER_ANY 0
150 #define wxPG_NAT_BUTTON_BORDER_X 0
151 #define wxPG_NAT_BUTTON_BORDER_Y 0
153 #define wxPG_TEXTCTRLYADJUST 0
157 #if (!wxPG_NAT_TEXTCTRL_BORDER_X && !wxPG_NAT_TEXTCTRL_BORDER_Y)
158 #define wxPG_ENABLE_CLIPPER_WINDOW 0
160 #define wxPG_ENABLE_CLIPPER_WINDOW 1
165 #define wxPG_CHOICEXADJUST 0
166 #define wxPG_CHOICEYADJUST 0
168 #define ODCB_CUST_PAINT_MARGIN 6 // Number added to image width for SetCustomPaintWidth
170 // Milliseconds to wait for two mouse-ups after focus inorder
171 // to trigger a double-click.
172 #define DOUBLE_CLICK_CONVERSION_TRESHOLD 500
174 // -----------------------------------------------------------------------
176 // -----------------------------------------------------------------------
178 IMPLEMENT_ABSTRACT_CLASS(wxPGEditor
, wxObject
)
181 wxPGEditor
::~wxPGEditor()
185 void wxPGEditor
::DrawValue( wxDC
& dc
, const wxRect
& rect
, wxPGProperty
* property
, const wxString
& text
) const
187 if ( !property
->IsValueUnspecified() )
188 dc
.DrawText( text
, rect
.x
+wxPG_XBEFORETEXT
, rect
.y
);
191 bool wxPGEditor
::GetValueFromControl( wxVariant
&, wxPGProperty
*, wxWindow
* ) const
196 void wxPGEditor
::SetControlStringValue( wxPGProperty
* WXUNUSED(property
), wxWindow
*, const wxString
& ) const
201 void wxPGEditor
::SetControlIntValue( wxPGProperty
* WXUNUSED(property
), wxWindow
*, int ) const
206 int wxPGEditor
::InsertItem( wxWindow
*, const wxString
&, int ) const
212 void wxPGEditor
::DeleteItem( wxWindow
*, int ) const
218 void wxPGEditor
::OnFocus( wxPGProperty
*, wxWindow
* ) const
223 bool wxPGEditor
::CanContainCustomImage() const
228 // -----------------------------------------------------------------------
230 // -----------------------------------------------------------------------
233 #if wxPG_ENABLE_CLIPPER_WINDOW
236 // Clipper window is used to "remove" borders from controls
237 // which otherwise insist on having them despite of supplied
238 // wxNO_BORDER window style.
240 class wxPGClipperWindow
: public wxWindow
242 DECLARE_CLASS(wxPGClipperWindow
)
248 wxPGClipperWindow
::Init();
251 wxPGClipperWindow(wxWindow
* parent
,
253 const wxPoint
& pos
= wxDefaultPosition
,
254 const wxSize
& size
= wxDefaultSize
)
257 Create(parent
,id
,pos
,size
);
260 void Create(wxWindow
* parent
,
262 const wxPoint
& pos
= wxDefaultPosition
,
263 const wxSize
& size
= wxDefaultSize
);
265 virtual ~wxPGClipperWindow();
267 virtual bool ProcessEvent(wxEvent
& event
);
269 inline wxWindow
* GetControl() const { return m_ctrl
; }
271 // This is called before wxControl is constructed.
272 void GetControlRect( int xadj
, int yadj
, wxPoint
& pt
, wxSize
& sz
);
274 // This is caleed after wxControl has been constructed.
275 void SetControl( wxWindow
* ctrl
);
277 virtual void Refresh( bool eraseBackground
= true,
278 const wxRect
*rect
= (const wxRect
*) NULL
);
279 virtual void SetFocus();
281 virtual bool SetFont(const wxFont
& font
);
283 inline int GetXClip() const { return m_xadj
; }
285 inline int GetYClip() const { return m_yadj
; }
290 int m_xadj
; // Horizontal border clip.
292 int m_yadj
; // Vertical border clip.
297 m_ctrl
= (wxWindow
*) NULL
;
302 IMPLEMENT_CLASS(wxPGClipperWindow
,wxWindow
)
305 // This is called before wxControl is constructed.
306 void wxPGClipperWindow
::GetControlRect( int xadj
, int yadj
, wxPoint
& pt
, wxSize
& sz
)
312 wxSize own_size
= GetSize();
313 sz
.x
= own_size
.x
+(xadj
*2);
314 sz
.y
= own_size
.y
+(yadj
*2);
318 // This is caleed after wxControl has been constructed.
319 void wxPGClipperWindow
::SetControl( wxWindow
* ctrl
)
323 // GTK requires this.
324 ctrl
->SetSizeHints(3,3);
326 // Correct size of this window to match the child.
327 wxSize sz
= GetSize();
328 wxSize chsz
= ctrl
->GetSize();
330 int hei_adj
= chsz
.y
- (sz
.y
+(m_yadj
*2));
332 SetSize(sz
.x
,chsz
.y
-(m_yadj
*2));
337 void wxPGClipperWindow
::Refresh( bool eraseBackground
, const wxRect
*rect
)
339 wxWindow
::Refresh(false,rect
);
341 m_ctrl
->Refresh(eraseBackground
);
345 // Pass focus to control
346 void wxPGClipperWindow
::SetFocus()
351 wxWindow
::SetFocus();
355 bool wxPGClipperWindow
::SetFont(const wxFont
& font
)
357 bool res
= wxWindow
::SetFont(font
);
359 return m_ctrl
->SetFont(font
);
364 void wxPGClipperWindow
::Create(wxWindow
* parent
,
369 wxWindow
::Create(parent
,id
,pos
,size
);
373 wxPGClipperWindow
::~wxPGClipperWindow()
378 bool wxPGClipperWindow
::ProcessEvent(wxEvent
& event
)
380 if ( event
.GetEventType() == wxEVT_SIZE
)
384 // Maintain correct size relationship.
385 wxSize sz
= GetSize();
386 m_ctrl
->SetSize(sz
.x
+(m_xadj
*2),sz
.y
+(m_yadj
*2));
391 return wxWindow
::ProcessEvent(event
);
394 #endif // wxPG_ENABLE_CLIPPER_WINDOW
396 /*wxWindow* wxPropertyGrid::GetActualEditorControl( wxWindow* ctrl )
398 #if wxPG_ENABLE_CLIPPER_WINDOW
399 // Pass real control instead of clipper window
400 if ( ctrl->IsKindOf(CLASSINFO(wxPGClipperWindow)) )
402 return ((wxPGClipperWindow*)ctrl)->GetControl();
409 // -----------------------------------------------------------------------
410 // wxPGTextCtrlEditor
411 // -----------------------------------------------------------------------
413 // Clipper window support macro (depending on whether it is used
414 // for this editor or not)
415 #if wxPG_NAT_TEXTCTRL_BORDER_X || wxPG_NAT_TEXTCTRL_BORDER_Y
416 #define wxPG_NAT_TEXTCTRL_BORDER_ANY 1
418 #define wxPG_NAT_TEXTCTRL_BORDER_ANY 0
421 WX_PG_IMPLEMENT_EDITOR_CLASS(TextCtrl
,wxPGTextCtrlEditor
,wxPGEditor
)
424 wxPGWindowList wxPGTextCtrlEditor
::CreateControls( wxPropertyGrid
* propGrid
,
425 wxPGProperty
* property
,
427 const wxSize
& sz
) const
432 // If has children, and limited editing is specified, then don't create.
433 if ( (property
->GetFlags() & wxPG_PROP_NOEDITOR
) &&
434 property
->GetChildCount() )
435 return (wxWindow
*) NULL
;
437 if ( !property
->IsValueUnspecified() )
438 text
= property
->GetValueString(property
->HasFlag(wxPG_PROP_READONLY
)?
0:wxPG_EDITABLE_VALUE
);
441 if ( (property
->GetFlags() & wxPG_PROP_PASSWORD
) &&
442 property
->IsKindOf(CLASSINFO(wxStringProperty
)) )
443 flags
|= wxTE_PASSWORD
;
445 wxWindow
* wnd
= propGrid
->GenerateEditorTextCtrl(pos
,sz
,text
,(wxWindow
*)NULL
,flags
,
446 property
->GetMaxLength());
452 void wxPGTextCtrlEditor
::DrawValue( wxDC
& dc
, wxPGProperty
* property
, const wxRect
& rect
) const
454 if ( !property
->IsValueUnspecified() )
456 wxString drawStr
= property
->GetDisplayedString();
458 // Code below should no longer be needed, as the obfuscation
459 // is now done in GetValueAsString.
460 /*if ( (property->GetFlags() & wxPG_PROP_PASSWORD) &&
461 property->IsKindOf(WX_PG_CLASSINFO(wxStringProperty)) )
463 size_t a = drawStr.length();
465 drawStr.Append(wxS('*'),a);
467 dc
.DrawText( drawStr
, rect
.x
+wxPG_XBEFORETEXT
, rect
.y
);
472 void wxPGTextCtrlEditor
::UpdateControl( wxPGProperty
* property
, wxWindow
* ctrl
) const
474 wxTextCtrl
* tc
= wxDynamicCast(ctrl
, wxTextCtrl
);
479 if ( tc
->HasFlag(wxTE_PASSWORD
) )
480 s
= property
->GetValueAsString(wxPG_FULL_VALUE
);
482 s
= property
->GetDisplayedString();
488 // Provided so that, for example, ComboBox editor can use the same code
489 // (multiple inheritance would get way too messy).
490 bool wxPGTextCtrlEditor
::OnTextCtrlEvent( wxPropertyGrid
* propGrid
,
491 wxPGProperty
* WXUNUSED(property
),
498 if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_ENTER
)
500 if ( propGrid
->IsEditorsValueModified() )
505 else if ( event
.GetEventType() == wxEVT_COMMAND_TEXT_UPDATED
)
508 // Pass this event outside wxPropertyGrid so that,
509 // if necessary, program can tell when user is editing
511 // FIXME: Is it safe to change event id in the middle of event
512 // processing (seems to work, but...)?
514 event
.SetId(propGrid
->GetId());
516 propGrid
->EditorsValueWasModified();
522 bool wxPGTextCtrlEditor
::OnEvent( wxPropertyGrid
* propGrid
,
523 wxPGProperty
* property
,
525 wxEvent
& event
) const
527 return wxPGTextCtrlEditor
::OnTextCtrlEvent(propGrid
,property
,ctrl
,event
);
531 bool wxPGTextCtrlEditor
::GetTextCtrlValueFromControl( wxVariant
& variant
, wxPGProperty
* property
, wxWindow
* ctrl
)
533 wxTextCtrl
* tc
= wxStaticCast(ctrl
, wxTextCtrl
);
534 wxString textVal
= tc
->GetValue();
536 if ( property
->UsesAutoUnspecified() && !textVal
.length() )
542 bool res
= property
->StringToValue(variant
, textVal
, wxPG_EDITABLE_VALUE
);
544 // Changing unspecified always causes event (returning
545 // true here should be enough to trigger it).
546 // TODO: Move to propgrid.cpp
547 if ( !res
&& variant
.IsNull() )
554 bool wxPGTextCtrlEditor
::GetValueFromControl( wxVariant
& variant
, wxPGProperty
* property
, wxWindow
* ctrl
) const
556 return wxPGTextCtrlEditor
::GetTextCtrlValueFromControl(variant
, property
, ctrl
);
560 void wxPGTextCtrlEditor
::SetValueToUnspecified( wxPGProperty
* property
, wxWindow
* ctrl
) const
562 wxTextCtrl
* tc
= wxStaticCast(ctrl
, wxTextCtrl
);
564 wxPropertyGrid
* pg
= property
->GetGrid();
565 wxASSERT(pg
); // Really, property grid should exist if editor does
567 tc
->SetValue(wxEmptyString
);
571 void wxPGTextCtrlEditor
::SetControlStringValue( wxPGProperty
* property
, wxWindow
* ctrl
, const wxString
& txt
) const
573 wxTextCtrl
* tc
= wxStaticCast(ctrl
, wxTextCtrl
);
575 wxPropertyGrid
* pg
= property
->GetGrid();
576 wxASSERT(pg
); // Really, property grid should exist if editor does
582 void wxPGTextCtrlEditor
::OnFocus( wxPGProperty
*, wxWindow
* wnd
) const
584 wxTextCtrl
* tc
= wxStaticCast(wnd
, wxTextCtrl
);
586 tc
->SetSelection(-1,-1);
590 wxPGTextCtrlEditor
::~wxPGTextCtrlEditor() { }
593 // -----------------------------------------------------------------------
595 // -----------------------------------------------------------------------
598 WX_PG_IMPLEMENT_EDITOR_CLASS(Choice
,wxPGChoiceEditor
,wxPGEditor
)
601 // This is a special enhanced double-click processor class.
602 // In essence, it allows for double-clicks for which the
603 // first click "created" the control.
604 class wxPGDoubleClickProcessor
: public wxEvtHandler
608 wxPGDoubleClickProcessor( wxOwnerDrawnComboBox
* combo
, wxPGProperty
* property
)
611 m_timeLastMouseUp
= 0;
613 m_property
= property
;
614 m_downReceived
= false;
619 void OnMouseEvent( wxMouseEvent
& event
)
621 wxLongLong t
= ::wxGetLocalTimeMillis();
622 int evtType
= event
.GetEventType();
624 if ( m_property
->HasFlag(wxPG_PROP_USE_DCC
) &&
625 m_property
->IsKindOf(CLASSINFO(wxBoolProperty
)) &&
626 !m_combo
->IsPopupShown() )
628 // Just check that it is in the text area
629 wxPoint pt
= event
.GetPosition();
630 if ( m_combo
->GetTextRect().Contains(pt
) )
632 if ( evtType
== wxEVT_LEFT_DOWN
)
634 // Set value to avoid up-events without corresponding downs
635 m_downReceived
= true;
637 else if ( evtType
== wxEVT_LEFT_DCLICK
)
639 // We'll make our own double-clicks
640 event
.SetEventType(0);
643 else if ( evtType
== wxEVT_LEFT_UP
)
645 if ( m_downReceived
|| m_timeLastMouseUp
== 1 )
647 wxLongLong timeFromLastUp
= (t
-m_timeLastMouseUp
);
649 if ( timeFromLastUp
< DOUBLE_CLICK_CONVERSION_TRESHOLD
)
651 event
.SetEventType(wxEVT_LEFT_DCLICK
);
652 m_timeLastMouseUp
= 1;
656 m_timeLastMouseUp
= t
;
666 void OnSetFocus( wxFocusEvent
& event
)
668 m_timeLastMouseUp
= ::wxGetLocalTimeMillis();
673 wxLongLong m_timeLastMouseUp
;
674 wxOwnerDrawnComboBox
* m_combo
;
675 wxPGProperty
* m_property
; // Selected property
678 DECLARE_EVENT_TABLE()
681 BEGIN_EVENT_TABLE(wxPGDoubleClickProcessor
, wxEvtHandler
)
682 EVT_MOUSE_EVENTS(wxPGDoubleClickProcessor
::OnMouseEvent
)
683 EVT_SET_FOCUS(wxPGDoubleClickProcessor
::OnSetFocus
)
688 class wxPGComboBox
: public wxOwnerDrawnComboBox
693 : wxOwnerDrawnComboBox()
695 m_dclickProcessor
= (wxPGDoubleClickProcessor
*) NULL
;
696 m_sizeEventCalled
= false;
701 if ( m_dclickProcessor
)
703 RemoveEventHandler(m_dclickProcessor
);
704 delete m_dclickProcessor
;
708 bool Create(wxWindow
*parent
,
710 const wxString
& value
,
713 const wxArrayString
& choices
,
715 const wxValidator
& validator
= wxDefaultValidator
,
716 const wxString
& name
= wxS("wxOwnerDrawnComboBox"))
718 if ( !wxOwnerDrawnComboBox
::Create( parent
,
729 m_dclickProcessor
= new wxPGDoubleClickProcessor(this, GetGrid()->GetSelection() );
731 PushEventHandler(m_dclickProcessor
);
736 virtual void OnDrawItem( wxDC
& dc
, const wxRect
& rect
, int item
, int flags
) const
738 wxPropertyGrid
* pg
= GetGrid();
739 pg
->OnComboItemPaint((wxPGCustomComboControl
*)this,item
,dc
,(wxRect
&)rect
,flags
);
741 virtual wxCoord
OnMeasureItem( size_t item
) const
743 wxPropertyGrid
* pg
= GetGrid();
747 pg
->OnComboItemPaint((wxPGCustomComboControl
*)this,item
,*((wxDC
*)NULL
),rect
,0);
751 wxPropertyGrid
* GetGrid() const
753 wxPropertyGrid
* pg
= wxDynamicCast(GetParent()->GetParent(),wxPropertyGrid
);
758 virtual wxCoord
OnMeasureItemWidth( size_t item
) const
760 wxPropertyGrid
* pg
= GetGrid();
764 pg
->OnComboItemPaint((wxPGCustomComboControl
*)this,item
,*((wxDC
*)NULL
),rect
,0);
768 virtual void PositionTextCtrl( int WXUNUSED(textCtrlXAdjust
), int WXUNUSED(textCtrlYAdjust
) )
770 wxPropertyGrid
* pg
= GetGrid();
771 wxOwnerDrawnComboBox
::PositionTextCtrl(
772 wxPG_TEXTCTRLXADJUST
- (wxPG_XBEFOREWIDGET
+wxPG_CONTROL_MARGIN
+1) - 1,
773 pg
->GetSpacingY() + 2
778 wxPGDoubleClickProcessor
* m_dclickProcessor
;
779 bool m_sizeEventCalled
;
783 void wxPropertyGrid
::OnComboItemPaint( wxPGCustomComboControl
* pCc
,
789 wxPGComboBox
* pCb
= (wxPGComboBox
*)pCc
;
792 wxASSERT( IsKindOf(CLASSINFO(wxPropertyGrid
)) );
794 wxPGProperty
* p
= m_selected
;
797 const wxPGChoices
& choices
= p
->GetChoices();
798 const wxPGCommonValue
* comVal
= NULL
;
799 int comVals
= p
->GetDisplayedCommonValueCount();
800 int comValIndex
= -1;
803 if ( choices
.IsOk() )
804 choiceCount
= choices
.GetCount();
806 if ( item
>= choiceCount
&& comVals
> 0 )
808 comValIndex
= item
- choiceCount
;
809 comVal
= GetCommonValue(comValIndex
);
810 if ( !p
->IsValueUnspecified() )
811 text
= comVal
->GetLabel();
815 if ( !(flags
& wxODCB_PAINTING_CONTROL
) )
817 text
= pCb
->GetString(item
);
821 if ( !p
->IsValueUnspecified() )
822 text
= p
->GetValueString(0);
831 const wxBitmap
* itemBitmap
= NULL
;
833 if ( item
>= 0 && choices
.IsOk() && choices
.Item(item
).GetBitmap().Ok() && comValIndex
== -1 )
834 itemBitmap
= &choices
.Item(item
).GetBitmap();
837 // Decide what custom image size to use
840 cis
.x
= itemBitmap
->GetWidth();
841 cis
.y
= itemBitmap
->GetHeight();
845 cis
= GetImageSize(p
, item
);
850 // Default measure behaviour (no flexible, custom paint image only)
851 if ( rect
.width
< 0 )
854 GetTextExtent(text
, &x
, &y
, 0, 0, &m_font
);
855 rect
.width
= cis
.x
+ wxCC_CUSTOM_IMAGE_MARGIN1
+ wxCC_CUSTOM_IMAGE_MARGIN2
+ 9 + x
;
858 rect
.height
= cis
.y
+ 2;
862 wxPGPaintData paintdata
;
863 paintdata
.m_parent
= NULL
;
864 paintdata
.m_choiceItem
= item
;
866 // This is by the current (1.0.0b) spec - if painting control, item is -1
867 if ( (flags
& wxODCB_PAINTING_CONTROL
) )
868 paintdata
.m_choiceItem
= -1;
871 dc
.SetBrush(*wxWHITE_BRUSH
);
878 wxPoint
pt(rect
.x
+ wxPG_CONTROL_MARGIN
- wxPG_CHOICEXADJUST
- 1,
883 if ( flags
& wxODCB_PAINTING_CONTROL
)
884 renderFlags
|= wxPGCellRenderer
::Control
;
886 if ( flags
& wxODCB_PAINTING_SELECTED
)
887 renderFlags
|= wxPGCellRenderer
::Selected
;
889 if ( cis
.x
> 0 && (p
->HasFlag(wxPG_PROP_CUSTOMIMAGE
) || !(flags
& wxODCB_PAINTING_CONTROL
)) &&
890 ( !p
->m_valueBitmap
|| item
== pCb
->GetSelection() ) &&
891 ( item
>= 0 || (flags
& wxODCB_PAINTING_CONTROL
) ) &&
895 pt
.x
+= wxCC_CUSTOM_IMAGE_MARGIN1
;
896 wxRect
r(pt
.x
,pt
.y
,cis
.x
,cis
.y
);
898 if ( flags
& wxODCB_PAINTING_CONTROL
)
901 r
.height
= wxPG_STD_CUST_IMAGE_HEIGHT(m_lineHeight
);
904 paintdata
.m_drawnWidth
= r
.width
;
906 dc
.SetPen(m_colPropFore
);
907 if ( comValIndex
>= 0 )
909 const wxPGCommonValue
* cv
= GetCommonValue(comValIndex
);
910 wxPGCellRenderer
* renderer
= cv
->GetRenderer();
911 r
.width
= rect
.width
;
912 renderer
->Render( dc
, r
, this, p
, m_selColumn
, comValIndex
, renderFlags
);
915 else if ( item
>= 0 )
917 p
->OnCustomPaint( dc
, r
, paintdata
);
921 dc
.DrawRectangle( r
);
924 pt
.x
+= paintdata
.m_drawnWidth
+ wxCC_CUSTOM_IMAGE_MARGIN2
- 1;
928 // TODO: This aligns text so that it seems to be horizontally
929 // on the same line as property values. Not really
930 // sure if its needed, but seems to not cause any harm.
933 if ( item
< 0 && (flags
& wxODCB_PAINTING_CONTROL
) )
934 item
= pCb
->GetSelection();
936 if ( choices
.IsOk() && item
>= 0 && comValIndex
< 0 )
938 const wxPGChoiceEntry
& cell
= choices
.Item(item
);
939 wxPGCellRenderer
* renderer
= wxPGGlobalVars
->m_defaultRenderer
;
940 int imageOffset
= renderer
->PreDrawCell( dc
, rect
, cell
, renderFlags
);
942 imageOffset
+= wxCC_CUSTOM_IMAGE_MARGIN1
+ wxCC_CUSTOM_IMAGE_MARGIN2
;
951 pt
.y
+= (rect
.height
-m_fontHeight
)/2 - 1;
955 dc
.DrawText( text
, pt
.x
+ wxPG_XBEFORETEXT
, pt
.y
);
962 p
->OnCustomPaint( dc
, rect
, paintdata
);
963 rect
.height
= paintdata
.m_drawnHeight
+ 2;
964 rect
.width
= cis
.x
+ wxCC_CUSTOM_IMAGE_MARGIN1
+ wxCC_CUSTOM_IMAGE_MARGIN2
+ 9;
968 bool wxPGChoiceEditor_SetCustomPaintWidth( wxPropertyGrid
* propGrid
, wxPGComboBox
* cb
, int cmnVal
)
970 wxPGProperty
* property
= propGrid
->GetSelectedProperty();
971 wxASSERT( property
);
975 // Yes, a common value is being selected
976 property
->SetCommonValue( cmnVal
);
977 wxSize imageSize
= propGrid
->GetCommonValue(cmnVal
)->
978 GetRenderer()->GetImageSize(property
, 1, cmnVal
);
979 if ( imageSize
.x
) imageSize
.x
+= ODCB_CUST_PAINT_MARGIN
;
980 cb
->SetCustomPaintWidth( imageSize
.x
);
985 wxSize imageSize
= propGrid
->GetImageSize(property
, -1);
986 if ( imageSize
.x
) imageSize
.x
+= ODCB_CUST_PAINT_MARGIN
;
987 cb
->SetCustomPaintWidth( imageSize
.x
);
992 // CreateControls calls this with CB_READONLY in extraStyle
993 wxWindow
* wxPGChoiceEditor
::CreateControlsBase( wxPropertyGrid
* propGrid
,
994 wxPGProperty
* property
,
997 long extraStyle
) const
999 const wxPGChoices
& choices
= property
->GetChoices();
1001 int index
= property
->GetChoiceSelection();
1003 bool isUnspecified
= property
->IsValueUnspecified();
1005 if ( !isUnspecified
)
1006 defString
= property
->GetDisplayedString();
1008 wxArrayString labels
= choices
.GetLabels();
1014 po
.y
+= wxPG_CHOICEYADJUST
;
1015 si
.y
-= (wxPG_CHOICEYADJUST
*2);
1017 po
.x
+= wxPG_CHOICEXADJUST
;
1018 si
.x
-= wxPG_CHOICEXADJUST
;
1019 wxWindow
* ctrlParent
= propGrid
->GetPanel();
1021 int odcbFlags
= extraStyle
| wxNO_BORDER
| wxTE_PROCESS_ENTER
;
1024 // If common value specified, use appropriate index
1025 unsigned int cmnVals
= property
->GetDisplayedCommonValueCount();
1028 if ( !isUnspecified
)
1030 int cmnVal
= property
->GetCommonValue();
1033 index
= labels
.size() + cmnVal
;
1038 for ( i
=0; i
<cmnVals
; i
++ )
1039 labels
.Add(propGrid
->GetCommonValueLabel(i
));
1042 cb
= new wxPGComboBox();
1046 cb
->Create(ctrlParent
,
1054 cb
->SetButtonPosition(si
.y
,0,wxRIGHT
);
1055 cb
->SetTextIndent(wxPG_XBEFORETEXT
-1);
1057 wxPGChoiceEditor_SetCustomPaintWidth( propGrid
, cb
, property
->GetCommonValue() );
1059 if ( index
>= 0 && index
< (int)cb
->GetCount() )
1061 cb
->SetSelection( index
);
1062 if ( defString
.length() )
1063 cb
->SetText( defString
);
1065 else if ( !(extraStyle
& wxCB_READONLY
) && defString
.length() )
1066 cb
->SetValue( defString
);
1068 cb
->SetSelection( -1 );
1074 return (wxWindow
*) cb
;
1078 void wxPGChoiceEditor
::UpdateControl( wxPGProperty
* property
, wxWindow
* ctrl
) const
1081 wxOwnerDrawnComboBox
* cb
= (wxOwnerDrawnComboBox
*)ctrl
;
1082 wxASSERT( cb
->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox
)));
1083 int ind
= property
->GetChoiceSelection();
1084 cb
->SetSelection(ind
);
1087 wxPGWindowList wxPGChoiceEditor
::CreateControls( wxPropertyGrid
* propGrid
, wxPGProperty
* property
,
1088 const wxPoint
& pos
, const wxSize
& sz
) const
1090 return CreateControlsBase(propGrid
,property
,pos
,sz
,wxCB_READONLY
);
1094 int wxPGChoiceEditor
::InsertItem( wxWindow
* ctrl
, const wxString
& label
, int index
) const
1097 wxOwnerDrawnComboBox
* cb
= (wxOwnerDrawnComboBox
*)ctrl
;
1098 wxASSERT( cb
->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox
)));
1101 index
= cb
->GetCount();
1103 return cb
->Insert(label
,index
);
1107 void wxPGChoiceEditor
::DeleteItem( wxWindow
* ctrl
, int index
) const
1110 wxOwnerDrawnComboBox
* cb
= (wxOwnerDrawnComboBox
*)ctrl
;
1111 wxASSERT( cb
->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox
)));
1116 bool wxPGChoiceEditor
::OnEvent( wxPropertyGrid
* propGrid
, wxPGProperty
* property
,
1117 wxWindow
* ctrl
, wxEvent
& event
) const
1119 if ( event
.GetEventType() == wxEVT_COMMAND_COMBOBOX_SELECTED
)
1121 wxPGComboBox
* cb
= (wxPGComboBox
*)ctrl
;
1122 int index
= cb
->GetSelection();
1123 int cmnValIndex
= -1;
1124 int cmnVals
= property
->GetDisplayedCommonValueCount();
1125 int items
= cb
->GetCount();
1127 if ( index
>= (items
-cmnVals
) )
1129 // Yes, a common value is being selected
1130 cmnValIndex
= index
- (items
-cmnVals
);
1131 property
->SetCommonValue( cmnValIndex
);
1133 // Truly set value to unspecified?
1134 if ( propGrid
->GetUnspecifiedCommonValue() == cmnValIndex
)
1136 if ( !property
->IsValueUnspecified() )
1137 propGrid
->SetInternalFlag(wxPG_FL_VALUE_CHANGE_IN_EVENT
);
1138 property
->SetValueToUnspecified();
1139 if ( !cb
->HasFlag(wxCB_READONLY
) )
1140 cb
->GetTextCtrl()->SetValue(wxEmptyString
);
1144 return wxPGChoiceEditor_SetCustomPaintWidth( propGrid
, cb
, cmnValIndex
);
1150 bool wxPGChoiceEditor
::GetValueFromControl( wxVariant
& variant
, wxPGProperty
* property
, wxWindow
* ctrl
) const
1152 wxOwnerDrawnComboBox
* cb
= (wxOwnerDrawnComboBox
*)ctrl
;
1154 int index
= cb
->GetSelection();
1156 if ( index
!= property
->GetChoiceSelection() ||
1157 // Changing unspecified always causes event (returning
1158 // true here should be enough to trigger it).
1159 property
->IsValueUnspecified()
1162 return property
->IntToValue( variant
, index
, 0 );
1168 void wxPGChoiceEditor
::SetControlStringValue( wxPGProperty
* WXUNUSED(property
), wxWindow
* ctrl
, const wxString
& txt
) const
1170 wxOwnerDrawnComboBox
* cb
= (wxOwnerDrawnComboBox
*)ctrl
;
1176 void wxPGChoiceEditor
::SetControlIntValue( wxPGProperty
* WXUNUSED(property
), wxWindow
* ctrl
, int value
) const
1178 wxOwnerDrawnComboBox
* cb
= (wxOwnerDrawnComboBox
*)ctrl
;
1180 cb
->SetSelection(value
);
1184 void wxPGChoiceEditor
::SetValueToUnspecified( wxPGProperty
* WXUNUSED(property
), wxWindow
* ctrl
) const
1186 wxOwnerDrawnComboBox
* cb
= (wxOwnerDrawnComboBox
*)ctrl
;
1187 cb
->SetSelection(-1);
1191 bool wxPGChoiceEditor
::CanContainCustomImage() const
1197 wxPGChoiceEditor
::~wxPGChoiceEditor() { }
1200 // -----------------------------------------------------------------------
1201 // wxPGComboBoxEditor
1202 // -----------------------------------------------------------------------
1205 WX_PG_IMPLEMENT_EDITOR_CLASS(ComboBox
,wxPGComboBoxEditor
,wxPGChoiceEditor
)
1208 void wxPGComboBoxEditor
::UpdateControl( wxPGProperty
* property
, wxWindow
* ctrl
) const
1210 wxOwnerDrawnComboBox
* cb
= (wxOwnerDrawnComboBox
*)ctrl
;
1211 cb
->SetValue(property
->GetValueString(wxPG_EDITABLE_VALUE
));
1213 // TODO: If string matches any selection, then select that.
1217 wxPGWindowList wxPGComboBoxEditor
::CreateControls( wxPropertyGrid
* propGrid
,
1218 wxPGProperty
* property
,
1220 const wxSize
& sz
) const
1222 return CreateControlsBase(propGrid
,property
,pos
,sz
,0);
1226 bool wxPGComboBoxEditor
::OnEvent( wxPropertyGrid
* propGrid
,
1227 wxPGProperty
* property
,
1229 wxEvent
& event
) const
1231 wxOwnerDrawnComboBox
* cb
= (wxOwnerDrawnComboBox
*) NULL
;
1232 wxWindow
* textCtrl
= (wxWindow
*) NULL
;
1236 cb
= (wxOwnerDrawnComboBox
*)ctrl
;
1237 textCtrl
= cb
->GetTextCtrl();
1240 if ( wxPGTextCtrlEditor
::OnTextCtrlEvent(propGrid
,property
,textCtrl
,event
) )
1243 return wxPGChoiceEditor
::OnEvent(propGrid
,property
,ctrl
,event
);
1247 bool wxPGComboBoxEditor
::GetValueFromControl( wxVariant
& variant
, wxPGProperty
* property
, wxWindow
* ctrl
) const
1249 wxOwnerDrawnComboBox
* cb
= (wxOwnerDrawnComboBox
*)ctrl
;
1250 wxString textVal
= cb
->GetValue();
1252 if ( property
->UsesAutoUnspecified() && !textVal
.length() )
1258 bool res
= property
->StringToValue(variant
, textVal
, wxPG_EDITABLE_VALUE
);
1260 // Changing unspecified always causes event (returning
1261 // true here should be enough to trigger it).
1262 if ( !res
&& variant
.IsNull() )
1269 void wxPGComboBoxEditor
::OnFocus( wxPGProperty
*, wxWindow
* ctrl
) const
1271 wxOwnerDrawnComboBox
* cb
= (wxOwnerDrawnComboBox
*)ctrl
;
1272 cb
->GetTextCtrl()->SetSelection(-1,-1);
1276 wxPGComboBoxEditor
::~wxPGComboBoxEditor() { }
1279 // -----------------------------------------------------------------------
1280 // wxPGChoiceAndButtonEditor
1281 // -----------------------------------------------------------------------
1284 // This simpler implement_editor macro doesn't define class body.
1285 WX_PG_IMPLEMENT_EDITOR_CLASS(ChoiceAndButton
,wxPGChoiceAndButtonEditor
,wxPGChoiceEditor
)
1288 wxPGWindowList wxPGChoiceAndButtonEditor
::CreateControls( wxPropertyGrid
* propGrid
,
1289 wxPGProperty
* property
,
1291 const wxSize
& sz
) const
1293 // Use one two units smaller to match size of the combo's dropbutton.
1294 // (normally a bigger button is used because it looks better)
1297 wxSize
bt_sz(bt_wid
,bt_wid
);
1299 // Position of button.
1300 wxPoint
bt_pos(pos
.x
+sz
.x
-bt_sz
.x
,pos
.y
);
1307 wxWindow
* bt
= propGrid
->GenerateEditorButton( bt_pos
, bt_sz
);
1310 wxSize
ch_sz(sz
.x
-bt
->GetSize().x
,sz
.y
);
1313 ch_sz
.x
-= wxPG_TEXTCTRL_AND_BUTTON_SPACING
;
1316 wxWindow
* ch
= wxPG_EDITOR(Choice
)->CreateControls(propGrid
,property
,
1317 pos
,ch_sz
).m_primary
;
1323 return wxPGWindowList(ch
, bt
);
1327 wxPGChoiceAndButtonEditor
::~wxPGChoiceAndButtonEditor() { }
1330 // -----------------------------------------------------------------------
1331 // wxPGTextCtrlAndButtonEditor
1332 // -----------------------------------------------------------------------
1335 // This simpler implement_editor macro doesn't define class body.
1336 WX_PG_IMPLEMENT_EDITOR_CLASS(TextCtrlAndButton
,wxPGTextCtrlAndButtonEditor
,wxPGTextCtrlEditor
)
1339 wxPGWindowList wxPGTextCtrlAndButtonEditor
::CreateControls( wxPropertyGrid
* propGrid
,
1340 wxPGProperty
* property
,
1342 const wxSize
& sz
) const
1345 wxWindow
* wnd
= propGrid
->GenerateEditorTextCtrlAndButton( pos
, sz
, &wnd2
,
1346 property
->GetFlags() & wxPG_PROP_NOEDITOR
, property
);
1348 return wxPGWindowList(wnd
, wnd2
);
1352 wxPGTextCtrlAndButtonEditor
::~wxPGTextCtrlAndButtonEditor() { }
1355 // -----------------------------------------------------------------------
1356 // wxPGCheckBoxEditor
1357 // -----------------------------------------------------------------------
1359 #if wxPG_INCLUDE_CHECKBOX
1361 WX_PG_IMPLEMENT_EDITOR_CLASS(CheckBox
,wxPGCheckBoxEditor
,wxPGEditor
)
1364 // state argument: 0x01 = set if checked
1365 // 0x02 = set if rectangle should be bold
1366 static void DrawSimpleCheckBox( wxDC
& dc
, const wxRect
& rect
, int box_hei
, int state
, const wxColour
& linecol
)
1370 wxRect
r(rect
.x
+wxPG_XBEFORETEXT
,rect
.y
+((rect
.height
-box_hei
)/2),box_hei
,box_hei
);
1372 // Draw check mark first because it is likely to overdraw the
1373 // surrounding rectangle.
1376 wxRect
r2(r
.x
+wxPG_CHECKMARK_XADJ
,
1377 r
.y
+wxPG_CHECKMARK_YADJ
,
1378 r
.width
+wxPG_CHECKMARK_WADJ
,
1379 r
.height
+wxPG_CHECKMARK_HADJ
);
1380 #if wxPG_CHECKMARK_DEFLATE
1381 r2
.Deflate(wxPG_CHECKMARK_DEFLATE
);
1383 dc
.DrawCheckMark(r2
);
1385 // This would draw a simple cross check mark.
1386 // dc.DrawLine(r.x,r.y,r.x+r.width-1,r.y+r.height-1);
1387 // dc.DrawLine(r.x,r.y+r.height-1,r.x+r.width-1,r.y);
1393 // Pen for thin rectangle.
1398 // Pen for bold rectangle.
1399 wxPen
linepen(linecol
,2,wxSOLID
);
1400 linepen
.SetJoin(wxJOIN_MITER
); // This prevents round edges.
1408 dc
.SetBrush(*wxTRANSPARENT_BRUSH
);
1410 dc
.DrawRectangle(r
);
1411 dc
.SetPen(*wxTRANSPARENT_PEN
);
1415 // Real simple custom-drawn checkbox-without-label class.
1417 class wxSimpleCheckBox
: public wxControl
1421 void SetValue( int value
);
1423 wxSimpleCheckBox( wxWindow
* parent
,
1425 const wxPoint
& pos
= wxDefaultPosition
,
1426 const wxSize
& size
= wxDefaultSize
)
1427 : wxControl(parent
,id
,pos
,size
,wxNO_BORDER
|wxWANTS_CHARS
)
1429 // Due to SetOwnFont stuff necessary for GTK+ 1.2, we need to have this
1430 SetFont( parent
->GetFont() );
1433 wxPropertyGrid
* pg
= (wxPropertyGrid
*) parent
->GetParent();
1434 wxASSERT( pg
->IsKindOf(CLASSINFO(wxPropertyGrid
)) );
1435 m_boxHeight
= pg
->GetFontHeight();
1436 SetBackgroundStyle( wxBG_STYLE_COLOUR
);
1439 virtual ~wxSimpleCheckBox();
1441 virtual bool ProcessEvent(wxEvent
& event
);
1446 static wxBitmap
* ms_doubleBuffer
;
1450 wxSimpleCheckBox
::~wxSimpleCheckBox()
1452 delete ms_doubleBuffer
;
1453 ms_doubleBuffer
= NULL
;
1457 wxBitmap
* wxSimpleCheckBox
::ms_doubleBuffer
= (wxBitmap
*) NULL
;
1459 // value = 2 means toggle (sorry, too lazy to do constants)
1460 void wxSimpleCheckBox
::SetValue( int value
)
1465 if ( m_state
> 1 ) m_state
= 0;
1473 wxCommandEvent
evt(wxEVT_COMMAND_CHECKBOX_CLICKED
,GetParent()->GetId());
1475 wxPropertyGrid
* propGrid
= (wxPropertyGrid
*) GetParent()->GetParent();
1476 wxASSERT( propGrid
->IsKindOf(CLASSINFO(wxPropertyGrid
)) );
1477 propGrid
->OnCustomEditorEvent(evt
);
1481 bool wxSimpleCheckBox
::ProcessEvent(wxEvent
& event
)
1483 wxPropertyGrid
* propGrid
= (wxPropertyGrid
*) GetParent()->GetParent();
1484 wxASSERT( propGrid
->IsKindOf(CLASSINFO(wxPropertyGrid
)) );
1486 if ( event
.GetEventType() == wxEVT_NAVIGATION_KEY
)
1488 //wxLogDebug(wxT("wxEVT_NAVIGATION_KEY"));
1489 //SetFocusFromKbd();
1491 //return wxControl::ProcessEvent(event);
1494 if ( ( (event
.GetEventType() == wxEVT_LEFT_DOWN
|| event
.GetEventType() == wxEVT_LEFT_DCLICK
)
1495 && ((wxMouseEvent
&)event
).m_x
> (wxPG_XBEFORETEXT
-2)
1496 && ((wxMouseEvent
&)event
).m_x
<= (wxPG_XBEFORETEXT
-2+m_boxHeight
) )
1502 else if ( event
.GetEventType() == wxEVT_PAINT
)
1504 wxSize clientSize
= GetClientSize();
1508 // Buffered paint DC doesn't seem to do much good
1509 if ( !ms_doubleBuffer ||
1510 clientSize.x > ms_doubleBuffer->GetWidth() ||
1511 clientSize.y > ms_doubleBuffer->GetHeight() )
1513 delete ms_doubleBuffer;
1514 ms_doubleBuffer = new wxBitmap(clientSize.x+25,clientSize.y+25);
1517 wxBufferedPaintDC dc(this,*ms_doubleBuffer);
1520 wxRect
rect(0,0,clientSize
.x
,clientSize
.y
);
1525 m_boxHeight
= propGrid
->GetFontHeight();
1527 wxColour bgcol
= GetBackgroundColour();
1528 dc
.SetBrush( bgcol
);
1530 dc
.DrawRectangle( rect
);
1532 wxColour txcol
= GetForegroundColour();
1534 int state
= m_state
;
1535 if ( m_font
.GetWeight() == wxBOLD
)
1538 DrawSimpleCheckBox(dc
,rect
,m_boxHeight
,state
,txcol
);
1540 // If focused, indicate it somehow.
1542 if ( wxWindow::FindFocus() == this )
1547 wxPGDrawFocusRect(dc,rect);
1553 else if ( event
.GetEventType() == wxEVT_SIZE
||
1554 event
.GetEventType() == wxEVT_SET_FOCUS
||
1555 event
.GetEventType() == wxEVT_KILL_FOCUS
1560 else if ( event
.GetEventType() == wxEVT_KEY_DOWN
)
1562 wxKeyEvent
& keyEv
= (wxKeyEvent
&) event
;
1564 if ( keyEv
.GetKeyCode() == WXK_TAB
)
1566 propGrid
->SendNavigationKeyEvent( keyEv
.ShiftDown()?
0:1 );
1570 if ( keyEv
.GetKeyCode() == WXK_SPACE
)
1576 return wxControl
::ProcessEvent(event
);
1580 wxPGWindowList wxPGCheckBoxEditor
::CreateControls( wxPropertyGrid
* propGrid
,
1581 wxPGProperty
* property
,
1583 const wxSize
& size
) const
1586 pt
.x
-= wxPG_XBEFOREWIDGET
;
1588 sz
.x
= propGrid
->GetFontHeight() + (wxPG_XBEFOREWIDGET
*2) + 4;
1590 wxSimpleCheckBox
* cb
= new wxSimpleCheckBox(propGrid
->GetPanel(),wxPG_SUBID1
,pt
,sz
);
1592 cb
->SetBackgroundColour(wxSystemSettings
::GetColour(wxSYS_COLOUR_WINDOW
));
1594 cb
->Connect( wxPG_SUBID1
, wxEVT_LEFT_DOWN
,
1595 (wxObjectEventFunction
) (wxEventFunction
) (wxCommandEventFunction
)
1596 &wxPropertyGrid
::OnCustomEditorEvent
, NULL
, propGrid
);
1598 cb
->Connect( wxPG_SUBID1
, wxEVT_LEFT_DCLICK
,
1599 (wxObjectEventFunction
) (wxEventFunction
) (wxCommandEventFunction
)
1600 &wxPropertyGrid
::OnCustomEditorEvent
, NULL
, propGrid
);
1602 if ( property
->GetChoiceSelection() > 0 &&
1603 !property
->IsValueUnspecified() )
1606 // If mouse cursor was on the item, toggle the value now.
1607 if ( propGrid
->GetInternalFlags() & wxPG_FL_ACTIVATION_BY_CLICK
)
1609 wxPoint pt
= cb
->ScreenToClient(::wxGetMousePosition());
1610 if ( pt
.x
<= (wxPG_XBEFORETEXT
-2+cb
->m_boxHeight
) )
1614 if ( cb
->m_state
> 1 )
1617 // Makes sure wxPG_EVT_CHANGING etc. is sent for this initial click
1618 propGrid
->ChangePropertyValue(property
, wxPGVariant_Bool(cb
->m_state
));
1622 propGrid
->SetInternalFlag( wxPG_FL_FIXED_WIDTH_EDITOR
);
1628 class wxPGCheckBoxRenderer : public wxPGDefaultRenderer
1632 virtual void Render( wxDC& dc, const wxRect& rect,
1633 const wxPropertyGrid* WXUNUSED(propertyGrid), wxPGProperty* property,
1634 int WXUNUSED(column), int WXUNUSED(item), int WXUNUSED(flags) ) const
1637 if ( !(property->GetFlags() & wxPG_PROP_UNSPECIFIED) )
1639 state = ((wxPGProperty*)property)->GetChoiceInfo((wxPGChoiceInfo*)NULL);
1640 if ( dc.GetFont().GetWeight() == wxBOLD ) state |= 2;
1642 DrawSimpleCheckBox(dc,rect,dc.GetCharHeight(),state,dc.GetTextForeground());
1648 wxPGCheckBoxRenderer g_wxPGCheckBoxRenderer;
1650 wxPGCellRenderer* wxPGCheckBoxEditor::GetCellRenderer() const
1652 return &g_wxPGCheckBoxRenderer;
1656 void wxPGCheckBoxEditor
::DrawValue( wxDC
& dc
, const wxRect
& rect
, wxPGProperty
* property
, const wxString
& WXUNUSED(text
) ) const
1659 if ( !property
->IsValueUnspecified() )
1661 state
= property
->GetChoiceSelection();
1662 if ( dc
.GetFont().GetWeight() == wxBOLD
) state
|= 2;
1664 DrawSimpleCheckBox(dc
,rect
,dc
.GetCharHeight(),state
,dc
.GetTextForeground());
1667 void wxPGCheckBoxEditor
::UpdateControl( wxPGProperty
* property
, wxWindow
* ctrl
) const
1670 ((wxSimpleCheckBox
*)ctrl
)->m_state
= property
->GetChoiceSelection();
1675 bool wxPGCheckBoxEditor
::OnEvent( wxPropertyGrid
* WXUNUSED(propGrid
), wxPGProperty
* WXUNUSED(property
),
1676 wxWindow
* WXUNUSED(ctrl
), wxEvent
& event
) const
1678 if ( event
.GetEventType() == wxEVT_COMMAND_CHECKBOX_CLICKED
)
1686 bool wxPGCheckBoxEditor
::GetValueFromControl( wxVariant
& variant
, wxPGProperty
* property
, wxWindow
* ctrl
) const
1688 wxSimpleCheckBox
* cb
= (wxSimpleCheckBox
*)ctrl
;
1690 int index
= cb
->m_state
;
1692 if ( index
!= property
->GetChoiceSelection() ||
1693 // Changing unspecified always causes event (returning
1694 // true here should be enough to trigger it).
1695 property
->IsValueUnspecified()
1698 return property
->IntToValue(variant
, index
, 0);
1704 void wxPGCheckBoxEditor
::SetControlIntValue( wxPGProperty
* WXUNUSED(property
), wxWindow
* ctrl
, int value
) const
1706 if ( value
!= 0 ) value
= 1;
1707 ((wxSimpleCheckBox
*)ctrl
)->m_state
= value
;
1712 void wxPGCheckBoxEditor
::SetValueToUnspecified( wxPGProperty
* WXUNUSED(property
), wxWindow
* ctrl
) const
1714 ((wxSimpleCheckBox
*)ctrl
)->m_state
= 0;
1719 wxPGCheckBoxEditor
::~wxPGCheckBoxEditor() { }
1722 #endif // wxPG_INCLUDE_CHECKBOX
1724 // -----------------------------------------------------------------------
1726 wxWindow
* wxPropertyGrid
::GetEditorControl() const
1728 wxWindow
* ctrl
= m_wndEditor
;
1733 // If it's clipper window, return its child instead
1734 #if wxPG_ENABLE_CLIPPER_WINDOW
1735 if ( ctrl
->IsKindOf(CLASSINFO(wxPGClipperWindow
)) )
1737 return ((wxPGClipperWindow
*)ctrl
)->GetControl();
1744 // -----------------------------------------------------------------------
1746 void wxPropertyGrid
::CorrectEditorWidgetSizeX()
1748 if ( m_selColumn
== -1 )
1752 int newSplitterx
= m_pState
->DoGetSplitterPosition(m_selColumn
-1);
1753 int newWidth
= newSplitterx
+ m_pState
->m_colWidths
[m_selColumn
];
1757 // if width change occurred, move secondary wnd by that amount
1758 wxRect r
= m_wndEditor2
->GetRect();
1760 r
.x
= newWidth
- secWid
;
1762 m_wndEditor2
->SetSize( r
);
1764 // if primary is textctrl, then we have to add some extra space
1768 if ( m_wndEditor
&& m_wndEditor
->IsKindOf(CLASSINFO(wxTextCtrl
)) )
1770 secWid
+= wxPG_TEXTCTRL_AND_BUTTON_SPACING
;
1775 wxRect r
= m_wndEditor
->GetRect();
1777 r
.x
= newSplitterx
+m_ctrlXAdjust
;
1779 if ( !(m_iFlags
& wxPG_FL_FIXED_WIDTH_EDITOR
) )
1780 r
.width
= newWidth
- r
.x
- secWid
;
1782 m_wndEditor
->SetSize(r
);
1786 m_wndEditor2
->Refresh();
1789 // -----------------------------------------------------------------------
1791 void wxPropertyGrid
::CorrectEditorWidgetPosY()
1793 if ( m_selected
&& (m_wndEditor
|| m_wndEditor2
) )
1795 wxRect r
= GetEditorWidgetRect(m_selected
, m_selColumn
);
1799 wxPoint pos
= m_wndEditor
->GetPosition();
1801 // Calculate y offset
1802 int offset
= pos
.y
% m_lineHeight
;
1804 m_wndEditor
->Move(pos
.x
, r
.y
+ offset
);
1809 wxPoint pos
= m_wndEditor2
->GetPosition();
1811 m_wndEditor2
->Move(pos
.x
, r
.y
);
1816 // -----------------------------------------------------------------------
1818 bool wxPropertyGrid
::AdjustPosForClipperWindow( wxWindow
* topCtrlWnd
, int* x
, int* y
)
1820 #if wxPG_ENABLE_CLIPPER_WINDOW
1821 // Take clipper window into account
1822 if (topCtrlWnd
->GetPosition().x
< 1 &&
1823 !topCtrlWnd
->IsKindOf(CLASSINFO(wxPGClipperWindow
)))
1825 topCtrlWnd
= topCtrlWnd
->GetParent();
1826 wxASSERT( topCtrlWnd
->IsKindOf(CLASSINFO(wxPGClipperWindow
)) );
1827 *x
-= ((wxPGClipperWindow
*)topCtrlWnd
)->GetXClip();
1828 *y
-= ((wxPGClipperWindow
*)topCtrlWnd
)->GetYClip();
1832 wxUnusedVar(topCtrlWnd
);
1839 // -----------------------------------------------------------------------
1841 // Fixes position of wxTextCtrl-like control (wxSpinCtrl usually
1842 // fits into that category as well).
1843 void wxPropertyGrid
::FixPosForTextCtrl( wxWindow
* ctrl
, const wxPoint
& offset
)
1845 // Center the control vertically
1846 wxRect finalPos
= ctrl
->GetRect();
1847 int y_adj
= (m_lineHeight
- finalPos
.height
)/2 + wxPG_TEXTCTRLYADJUST
;
1849 // Prevent over-sized control
1850 int sz_dec
= (y_adj
+ finalPos
.height
) - m_lineHeight
;
1851 if ( sz_dec
< 0 ) sz_dec
= 0;
1853 finalPos
.y
+= y_adj
;
1854 finalPos
.height
-= (y_adj
+sz_dec
);
1856 const int textCtrlXAdjust
= wxPG_TEXTCTRLXADJUST
;
1858 finalPos
.x
+= textCtrlXAdjust
;
1859 finalPos
.width
-= textCtrlXAdjust
;
1861 finalPos
.x
+= offset
.x
;
1862 finalPos
.y
+= offset
.y
;
1864 ctrl
->SetSize(finalPos
);
1867 // -----------------------------------------------------------------------
1869 wxWindow
* wxPropertyGrid
::GenerateEditorTextCtrl( const wxPoint
& pos
,
1871 const wxString
& value
,
1872 wxWindow
* secondary
,
1876 wxPGProperty
* selected
= m_selected
;
1879 int tcFlags
= wxTE_PROCESS_ENTER
| extraStyle
;
1881 if ( selected
->HasFlag(wxPG_PROP_READONLY
) )
1882 tcFlags
|= wxTE_READONLY
;
1884 wxPoint
p(pos
.x
,pos
.y
);
1885 wxSize
s(sz
.x
,sz
.y
);
1887 // Need to reduce width of text control on Mac
1888 #if defined(__WXMAC__)
1892 // Take button into acccount
1895 s
.x
-= (secondary
->GetSize().x
+ wxPG_TEXTCTRL_AND_BUTTON_SPACING
);
1896 m_iFlags
&= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE
);
1899 // If the height is significantly higher, then use border, and fill the rect exactly.
1900 bool hasSpecialSize
= false;
1902 if ( (sz
.y
- m_lineHeight
) > 5 )
1903 hasSpecialSize
= true;
1905 #if wxPG_NAT_TEXTCTRL_BORDER_ANY
1907 // Create clipper window
1908 wxPGClipperWindow
* wnd
= new wxPGClipperWindow();
1909 #if defined(__WXMSW__)
1912 wnd
->Create(GetPanel(),wxPG_SUBID1
,p
,s
);
1914 // This generates rect of the control inside the clipper window
1915 if ( !hasSpecialSize
)
1916 wnd
->GetControlRect(wxPG_NAT_TEXTCTRL_BORDER_X
, wxPG_NAT_TEXTCTRL_BORDER_Y
, p
, s
);
1918 wnd
->GetControlRect(0, 0, p
, s
);
1920 wxWindow
* ctrlParent
= wnd
;
1924 wxWindow
* ctrlParent
= GetPanel();
1926 if ( !hasSpecialSize
)
1927 tcFlags
|= wxNO_BORDER
;
1931 wxTextCtrl
* tc
= new wxTextCtrl();
1933 #if defined(__WXMSW__) && !wxPG_NAT_TEXTCTRL_BORDER_ANY
1936 SetupTextCtrlValue(value
);
1937 tc
->Create(ctrlParent
,wxPG_SUBID1
,value
, p
, s
,tcFlags
);
1939 #if wxPG_NAT_TEXTCTRL_BORDER_ANY
1941 wnd
->SetControl(tc
);
1946 // Center the control vertically
1947 if ( !hasSpecialSize
)
1948 FixPosForTextCtrl(ed
);
1956 // Set maximum length
1958 tc
->SetMaxLength( maxLen
);
1960 return (wxWindow
*) ed
;
1963 // -----------------------------------------------------------------------
1965 wxWindow
* wxPropertyGrid
::GenerateEditorButton( const wxPoint
& pos
, const wxSize
& sz
)
1967 wxPGProperty
* selected
= m_selected
;
1971 // Decorations are chunky on Mac, and we can't make the button square, so
1972 // do things a bit differently on this platform.
1974 wxPoint
p(pos
.x
+sz
.x
,
1975 pos
.y
+wxPG_BUTTON_SIZEDEC
-wxPG_NAT_BUTTON_BORDER_Y
);
1978 wxButton
* but
= new wxButton();
1979 but
->Create(GetPanel(),wxPG_SUBID2
,wxS("..."),p
,s
,wxWANTS_CHARS
);
1981 // Now that we know the size, move to the correct position
1982 p
.x
= pos
.x
+ sz
.x
- but
->GetSize().x
- 2;
1986 wxSize
s(sz
.y
-(wxPG_BUTTON_SIZEDEC
*2)+(wxPG_NAT_BUTTON_BORDER_Y
*2),
1987 sz
.y
-(wxPG_BUTTON_SIZEDEC
*2)+(wxPG_NAT_BUTTON_BORDER_Y
*2));
1989 // Reduce button width to lineheight
1990 if ( s
.x
> m_lineHeight
)
1994 // On wxGTK, take fixed button margins into account
1999 wxPoint
p(pos
.x
+sz
.x
-s
.x
,
2000 pos
.y
+wxPG_BUTTON_SIZEDEC
-wxPG_NAT_BUTTON_BORDER_Y
);
2002 wxButton
* but
= new wxButton();
2006 but
->Create(GetPanel(),wxPG_SUBID2
,wxS("..."),p
,s
,wxWANTS_CHARS
);
2009 wxFont font
= GetFont();
2010 font
.SetPointSize(font
.GetPointSize()-2);
2013 but
->SetFont(GetFont());
2017 if ( selected
->HasFlag(wxPG_PROP_READONLY
) )
2023 // -----------------------------------------------------------------------
2025 wxWindow
* wxPropertyGrid
::GenerateEditorTextCtrlAndButton( const wxPoint
& pos
,
2027 wxWindow
** psecondary
,
2029 wxPGProperty
* property
)
2031 wxButton
* but
= (wxButton
*)GenerateEditorButton(pos
,sz
);
2032 *psecondary
= (wxWindow
*)but
;
2034 if ( limitedEditing
)
2037 // There is button Show in GenerateEditorTextCtrl as well
2040 return (wxWindow
*) NULL
;
2045 if ( !property
->IsValueUnspecified() )
2046 text
= property
->GetValueString(property
->HasFlag(wxPG_PROP_READONLY
)?
0:wxPG_EDITABLE_VALUE
);
2048 return GenerateEditorTextCtrl(pos
,sz
,text
,but
,property
->m_maxLen
);
2051 // -----------------------------------------------------------------------
2053 wxTextCtrl
* wxPropertyGrid
::GetEditorTextCtrl() const
2055 wxWindow
* wnd
= GetEditorControl();
2060 if ( wnd
->IsKindOf(CLASSINFO(wxTextCtrl
)) )
2061 return wxStaticCast(wnd
, wxTextCtrl
);
2063 if ( wnd
->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox
)) )
2065 wxOwnerDrawnComboBox
* cb
= wxStaticCast(wnd
, wxOwnerDrawnComboBox
);
2066 return cb
->GetTextCtrl();
2072 // -----------------------------------------------------------------------
2074 wxPGEditor
* wxPropertyGridInterface
::GetEditorByName( const wxString
& editorName
)
2076 wxPGHashMapS2P
::const_iterator it
;
2078 it
= wxPGGlobalVars
->m_mapEditorClasses
.find(editorName
);
2079 if ( it
== wxPGGlobalVars
->m_mapEditorClasses
.end() )
2081 return (wxPGEditor
*) it
->second
;
2084 // -----------------------------------------------------------------------
2085 // wxPGEditorDialogAdapter
2086 // -----------------------------------------------------------------------
2088 IMPLEMENT_ABSTRACT_CLASS(wxPGEditorDialogAdapter
, wxObject
)
2090 bool wxPGEditorDialogAdapter
::ShowDialog( wxPropertyGrid
* propGrid
, wxPGProperty
* property
)
2092 if ( !propGrid
->EditorValidate() )
2095 bool res
= DoShowDialog( propGrid
, property
);
2099 propGrid
->ValueChangeInEvent( m_value
);
2106 // -----------------------------------------------------------------------
2108 // -----------------------------------------------------------------------
2110 wxPGMultiButton
::wxPGMultiButton( wxPropertyGrid
* pg
, const wxSize
& sz
)
2111 : wxWindow( pg
->GetPanel(), wxPG_SUBID2
, wxPoint(-100,-100), wxSize(0, sz
.y
) ),
2112 m_fullEditorSize(sz
), m_buttonsWidth(0)
2114 SetBackgroundColour(pg
->GetCellBackgroundColour());
2117 int wxPGMultiButton
::GenId( int id
) const
2121 if ( m_buttons
.size() )
2122 id
= GetButton(m_buttons
.size()-1)->GetId() + 1;
2130 void wxPGMultiButton
::Add( const wxBitmap
& bitmap
, int id
)
2133 wxSize sz
= GetSize();
2134 wxButton
* button
= new wxBitmapButton( this, id
, bitmap
, wxPoint(sz
.x
, 0), wxSize(sz
.y
, sz
.y
) );
2135 m_buttons
.push_back(button
);
2136 int bw
= button
->GetSize().x
;
2137 SetSize(wxSize(sz
.x
+bw
,sz
.y
));
2138 m_buttonsWidth
+= bw
;
2142 void wxPGMultiButton
::Add( const wxString
& label
, int id
)
2145 wxSize sz
= GetSize();
2146 wxButton
* button
= new wxButton( this, id
, label
, wxPoint(sz
.x
, 0), wxSize(sz
.y
, sz
.y
) );
2147 m_buttons
.push_back(button
);
2148 int bw
= button
->GetSize().x
;
2149 SetSize(wxSize(sz
.x
+bw
,sz
.y
));
2150 m_buttonsWidth
+= bw
;
2153 // -----------------------------------------------------------------------
2155 #endif // wxUSE_PROPGRID