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