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