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