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