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