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