]> git.saurik.com Git - wxWidgets.git/blame - src/propgrid/editors.cpp
remove also wxT(); it's confusing, too; do load wxstd.mo catalog after calling wxLoca...
[wxWidgets.git] / src / propgrid / editors.cpp
CommitLineData
1c4293cb
VZ
1/////////////////////////////////////////////////////////////////////////////
2// Name: src/propgrid/editors.cpp
3// Purpose: wxPropertyGrid editors
4// Author: Jaakko Salli
5// Modified by:
6// Created: 2007-04-14
ea5af9c5 7// RCS-ID: $Id$
1c4293cb
VZ
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
f4bc1aa2
JS
19#if wxUSE_PROPGRID
20
1c4293cb
VZ
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"
1c4293cb
VZ
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
3b211af1
SC
61#include "wx/propgrid/propgrid.h"
62#include "wx/propgrid/editors.h"
63#include "wx/propgrid/props.h"
1c4293cb
VZ
64
65#if wxPG_USE_RENDERER_NATIVE
3b211af1 66 #include "wx/renderer.h"
1c4293cb
VZ
67#endif
68
69// How many pixels between textctrl and button
70#ifdef __WXMAC__
13ab7561 71 #define wxPG_TEXTCTRL_AND_BUTTON_SPACING 4
1c4293cb
VZ
72#else
73 #define wxPG_TEXTCTRL_AND_BUTTON_SPACING 2
74#endif
75
76#define wxPG_BUTTON_SIZEDEC 0
77
3b211af1 78#include "wx/odcombo.h"
1c4293cb
VZ
79
80#ifdef __WXMSW__
3b211af1 81 #include "wx/msw/private.h"
1c4293cb
VZ
82#endif
83
84// -----------------------------------------------------------------------
85
86#if defined(__WXMSW__)
87 // tested
1c4293cb
VZ
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
1c4293cb
VZ
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
1c4293cb
VZ
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
012b4a07 126 #define wxPG_TEXTCTRLYADJUST 0
1c4293cb
VZ
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
1c4293cb
VZ
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
1c4293cb 144// for odcombo
53ba104e 145#ifdef __WXMAC__
13ab7561
RR
146#define wxPG_CHOICEXADJUST -3 // required because wxComboCtrl reserves 3pixels for wxTextCtrl's focus ring
147#define wxPG_CHOICEYADJUST -3
53ba104e 148#else
1c4293cb
VZ
149#define wxPG_CHOICEXADJUST 0
150#define wxPG_CHOICEYADJUST 0
53ba104e 151#endif
1c4293cb
VZ
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
163IMPLEMENT_ABSTRACT_CLASS(wxPGEditor, wxObject)
164
165
166wxPGEditor::~wxPGEditor()
167{
168}
169
5a45dd6f
JS
170wxString wxPGEditor::GetName() const
171{
172 return GetClassInfo()->GetClassName();
173}
174
1c4293cb
VZ
175void 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
181bool wxPGEditor::GetValueFromControl( wxVariant&, wxPGProperty*, wxWindow* ) const
182{
183 return false;
184}
185
186void wxPGEditor::SetControlStringValue( wxPGProperty* WXUNUSED(property), wxWindow*, const wxString& ) const
187{
188}
189
190
191void wxPGEditor::SetControlIntValue( wxPGProperty* WXUNUSED(property), wxWindow*, int ) const
192{
193}
194
195
196int wxPGEditor::InsertItem( wxWindow*, const wxString&, int ) const
197{
198 return -1;
199}
200
201
202void wxPGEditor::DeleteItem( wxWindow*, int ) const
203{
204 return;
205}
206
207
208void wxPGEditor::OnFocus( wxPGProperty*, wxWindow* ) const
209{
210}
211
212
213bool wxPGEditor::CanContainCustomImage() const
214{
215 return false;
216}
217
1c4293cb
VZ
218// -----------------------------------------------------------------------
219// wxPGTextCtrlEditor
220// -----------------------------------------------------------------------
221
52cefafe 222WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(TextCtrl,wxPGTextCtrlEditor,wxPGEditor)
1c4293cb
VZ
223
224
225wxPGWindowList wxPGTextCtrlEditor::CreateControls( wxPropertyGrid* propGrid,
226 wxPGProperty* property,
227 const wxPoint& pos,
228 const wxSize& sz ) const
229{
230 wxString text;
231
232 //
233 // If has children, and limited editing is specified, then don't create.
234 if ( (property->GetFlags() & wxPG_PROP_NOEDITOR) &&
235 property->GetChildCount() )
236 return (wxWindow*) NULL;
237
238 if ( !property->IsValueUnspecified() )
1425eca5
JS
239 text = property->GetValueAsString(property->HasFlag(wxPG_PROP_READONLY) ?
240 0 : wxPG_EDITABLE_VALUE);
1c4293cb
VZ
241
242 int flags = 0;
243 if ( (property->GetFlags() & wxPG_PROP_PASSWORD) &&
244 property->IsKindOf(CLASSINFO(wxStringProperty)) )
245 flags |= wxTE_PASSWORD;
246
247 wxWindow* wnd = propGrid->GenerateEditorTextCtrl(pos,sz,text,(wxWindow*)NULL,flags,
248 property->GetMaxLength());
249
250 return wnd;
251}
252
253#if 0
254void 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
274void wxPGTextCtrlEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
275{
d27f4c7b
RR
276 wxTextCtrl* tc = wxDynamicCast(ctrl, wxTextCtrl);
277 if (!tc) return;
1c4293cb
VZ
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
f521bae6
JS
286 tc->SetValue(s);
287
a6353fe8
JS
288 //
289 // Fix indentation, just in case (change in font boldness is one good
290 // reason).
f521bae6 291#if defined(__WXMSW__) && !defined(__WXWINCE__)
a6353fe8
JS
292 ::SendMessage(GetHwndOf(tc),
293 EM_SETMARGINS,
294 EC_LEFTMARGIN | EC_RIGHTMARGIN,
295 MAKELONG(0, 0));
f521bae6 296#endif
f521bae6 297}
1c4293cb
VZ
298
299// Provided so that, for example, ComboBox editor can use the same code
300// (multiple inheritance would get way too messy).
301bool 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
333bool 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
342bool 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
365bool wxPGTextCtrlEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
366{
367 return wxPGTextCtrlEditor::GetTextCtrlValueFromControl(variant, property, ctrl);
368}
369
370
371void 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
382void 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
393void wxPGTextCtrlEditor::OnFocus( wxPGProperty*, wxWindow* wnd ) const
394{
395 wxTextCtrl* tc = wxStaticCast(wnd, wxTextCtrl);
396
397 tc->SetSelection(-1,-1);
398}
399
400
401wxPGTextCtrlEditor::~wxPGTextCtrlEditor() { }
402
403
404// -----------------------------------------------------------------------
405// wxPGChoiceEditor
406// -----------------------------------------------------------------------
407
408
52cefafe 409WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(Choice,wxPGChoiceEditor,wxPGEditor)
1c4293cb
VZ
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.
415class wxPGDoubleClickProcessor : public wxEvtHandler
416{
417public:
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
428protected:
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
483private:
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
492BEGIN_EVENT_TABLE(wxPGDoubleClickProcessor, wxEvtHandler)
493 EVT_MOUSE_EVENTS(wxPGDoubleClickProcessor::OnMouseEvent)
494 EVT_SET_FOCUS(wxPGDoubleClickProcessor::OnSetFocus)
495END_EVENT_TABLE()
496
497
498
499class wxPGComboBox : public wxOwnerDrawnComboBox
500{
501public:
502
503 wxPGComboBox()
504 : wxOwnerDrawnComboBox()
505 {
506 m_dclickProcessor = (wxPGDoubleClickProcessor*) 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
e9cc4973
JS
540 m_dclickProcessor = new
541 wxPGDoubleClickProcessor( this, GetGrid()->GetSelection() );
1c4293cb
VZ
542
543 PushEventHandler(m_dclickProcessor);
544
545 return true;
546 }
547
e9cc4973
JS
548 virtual void OnDrawItem( wxDC& dc,
549 const wxRect& rect,
550 int item,
551 int flags ) const
1c4293cb
VZ
552 {
553 wxPropertyGrid* pg = GetGrid();
e9cc4973 554 pg->OnComboItemPaint( this, item, &dc, (wxRect&)rect, flags );
1c4293cb 555 }
e9cc4973 556
1c4293cb
VZ
557 virtual wxCoord OnMeasureItem( size_t item ) const
558 {
559 wxPropertyGrid* pg = GetGrid();
560 wxRect rect;
561 rect.x = -1;
562 rect.width = 0;
e9cc4973 563 pg->OnComboItemPaint( this, item, NULL, rect, 0 );
1c4293cb
VZ
564 return rect.height;
565 }
566
567 wxPropertyGrid* GetGrid() const
568 {
e9cc4973
JS
569 wxPropertyGrid* pg = wxDynamicCast(GetParent()->GetParent(),
570 wxPropertyGrid);
1c4293cb
VZ
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;
e9cc4973 581 pg->OnComboItemPaint( this, item, NULL, rect, 0 );
1c4293cb
VZ
582 return rect.width;
583 }
584
e9cc4973
JS
585 virtual void PositionTextCtrl( int WXUNUSED(textCtrlXAdjust),
586 int WXUNUSED(textCtrlYAdjust) )
1c4293cb
VZ
587 {
588 wxPropertyGrid* pg = GetGrid();
589 wxOwnerDrawnComboBox::PositionTextCtrl(
e9cc4973
JS
590 wxPG_TEXTCTRLXADJUST -
591 (wxPG_XBEFOREWIDGET+wxPG_CONTROL_MARGIN+1) - 1,
1c4293cb
VZ
592 pg->GetSpacingY() + 2
593 );
594 }
595
596private:
597 wxPGDoubleClickProcessor* m_dclickProcessor;
598 bool m_sizeEventCalled;
599};
600
601
e9cc4973 602void wxPropertyGrid::OnComboItemPaint( const wxPGComboBox* pCb,
1c4293cb 603 int item,
e9cc4973 604 wxDC* pDc,
1c4293cb
VZ
605 wxRect& rect,
606 int flags )
607{
1c4293cb
VZ
608 // Sanity check
609 wxASSERT( IsKindOf(CLASSINFO(wxPropertyGrid)) );
610
611 wxPGProperty* p = m_selected;
612 wxString text;
613
939d9364 614 const wxPGChoices& choices = p->GetChoices();
1c4293cb 615 const wxPGCommonValue* comVal = NULL;
1c4293cb
VZ
616 int comVals = p->GetDisplayedCommonValueCount();
617 int comValIndex = -1;
939d9364
JS
618
619 int choiceCount = 0;
620 if ( choices.IsOk() )
621 choiceCount = choices.GetCount();
622
1c4293cb
VZ
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() )
1425eca5 639 text = p->GetValueAsString(0);
1c4293cb
VZ
640 }
641 }
642
643 if ( item < 0 )
644 return;
645
646 wxSize cis;
647
648 const wxBitmap* itemBitmap = NULL;
649
939d9364
JS
650 if ( item >= 0 && choices.IsOk() && choices.Item(item).GetBitmap().Ok() && comValIndex == -1 )
651 itemBitmap = &choices.Item(item).GetBitmap();
1c4293cb
VZ
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 GetTextExtent(text, &x, &y, 0, 0, &m_font);
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
e9cc4973
JS
687 if ( pDc )
688 pDc->SetBrush(*wxWHITE_BRUSH);
1c4293cb
VZ
689
690 if ( rect.x >= 0 )
691 {
692 //
693 // DrawItem call
e9cc4973 694 wxDC& dc = *pDc;
1c4293cb
VZ
695
696 wxPoint pt(rect.x + wxPG_CONTROL_MARGIN - wxPG_CHOICEXADJUST - 1,
697 rect.y + 1);
698
d7e2b522 699 int renderFlags = wxPGCellRenderer::DontUseCellColours;
1c4293cb
VZ
700
701 if ( flags & wxODCB_PAINTING_CONTROL )
702 renderFlags |= wxPGCellRenderer::Control;
a2851207
JS
703 else
704 renderFlags |= wxPGCellRenderer::ChoicePopup;
1c4293cb
VZ
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
939d9364 756 if ( choices.IsOk() && item >= 0 && comValIndex < 0 )
1c4293cb 757 {
939d9364 758 const wxPGChoiceEntry& cell = choices.Item(item);
1c4293cb
VZ
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
e9cc4973 781 wxDC& dc = *pDc;
1c4293cb
VZ
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
789bool 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
814wxWindow* wxPGChoiceEditor::CreateControlsBase( wxPropertyGrid* propGrid,
815 wxPGProperty* property,
816 const wxPoint& pos,
817 const wxSize& sz,
818 long extraStyle ) const
819{
939d9364
JS
820 const wxPGChoices& choices = property->GetChoices();
821 wxString defString;
822 int index = property->GetChoiceSelection();
1c4293cb
VZ
823
824 bool isUnspecified = property->IsValueUnspecified();
825
939d9364 826 if ( !isUnspecified )
1c4293cb
VZ
827 defString = property->GetDisplayedString();
828
1c4293cb
VZ
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
7adf8bf3 842 int odcbFlags = extraStyle | wxBORDER_NONE | wxTE_PROCESS_ENTER;
1c4293cb
VZ
843
844 //
845 // If common value specified, use appropriate index
846 unsigned int cmnVals = property->GetDisplayedCommonValueCount();
847 if ( cmnVals )
848 {
849 if ( !isUnspecified )
850 {
851 int cmnVal = property->GetCommonValue();
852 if ( cmnVal >= 0 )
853 {
854 index = labels.size() + cmnVal;
855 }
856 }
857
858 unsigned int i;
859 for ( i=0; i<cmnVals; i++ )
860 labels.Add(propGrid->GetCommonValueLabel(i));
861 }
862
863 cb = new wxPGComboBox();
864#ifdef __WXMSW__
865 cb->Hide();
866#endif
867 cb->Create(ctrlParent,
868 wxPG_SUBID1,
869 wxString(),
870 po,
871 si,
872 labels,
873 odcbFlags);
874
1c4293cb 875 cb->SetButtonPosition(si.y,0,wxRIGHT);
1c4293cb
VZ
876 cb->SetTextIndent(wxPG_XBEFORETEXT-1);
877
878 wxPGChoiceEditor_SetCustomPaintWidth( propGrid, cb, property->GetCommonValue() );
1c4293cb
VZ
879
880 if ( index >= 0 && index < (int)cb->GetCount() )
881 {
882 cb->SetSelection( index );
883 if ( defString.length() )
884 cb->SetText( defString );
885 }
886 else if ( !(extraStyle & wxCB_READONLY) && defString.length() )
887 cb->SetValue( defString );
888 else
889 cb->SetSelection( -1 );
890
891#ifdef __WXMSW__
892 cb->Show();
893#endif
894
895 return (wxWindow*) cb;
896}
897
898
899void wxPGChoiceEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
900{
901 wxASSERT( ctrl );
902 wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
903 wxASSERT( cb->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)));
939d9364 904 int ind = property->GetChoiceSelection();
1c4293cb
VZ
905 cb->SetSelection(ind);
906}
907
908wxPGWindowList wxPGChoiceEditor::CreateControls( wxPropertyGrid* propGrid, wxPGProperty* property,
909 const wxPoint& pos, const wxSize& sz ) const
910{
911 return CreateControlsBase(propGrid,property,pos,sz,wxCB_READONLY);
912}
913
914
915int wxPGChoiceEditor::InsertItem( wxWindow* ctrl, const wxString& label, int index ) const
916{
917 wxASSERT( ctrl );
918 wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
919 wxASSERT( cb->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)));
920
921 if (index < 0)
922 index = cb->GetCount();
923
924 return cb->Insert(label,index);
925}
926
927
928void wxPGChoiceEditor::DeleteItem( wxWindow* ctrl, int index ) const
929{
930 wxASSERT( ctrl );
931 wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
932 wxASSERT( cb->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)));
933
934 cb->Delete(index);
935}
936
937bool wxPGChoiceEditor::OnEvent( wxPropertyGrid* propGrid, wxPGProperty* property,
938 wxWindow* ctrl, wxEvent& event ) const
939{
940 if ( event.GetEventType() == wxEVT_COMMAND_COMBOBOX_SELECTED )
941 {
942 wxPGComboBox* cb = (wxPGComboBox*)ctrl;
943 int index = cb->GetSelection();
944 int cmnValIndex = -1;
945 int cmnVals = property->GetDisplayedCommonValueCount();
946 int items = cb->GetCount();
947
948 if ( index >= (items-cmnVals) )
949 {
950 // Yes, a common value is being selected
951 cmnValIndex = index - (items-cmnVals);
952 property->SetCommonValue( cmnValIndex );
953
954 // Truly set value to unspecified?
955 if ( propGrid->GetUnspecifiedCommonValue() == cmnValIndex )
956 {
957 if ( !property->IsValueUnspecified() )
958 propGrid->SetInternalFlag(wxPG_FL_VALUE_CHANGE_IN_EVENT);
959 property->SetValueToUnspecified();
960 if ( !cb->HasFlag(wxCB_READONLY) )
961 cb->GetTextCtrl()->SetValue(wxEmptyString);
962 return false;
963 }
964 }
965 return wxPGChoiceEditor_SetCustomPaintWidth( propGrid, cb, cmnValIndex );
966 }
967 return false;
968}
969
970
971bool wxPGChoiceEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
972{
973 wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
974
975 int index = cb->GetSelection();
976
939d9364 977 if ( index != property->GetChoiceSelection() ||
1c4293cb
VZ
978 // Changing unspecified always causes event (returning
979 // true here should be enough to trigger it).
980 property->IsValueUnspecified()
981 )
982 {
983 return property->IntToValue( variant, index, 0 );
984 }
985 return false;
986}
987
988
989void wxPGChoiceEditor::SetControlStringValue( wxPGProperty* WXUNUSED(property), wxWindow* ctrl, const wxString& txt ) const
990{
991 wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
992 wxASSERT( cb );
993 cb->SetValue(txt);
994}
995
996
997void wxPGChoiceEditor::SetControlIntValue( wxPGProperty* WXUNUSED(property), wxWindow* ctrl, int value ) const
998{
999 wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1000 wxASSERT( cb );
1001 cb->SetSelection(value);
1002}
1003
1004
1005void wxPGChoiceEditor::SetValueToUnspecified( wxPGProperty* WXUNUSED(property), wxWindow* ctrl ) const
1006{
1007 wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1008 cb->SetSelection(-1);
1009}
1010
1011
1012bool wxPGChoiceEditor::CanContainCustomImage() const
1013{
1014 return true;
1015}
1016
1017
1018wxPGChoiceEditor::~wxPGChoiceEditor() { }
1019
1020
1021// -----------------------------------------------------------------------
1022// wxPGComboBoxEditor
1023// -----------------------------------------------------------------------
1024
1025
52cefafe
JS
1026WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(ComboBox,
1027 wxPGComboBoxEditor,
1028 wxPGChoiceEditor)
1c4293cb
VZ
1029
1030
1031void wxPGComboBoxEditor::UpdateControl( wxPGProperty* property, wxWindow* ctrl ) const
1032{
1033 wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1425eca5 1034 cb->SetValue(property->GetValueAsString(wxPG_EDITABLE_VALUE));
1c4293cb
VZ
1035
1036 // TODO: If string matches any selection, then select that.
1037}
1038
1039
1040wxPGWindowList wxPGComboBoxEditor::CreateControls( wxPropertyGrid* propGrid,
1041 wxPGProperty* property,
1042 const wxPoint& pos,
1043 const wxSize& sz ) const
1044{
1045 return CreateControlsBase(propGrid,property,pos,sz,0);
1046}
1047
1048
1049bool wxPGComboBoxEditor::OnEvent( wxPropertyGrid* propGrid,
1050 wxPGProperty* property,
1051 wxWindow* ctrl,
1052 wxEvent& event ) const
1053{
1054 wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*) NULL;
1055 wxWindow* textCtrl = (wxWindow*) NULL;
1056
1057 if ( ctrl )
1058 {
1059 cb = (wxOwnerDrawnComboBox*)ctrl;
1060 textCtrl = cb->GetTextCtrl();
1061 }
1062
1063 if ( wxPGTextCtrlEditor::OnTextCtrlEvent(propGrid,property,textCtrl,event) )
1064 return true;
1065
1066 return wxPGChoiceEditor::OnEvent(propGrid,property,ctrl,event);
1067}
1068
1069
1070bool wxPGComboBoxEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
1071{
1072 wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1073 wxString textVal = cb->GetValue();
1074
1075 if ( property->UsesAutoUnspecified() && !textVal.length() )
1076 {
1077 variant.MakeNull();
1078 return true;
1079 }
1080
1081 bool res = property->StringToValue(variant, textVal, wxPG_EDITABLE_VALUE);
1082
1083 // Changing unspecified always causes event (returning
1084 // true here should be enough to trigger it).
1085 if ( !res && variant.IsNull() )
1086 res = true;
1087
1088 return res;
1089}
1090
1091
1092void wxPGComboBoxEditor::OnFocus( wxPGProperty*, wxWindow* ctrl ) const
1093{
1094 wxOwnerDrawnComboBox* cb = (wxOwnerDrawnComboBox*)ctrl;
1095 cb->GetTextCtrl()->SetSelection(-1,-1);
1096}
1097
1098
1099wxPGComboBoxEditor::~wxPGComboBoxEditor() { }
1100
1101
1102// -----------------------------------------------------------------------
1103// wxPGChoiceAndButtonEditor
1104// -----------------------------------------------------------------------
1105
1106
52cefafe
JS
1107WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(ChoiceAndButton,
1108 wxPGChoiceAndButtonEditor,
1109 wxPGChoiceEditor)
1c4293cb
VZ
1110
1111
1112wxPGWindowList wxPGChoiceAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
1113 wxPGProperty* property,
1114 const wxPoint& pos,
1115 const wxSize& sz ) const
1116{
1117 // Use one two units smaller to match size of the combo's dropbutton.
1118 // (normally a bigger button is used because it looks better)
1119 int bt_wid = sz.y;
1120 bt_wid -= 2;
1121 wxSize bt_sz(bt_wid,bt_wid);
1122
1123 // Position of button.
1124 wxPoint bt_pos(pos.x+sz.x-bt_sz.x,pos.y);
1125#ifdef __WXMAC__
1126 bt_pos.y -= 1;
1127#else
1128 bt_pos.y += 1;
1129#endif
1130
1131 wxWindow* bt = propGrid->GenerateEditorButton( bt_pos, bt_sz );
1132
1133 // Size of choice.
1134 wxSize ch_sz(sz.x-bt->GetSize().x,sz.y);
1135
1136#ifdef __WXMAC__
1137 ch_sz.x -= wxPG_TEXTCTRL_AND_BUTTON_SPACING;
1138#endif
1139
c26873c8 1140 wxWindow* ch = wxPGEditor_Choice->CreateControls(propGrid,property,
1c4293cb
VZ
1141 pos,ch_sz).m_primary;
1142
1143#ifdef __WXMSW__
1144 bt->Show();
1145#endif
1146
1147 return wxPGWindowList(ch, bt);
1148}
1149
1150
1151wxPGChoiceAndButtonEditor::~wxPGChoiceAndButtonEditor() { }
1152
1153
1154// -----------------------------------------------------------------------
1155// wxPGTextCtrlAndButtonEditor
1156// -----------------------------------------------------------------------
1157
52cefafe
JS
1158WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(TextCtrlAndButton,
1159 wxPGTextCtrlAndButtonEditor,
1160 wxPGTextCtrlEditor)
1c4293cb
VZ
1161
1162
1163wxPGWindowList wxPGTextCtrlAndButtonEditor::CreateControls( wxPropertyGrid* propGrid,
1164 wxPGProperty* property,
1165 const wxPoint& pos,
1166 const wxSize& sz ) const
1167{
1168 wxWindow* wnd2;
1169 wxWindow* wnd = propGrid->GenerateEditorTextCtrlAndButton( pos, sz, &wnd2,
1170 property->GetFlags() & wxPG_PROP_NOEDITOR, property);
1171
1172 return wxPGWindowList(wnd, wnd2);
1173}
1174
1175
1176wxPGTextCtrlAndButtonEditor::~wxPGTextCtrlAndButtonEditor() { }
1177
1178
1179// -----------------------------------------------------------------------
1180// wxPGCheckBoxEditor
1181// -----------------------------------------------------------------------
1182
1183#if wxPG_INCLUDE_CHECKBOX
1184
52cefafe
JS
1185WX_PG_IMPLEMENT_INTERNAL_EDITOR_CLASS(CheckBox,
1186 wxPGCheckBoxEditor,
1187 wxPGEditor)
1c4293cb
VZ
1188
1189
e84ed895
JS
1190// Check box state flags
1191enum
1c4293cb 1192{
e84ed895
JS
1193 wxSCB_STATE_UNCHECKED = 0,
1194 wxSCB_STATE_CHECKED = 1,
1195 wxSCB_STATE_BOLD = 2,
1196 wxSCB_STATE_UNSPECIFIED = 4
1197};
1198
1199const int wxSCB_SETVALUE_CYCLE = 2;
1c4293cb 1200
e84ed895
JS
1201
1202static void DrawSimpleCheckBox( wxDC& dc, const wxRect& rect, int box_hei,
1203 int state, const wxColour& lineCol )
1204{
1c4293cb 1205 // Box rectangle.
e84ed895
JS
1206 wxRect r(rect.x+wxPG_XBEFORETEXT,rect.y+((rect.height-box_hei)/2),
1207 box_hei,box_hei);
1208 wxColour useCol = lineCol;
1209
1210 if ( state & wxSCB_STATE_UNSPECIFIED )
1211 {
1212 useCol = wxColour(220, 220, 220);
1213 }
1c4293cb
VZ
1214
1215 // Draw check mark first because it is likely to overdraw the
1216 // surrounding rectangle.
e84ed895 1217 if ( state & wxSCB_STATE_CHECKED )
1c4293cb
VZ
1218 {
1219 wxRect r2(r.x+wxPG_CHECKMARK_XADJ,
1220 r.y+wxPG_CHECKMARK_YADJ,
1221 r.width+wxPG_CHECKMARK_WADJ,
1222 r.height+wxPG_CHECKMARK_HADJ);
1223 #if wxPG_CHECKMARK_DEFLATE
1224 r2.Deflate(wxPG_CHECKMARK_DEFLATE);
1225 #endif
1226 dc.DrawCheckMark(r2);
1227
1228 // This would draw a simple cross check mark.
1229 // dc.DrawLine(r.x,r.y,r.x+r.width-1,r.y+r.height-1);
1230 // dc.DrawLine(r.x,r.y+r.height-1,r.x+r.width-1,r.y);
1c4293cb
VZ
1231 }
1232
e84ed895 1233 if ( !(state & wxSCB_STATE_BOLD) )
1c4293cb
VZ
1234 {
1235 // Pen for thin rectangle.
e84ed895 1236 dc.SetPen(useCol);
1c4293cb
VZ
1237 }
1238 else
1239 {
1240 // Pen for bold rectangle.
e84ed895 1241 wxPen linepen(useCol,2,wxSOLID);
1c4293cb
VZ
1242 linepen.SetJoin(wxJOIN_MITER); // This prevents round edges.
1243 dc.SetPen(linepen);
1244 r.x++;
1245 r.y++;
1246 r.width--;
1247 r.height--;
1248 }
1249
1250 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1251
1252 dc.DrawRectangle(r);
1253 dc.SetPen(*wxTRANSPARENT_PEN);
1254}
1255
1256//
1257// Real simple custom-drawn checkbox-without-label class.
1258//
1259class wxSimpleCheckBox : public wxControl
1260{
1261public:
1262
1263 void SetValue( int value );
1264
1265 wxSimpleCheckBox( wxWindow* parent,
1266 wxWindowID id,
1267 const wxPoint& pos = wxDefaultPosition,
1268 const wxSize& size = wxDefaultSize )
7adf8bf3 1269 : wxControl(parent,id,pos,size,wxBORDER_NONE|wxWANTS_CHARS)
1c4293cb
VZ
1270 {
1271 // Due to SetOwnFont stuff necessary for GTK+ 1.2, we need to have this
1272 SetFont( parent->GetFont() );
1273
1274 m_state = 0;
e84ed895
JS
1275 m_boxHeight = 12;
1276
1277 SetBackgroundStyle( wxBG_STYLE_CUSTOM );
1c4293cb
VZ
1278 }
1279
1280 virtual ~wxSimpleCheckBox();
1281
1c4293cb
VZ
1282 int m_state;
1283 int m_boxHeight;
1284
e84ed895
JS
1285private:
1286 void OnPaint( wxPaintEvent& event );
1287 void OnLeftClick( wxMouseEvent& event );
1288 void OnKeyDown( wxKeyEvent& event );
1289
1290 void OnResize( wxSizeEvent& event )
1291 {
1292 Refresh();
1293 event.Skip();
1294 }
1295
1c4293cb
VZ
1296 static wxBitmap* ms_doubleBuffer;
1297
e84ed895 1298 DECLARE_EVENT_TABLE()
1c4293cb
VZ
1299};
1300
e84ed895
JS
1301BEGIN_EVENT_TABLE(wxSimpleCheckBox, wxControl)
1302 EVT_PAINT(wxSimpleCheckBox::OnPaint)
1303 EVT_LEFT_DOWN(wxSimpleCheckBox::OnLeftClick)
1304 EVT_LEFT_DCLICK(wxSimpleCheckBox::OnLeftClick)
1305 EVT_KEY_DOWN(wxSimpleCheckBox::OnKeyDown)
1306 EVT_SIZE(wxSimpleCheckBox::OnResize)
1307END_EVENT_TABLE()
1308
1c4293cb
VZ
1309wxSimpleCheckBox::~wxSimpleCheckBox()
1310{
1311 delete ms_doubleBuffer;
1312 ms_doubleBuffer = NULL;
1313}
1314
1c4293cb
VZ
1315wxBitmap* wxSimpleCheckBox::ms_doubleBuffer = (wxBitmap*) NULL;
1316
e84ed895
JS
1317void wxSimpleCheckBox::OnPaint( wxPaintEvent& WXUNUSED(event) )
1318{
1319 wxSize clientSize = GetClientSize();
1320 wxAutoBufferedPaintDC dc(this);
1321
1322 dc.Clear();
1323 wxRect rect(0,0,clientSize.x,clientSize.y);
1324 rect.y += 1;
1325 rect.width += 1;
1326
1327 wxColour bgcol = GetBackgroundColour();
1328 dc.SetBrush( bgcol );
1329 dc.SetPen( bgcol );
1330 dc.DrawRectangle( rect );
1331
1332 wxColour txcol = GetForegroundColour();
1333
1334 int state = m_state;
1335 if ( !(state & wxSCB_STATE_UNSPECIFIED) &&
1336 m_font.GetWeight() == wxBOLD )
1337 state |= wxSCB_STATE_BOLD;
1338
1339 DrawSimpleCheckBox(dc,rect,m_boxHeight,state,txcol);
1340}
1341
1342void wxSimpleCheckBox::OnLeftClick( wxMouseEvent& event )
1343{
1344 if ( (event.m_x > (wxPG_XBEFORETEXT-2)) &&
1345 (event.m_x <= (wxPG_XBEFORETEXT-2+m_boxHeight)) )
1346 {
1347 SetValue(wxSCB_SETVALUE_CYCLE);
1348 }
1349}
1350
1351void wxSimpleCheckBox::OnKeyDown( wxKeyEvent& event )
1352{
1353 if ( event.GetKeyCode() == WXK_SPACE )
1354 {
1355 SetValue(wxSCB_SETVALUE_CYCLE);
1356 }
1357}
1358
1c4293cb
VZ
1359void wxSimpleCheckBox::SetValue( int value )
1360{
e84ed895 1361 if ( value == wxSCB_SETVALUE_CYCLE )
1c4293cb 1362 {
e84ed895
JS
1363 if ( m_state & wxSCB_STATE_CHECKED )
1364 m_state &= ~wxSCB_STATE_CHECKED;
1365 else
1366 m_state |= wxSCB_STATE_CHECKED;
1c4293cb
VZ
1367 }
1368 else
1369 {
1370 m_state = value;
1371 }
1372 Refresh();
1373
1374 wxCommandEvent evt(wxEVT_COMMAND_CHECKBOX_CLICKED,GetParent()->GetId());
1375
1376 wxPropertyGrid* propGrid = (wxPropertyGrid*) GetParent()->GetParent();
1377 wxASSERT( propGrid->IsKindOf(CLASSINFO(wxPropertyGrid)) );
b0996c3d 1378 propGrid->HandleCustomEditorEvent(evt);
1c4293cb
VZ
1379}
1380
1c4293cb
VZ
1381wxPGWindowList wxPGCheckBoxEditor::CreateControls( wxPropertyGrid* propGrid,
1382 wxPGProperty* property,
1383 const wxPoint& pos,
1384 const wxSize& size ) const
1385{
1386 wxPoint pt = pos;
1387 pt.x -= wxPG_XBEFOREWIDGET;
1388 wxSize sz = size;
1389 sz.x = propGrid->GetFontHeight() + (wxPG_XBEFOREWIDGET*2) + 4;
1390
e84ed895
JS
1391 wxSimpleCheckBox* cb = new wxSimpleCheckBox(propGrid->GetPanel(),
1392 wxPG_SUBID1, pt, sz);
1c4293cb
VZ
1393
1394 cb->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
1395
e84ed895 1396 UpdateControl(property, cb);
1c4293cb 1397
e84ed895 1398 if ( !property->IsValueUnspecified() )
1c4293cb 1399 {
e84ed895
JS
1400 // If mouse cursor was on the item, toggle the value now.
1401 if ( propGrid->GetInternalFlags() & wxPG_FL_ACTIVATION_BY_CLICK )
1c4293cb 1402 {
e84ed895
JS
1403 wxPoint pt = cb->ScreenToClient(::wxGetMousePosition());
1404 if ( pt.x <= (wxPG_XBEFORETEXT-2+cb->m_boxHeight) )
1405 {
1406 if ( cb->m_state & wxSCB_STATE_CHECKED )
1407 cb->m_state &= ~wxSCB_STATE_CHECKED;
1408 else
1409 cb->m_state |= wxSCB_STATE_CHECKED;
1410
1411 // Makes sure wxPG_EVT_CHANGING etc. is sent for this initial
1412 // click
1413 propGrid->ChangePropertyValue(property,
1414 wxPGVariant_Bool(cb->m_state));
1415 }
1c4293cb
VZ
1416 }
1417 }
1418
1419 propGrid->SetInternalFlag( wxPG_FL_FIXED_WIDTH_EDITOR );
1420
1421 return cb;
1422}
1423
e84ed895
JS
1424void wxPGCheckBoxEditor::DrawValue( wxDC& dc, const wxRect& rect,
1425 wxPGProperty* property,
1426 const wxString& WXUNUSED(text) ) const
1c4293cb 1427{
e84ed895
JS
1428 int state = wxSCB_STATE_UNCHECKED;
1429 wxColour rectCol = dc.GetTextForeground();
1c4293cb 1430
e84ed895 1431 if ( !property->IsValueUnspecified() )
1c4293cb 1432 {
e84ed895
JS
1433 state = property->GetChoiceSelection();
1434 if ( dc.GetFont().GetWeight() == wxBOLD )
1435 state |= wxSCB_STATE_BOLD;
1436 }
1437 else
1438 {
1439 state |= wxSCB_STATE_UNSPECIFIED;
1c4293cb
VZ
1440 }
1441
e84ed895 1442 DrawSimpleCheckBox(dc, rect, dc.GetCharHeight(), state, rectCol);
1c4293cb 1443}
1c4293cb 1444
e84ed895
JS
1445void wxPGCheckBoxEditor::UpdateControl( wxPGProperty* property,
1446 wxWindow* ctrl ) const
1c4293cb 1447{
e84ed895
JS
1448 wxSimpleCheckBox* cb = (wxSimpleCheckBox*) ctrl;
1449 wxASSERT( cb );
1450
1c4293cb 1451 if ( !property->IsValueUnspecified() )
e84ed895
JS
1452 cb->m_state = property->GetChoiceSelection();
1453 else
1454 cb->m_state = wxSCB_STATE_UNSPECIFIED;
1c4293cb 1455
e84ed895
JS
1456 wxPropertyGrid* propGrid = property->GetGrid();
1457 cb->m_boxHeight = propGrid->GetFontHeight();
1c4293cb 1458
e84ed895
JS
1459 cb->Refresh();
1460}
1c4293cb
VZ
1461
1462bool wxPGCheckBoxEditor::OnEvent( wxPropertyGrid* WXUNUSED(propGrid), wxPGProperty* WXUNUSED(property),
1463 wxWindow* WXUNUSED(ctrl), wxEvent& event ) const
1464{
1465 if ( event.GetEventType() == wxEVT_COMMAND_CHECKBOX_CLICKED )
1466 {
1467 return true;
1468 }
1469 return false;
1470}
1471
1472
1473bool wxPGCheckBoxEditor::GetValueFromControl( wxVariant& variant, wxPGProperty* property, wxWindow* ctrl ) const
1474{
1475 wxSimpleCheckBox* cb = (wxSimpleCheckBox*)ctrl;
1476
1477 int index = cb->m_state;
1478
939d9364 1479 if ( index != property->GetChoiceSelection() ||
1c4293cb
VZ
1480 // Changing unspecified always causes event (returning
1481 // true here should be enough to trigger it).
1482 property->IsValueUnspecified()
1483 )
1484 {
1485 return property->IntToValue(variant, index, 0);
1486 }
1487 return false;
1488}
1489
1490
1491void wxPGCheckBoxEditor::SetControlIntValue( wxPGProperty* WXUNUSED(property), wxWindow* ctrl, int value ) const
1492{
1493 if ( value != 0 ) value = 1;
1494 ((wxSimpleCheckBox*)ctrl)->m_state = value;
1495 ctrl->Refresh();
1496}
1497
1498
1499void wxPGCheckBoxEditor::SetValueToUnspecified( wxPGProperty* WXUNUSED(property), wxWindow* ctrl ) const
1500{
1501 ((wxSimpleCheckBox*)ctrl)->m_state = 0;
1502 ctrl->Refresh();
1503}
1504
1505
1506wxPGCheckBoxEditor::~wxPGCheckBoxEditor() { }
1507
1508
1509#endif // wxPG_INCLUDE_CHECKBOX
1510
1511// -----------------------------------------------------------------------
1512
1513wxWindow* wxPropertyGrid::GetEditorControl() const
1514{
1515 wxWindow* ctrl = m_wndEditor;
1516
1517 if ( !ctrl )
1518 return ctrl;
1519
1c4293cb
VZ
1520 return ctrl;
1521}
1522
1523// -----------------------------------------------------------------------
1524
1525void wxPropertyGrid::CorrectEditorWidgetSizeX()
1526{
1527 if ( m_selColumn == -1 )
1528 return;
1529
1530 int secWid = 0;
1531 int newSplitterx = m_pState->DoGetSplitterPosition(m_selColumn-1);
1532 int newWidth = newSplitterx + m_pState->m_colWidths[m_selColumn];
1533
1534 if ( m_wndEditor2 )
1535 {
1536 // if width change occurred, move secondary wnd by that amount
1537 wxRect r = m_wndEditor2->GetRect();
1538 secWid = r.width;
1539 r.x = newWidth - secWid;
1540
1541 m_wndEditor2->SetSize( r );
1542
1543 // if primary is textctrl, then we have to add some extra space
1544#ifdef __WXMAC__
1545 if ( m_wndEditor )
1546#else
1547 if ( m_wndEditor && m_wndEditor->IsKindOf(CLASSINFO(wxTextCtrl)) )
1548#endif
1549 secWid += wxPG_TEXTCTRL_AND_BUTTON_SPACING;
1550 }
1551
1552 if ( m_wndEditor )
1553 {
1554 wxRect r = m_wndEditor->GetRect();
1555
1556 r.x = newSplitterx+m_ctrlXAdjust;
1557
1558 if ( !(m_iFlags & wxPG_FL_FIXED_WIDTH_EDITOR) )
1559 r.width = newWidth - r.x - secWid;
1560
1561 m_wndEditor->SetSize(r);
1562 }
1563
1564 if ( m_wndEditor2 )
1565 m_wndEditor2->Refresh();
1566}
1567
1568// -----------------------------------------------------------------------
1569
1570void wxPropertyGrid::CorrectEditorWidgetPosY()
1571{
aace2c4f
JS
1572 if ( m_selColumn == -1 )
1573 return;
1574
1c4293cb
VZ
1575 if ( m_selected && (m_wndEditor || m_wndEditor2) )
1576 {
1577 wxRect r = GetEditorWidgetRect(m_selected, m_selColumn);
1578
1579 if ( m_wndEditor )
1580 {
1581 wxPoint pos = m_wndEditor->GetPosition();
1582
1583 // Calculate y offset
1584 int offset = pos.y % m_lineHeight;
1585
1586 m_wndEditor->Move(pos.x, r.y + offset);
1587 }
1588
1589 if ( m_wndEditor2 )
1590 {
1591 wxPoint pos = m_wndEditor2->GetPosition();
1592
1593 m_wndEditor2->Move(pos.x, r.y);
1594 }
1595 }
1596}
1597
1598// -----------------------------------------------------------------------
1599
1c4293cb
VZ
1600// Fixes position of wxTextCtrl-like control (wxSpinCtrl usually
1601// fits into that category as well).
1602void wxPropertyGrid::FixPosForTextCtrl( wxWindow* ctrl, const wxPoint& offset )
1603{
1604 // Center the control vertically
1605 wxRect finalPos = ctrl->GetRect();
1606 int y_adj = (m_lineHeight - finalPos.height)/2 + wxPG_TEXTCTRLYADJUST;
1607
1608 // Prevent over-sized control
1609 int sz_dec = (y_adj + finalPos.height) - m_lineHeight;
1610 if ( sz_dec < 0 ) sz_dec = 0;
1611
1612 finalPos.y += y_adj;
1613 finalPos.height -= (y_adj+sz_dec);
1614
1615 const int textCtrlXAdjust = wxPG_TEXTCTRLXADJUST;
1616
1617 finalPos.x += textCtrlXAdjust;
1618 finalPos.width -= textCtrlXAdjust;
1619
1620 finalPos.x += offset.x;
1621 finalPos.y += offset.y;
1622
1623 ctrl->SetSize(finalPos);
1624}
1625
1626// -----------------------------------------------------------------------
1627
1628wxWindow* wxPropertyGrid::GenerateEditorTextCtrl( const wxPoint& pos,
1629 const wxSize& sz,
1630 const wxString& value,
1631 wxWindow* secondary,
1632 int extraStyle,
1633 int maxLen )
1634{
d9fb481c 1635 wxWindowID id = wxPG_SUBID1;
1c4293cb
VZ
1636 wxPGProperty* selected = m_selected;
1637 wxASSERT(selected);
1638
1639 int tcFlags = wxTE_PROCESS_ENTER | extraStyle;
1640
1641 if ( selected->HasFlag(wxPG_PROP_READONLY) )
1642 tcFlags |= wxTE_READONLY;
1643
1644 wxPoint p(pos.x,pos.y);
1645 wxSize s(sz.x,sz.y);
1646
1647 // Need to reduce width of text control on Mac
1648#if defined(__WXMAC__)
012b4a07 1649 s.x -= 8;
1c4293cb
VZ
1650#endif
1651
1652 // Take button into acccount
1653 if ( secondary )
1654 {
1655 s.x -= (secondary->GetSize().x + wxPG_TEXTCTRL_AND_BUTTON_SPACING);
1656 m_iFlags &= ~(wxPG_FL_PRIMARY_FILLS_ENTIRE);
1657 }
1658
1659 // If the height is significantly higher, then use border, and fill the rect exactly.
1660 bool hasSpecialSize = false;
1661
1662 if ( (sz.y - m_lineHeight) > 5 )
1663 hasSpecialSize = true;
1664
1c4293cb
VZ
1665 wxWindow* ctrlParent = GetPanel();
1666
1667 if ( !hasSpecialSize )
7adf8bf3 1668 tcFlags |= wxBORDER_NONE;
1c4293cb
VZ
1669
1670 wxTextCtrl* tc = new wxTextCtrl();
1671
7adf8bf3 1672#if defined(__WXMSW__)
1c4293cb
VZ
1673 tc->Hide();
1674#endif
1675 SetupTextCtrlValue(value);
d9fb481c 1676 tc->Create(ctrlParent,id,value, p, s,tcFlags);
012b4a07 1677
1c4293cb 1678 wxWindow* ed = tc;
1c4293cb
VZ
1679
1680 // Center the control vertically
1681 if ( !hasSpecialSize )
1682 FixPosForTextCtrl(ed);
1683
1684#ifdef __WXMSW__
1685 ed->Show();
1686 if ( secondary )
1687 secondary->Show();
1688#endif
1689
1690 // Set maximum length
1691 if ( maxLen > 0 )
1692 tc->SetMaxLength( maxLen );
1693
1694 return (wxWindow*) ed;
1695}
1696
1697// -----------------------------------------------------------------------
1698
1699wxWindow* wxPropertyGrid::GenerateEditorButton( const wxPoint& pos, const wxSize& sz )
1700{
d9fb481c 1701 wxWindowID id = wxPG_SUBID2;
1c4293cb
VZ
1702 wxPGProperty* selected = m_selected;
1703 wxASSERT(selected);
1704
1705#ifdef __WXMAC__
1706 // Decorations are chunky on Mac, and we can't make the button square, so
1707 // do things a bit differently on this platform.
1708
1709 wxPoint p(pos.x+sz.x,
1710 pos.y+wxPG_BUTTON_SIZEDEC-wxPG_NAT_BUTTON_BORDER_Y);
1711 wxSize s(25, -1);
1712
1713 wxButton* but = new wxButton();
d9fb481c 1714 but->Create(GetPanel(),id,wxS("..."),p,s,wxWANTS_CHARS);
1c4293cb
VZ
1715
1716 // Now that we know the size, move to the correct position
1717 p.x = pos.x + sz.x - but->GetSize().x - 2;
1718 but->Move(p);
1719
1720#else
1721 wxSize s(sz.y-(wxPG_BUTTON_SIZEDEC*2)+(wxPG_NAT_BUTTON_BORDER_Y*2),
1722 sz.y-(wxPG_BUTTON_SIZEDEC*2)+(wxPG_NAT_BUTTON_BORDER_Y*2));
1723
1724 // Reduce button width to lineheight
1725 if ( s.x > m_lineHeight )
1726 s.x = m_lineHeight;
1727
d863389f
JS
1728#ifdef __WXGTK__
1729 // On wxGTK, take fixed button margins into account
1730 if ( s.x < 25 )
1731 s.x = 25;
1732#endif
1733
1c4293cb
VZ
1734 wxPoint p(pos.x+sz.x-s.x,
1735 pos.y+wxPG_BUTTON_SIZEDEC-wxPG_NAT_BUTTON_BORDER_Y);
1736
1737 wxButton* but = new wxButton();
d863389f 1738 #ifdef __WXMSW__
1c4293cb 1739 but->Hide();
d863389f 1740 #endif
d9fb481c 1741 but->Create(GetPanel(),id,wxS("..."),p,s,wxWANTS_CHARS);
1c4293cb 1742
d863389f
JS
1743 #ifdef __WXGTK__
1744 wxFont font = GetFont();
1745 font.SetPointSize(font.GetPointSize()-2);
1746 but->SetFont(font);
1747 #else
1748 but->SetFont(GetFont());
1749 #endif
1c4293cb
VZ
1750#endif
1751
1752 if ( selected->HasFlag(wxPG_PROP_READONLY) )
1753 but->Disable();
1754
1755 return but;
1756}
1757
1758// -----------------------------------------------------------------------
1759
1760wxWindow* wxPropertyGrid::GenerateEditorTextCtrlAndButton( const wxPoint& pos,
1761 const wxSize& sz,
1762 wxWindow** psecondary,
1763 int limitedEditing,
1764 wxPGProperty* property )
1765{
1766 wxButton* but = (wxButton*)GenerateEditorButton(pos,sz);
1767 *psecondary = (wxWindow*)but;
1768
1769 if ( limitedEditing )
1770 {
1771 #ifdef __WXMSW__
1772 // There is button Show in GenerateEditorTextCtrl as well
1773 but->Show();
1774 #endif
1775 return (wxWindow*) NULL;
1776 }
1777
1778 wxString text;
1779
1780 if ( !property->IsValueUnspecified() )
1425eca5 1781 text = property->GetValueAsString(property->HasFlag(wxPG_PROP_READONLY)?0:wxPG_EDITABLE_VALUE);
1c4293cb
VZ
1782
1783 return GenerateEditorTextCtrl(pos,sz,text,but,property->m_maxLen);
1784}
1785
1786// -----------------------------------------------------------------------
1787
1788wxTextCtrl* wxPropertyGrid::GetEditorTextCtrl() const
1789{
1790 wxWindow* wnd = GetEditorControl();
1791
1792 if ( !wnd )
1793 return NULL;
1794
1795 if ( wnd->IsKindOf(CLASSINFO(wxTextCtrl)) )
1796 return wxStaticCast(wnd, wxTextCtrl);
1797
1798 if ( wnd->IsKindOf(CLASSINFO(wxOwnerDrawnComboBox)) )
1799 {
1800 wxOwnerDrawnComboBox* cb = wxStaticCast(wnd, wxOwnerDrawnComboBox);
1801 return cb->GetTextCtrl();
1802 }
1803
1804 return NULL;
1805}
1806
1807// -----------------------------------------------------------------------
1808
1809wxPGEditor* wxPropertyGridInterface::GetEditorByName( const wxString& editorName )
1810{
1811 wxPGHashMapS2P::const_iterator it;
1812
1813 it = wxPGGlobalVars->m_mapEditorClasses.find(editorName);
1814 if ( it == wxPGGlobalVars->m_mapEditorClasses.end() )
1815 return NULL;
1816 return (wxPGEditor*) it->second;
1817}
1818
1819// -----------------------------------------------------------------------
1820// wxPGEditorDialogAdapter
1821// -----------------------------------------------------------------------
1822
1823IMPLEMENT_ABSTRACT_CLASS(wxPGEditorDialogAdapter, wxObject)
1824
1825bool wxPGEditorDialogAdapter::ShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
1826{
1827 if ( !propGrid->EditorValidate() )
1828 return false;
1829
1830 bool res = DoShowDialog( propGrid, property );
1831
1832 if ( res )
1833 {
1834 propGrid->ValueChangeInEvent( m_value );
1835 return true;
1836 }
1837
1838 return false;
1839}
1840
1841// -----------------------------------------------------------------------
1842// wxPGMultiButton
1843// -----------------------------------------------------------------------
1844
1845wxPGMultiButton::wxPGMultiButton( wxPropertyGrid* pg, const wxSize& sz )
1846 : wxWindow( pg->GetPanel(), wxPG_SUBID2, wxPoint(-100,-100), wxSize(0, sz.y) ),
1847 m_fullEditorSize(sz), m_buttonsWidth(0)
1848{
1849 SetBackgroundColour(pg->GetCellBackgroundColour());
1850}
1851
b0996c3d
JS
1852void wxPGMultiButton::Finalize( wxPropertyGrid* WXUNUSED(propGrid),
1853 const wxPoint& pos )
7a344f1b
JS
1854{
1855 Move( pos.x + m_fullEditorSize.x - m_buttonsWidth, pos.y );
7a344f1b
JS
1856}
1857
1c4293cb
VZ
1858int wxPGMultiButton::GenId( int id ) const
1859{
1860 if ( id < -1 )
1861 {
1862 if ( m_buttons.size() )
1863 id = GetButton(m_buttons.size()-1)->GetId() + 1;
1864 else
1865 id = wxPG_SUBID2;
1866 }
1867 return id;
1868}
1869
1870#if wxUSE_BMPBUTTON
1871void wxPGMultiButton::Add( const wxBitmap& bitmap, int id )
1872{
1873 id = GenId(id);
1874 wxSize sz = GetSize();
1875 wxButton* button = new wxBitmapButton( this, id, bitmap, wxPoint(sz.x, 0), wxSize(sz.y, sz.y) );
1876 m_buttons.push_back(button);
1877 int bw = button->GetSize().x;
1878 SetSize(wxSize(sz.x+bw,sz.y));
1879 m_buttonsWidth += bw;
1880}
1881#endif
1882
1883void wxPGMultiButton::Add( const wxString& label, int id )
1884{
1885 id = GenId(id);
1886 wxSize sz = GetSize();
1887 wxButton* button = new wxButton( this, id, label, wxPoint(sz.x, 0), wxSize(sz.y, sz.y) );
1888 m_buttons.push_back(button);
1889 int bw = button->GetSize().x;
1890 SetSize(wxSize(sz.x+bw,sz.y));
1891 m_buttonsWidth += bw;
1892}
1893
1894// -----------------------------------------------------------------------
f4bc1aa2
JS
1895
1896#endif // wxUSE_PROPGRID