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