Added wxNumericPropertyValidator, which is a custom wxTextValidator with more accurat...
[wxWidgets.git] / src / propgrid / props.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/propgrid/props.cpp
3 // Purpose: Basic Property Classes
4 // Author: Jaakko Salli
5 // Modified by:
6 // Created: 2005-05-14
7 // RCS-ID: $Id$
8 // Copyright: (c) Jaakko Salli
9 // Licence: wxWindows licence
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/bmpbuttn.h"
35 #include "wx/pen.h"
36 #include "wx/brush.h"
37 #include "wx/cursor.h"
38 #include "wx/dialog.h"
39 #include "wx/settings.h"
40 #include "wx/msgdlg.h"
41 #include "wx/choice.h"
42 #include "wx/stattext.h"
43 #include "wx/scrolwin.h"
44 #include "wx/dirdlg.h"
45 #include "wx/combobox.h"
46 #include "wx/layout.h"
47 #include "wx/sizer.h"
48 #include "wx/textdlg.h"
49 #include "wx/filedlg.h"
50 #include "wx/intl.h"
51 #endif
52
53 #include "wx/filename.h"
54
55 #include "wx/propgrid/propgrid.h"
56
57 #define wxPG_CUSTOM_IMAGE_WIDTH 20 // for wxColourProperty etc.
58
59
60 // -----------------------------------------------------------------------
61 // wxStringProperty
62 // -----------------------------------------------------------------------
63
64 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxStringProperty,wxPGProperty,
65 wxString,const wxString&,TextCtrl)
66
67 wxStringProperty::wxStringProperty( const wxString& label,
68 const wxString& name,
69 const wxString& value )
70 : wxPGProperty(label,name)
71 {
72 SetValue(value);
73 }
74
75 void wxStringProperty::OnSetValue()
76 {
77 if ( !m_value.IsNull() && m_value.GetString() == wxS("<composed>") )
78 SetFlag(wxPG_PROP_COMPOSED_VALUE);
79
80 if ( HasFlag(wxPG_PROP_COMPOSED_VALUE) )
81 {
82 wxString s;
83 DoGenerateComposedValue(s);
84 m_value = s;
85 }
86 }
87
88 wxStringProperty::~wxStringProperty() { }
89
90 wxString wxStringProperty::ValueToString( wxVariant& value,
91 int argFlags ) const
92 {
93 wxString s = value.GetString();
94
95 if ( GetChildCount() && HasFlag(wxPG_PROP_COMPOSED_VALUE) )
96 {
97 // Value stored in m_value is non-editable, non-full value
98 if ( (argFlags & wxPG_FULL_VALUE) || (argFlags & wxPG_EDITABLE_VALUE) )
99 {
100 // Calling this under incorrect conditions will fail
101 wxASSERT_MSG( argFlags & wxPG_VALUE_IS_CURRENT,
102 "Sorry, currently default wxPGProperty::ValueToString() "
103 "implementation only works if value is m_value." );
104
105 DoGenerateComposedValue(s, argFlags);
106 }
107
108 return s;
109 }
110
111 // If string is password and value is for visual purposes,
112 // then return asterisks instead the actual string.
113 if ( (m_flags & wxPG_PROP_PASSWORD) && !(argFlags & (wxPG_FULL_VALUE|wxPG_EDITABLE_VALUE)) )
114 return wxString(wxChar('*'), s.Length());
115
116 return s;
117 }
118
119 bool wxStringProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
120 {
121 if ( GetChildCount() && HasFlag(wxPG_PROP_COMPOSED_VALUE) )
122 return wxPGProperty::StringToValue(variant, text, argFlags);
123
124 if ( variant != text )
125 {
126 variant = text;
127 return true;
128 }
129
130 return false;
131 }
132
133 bool wxStringProperty::DoSetAttribute( const wxString& name, wxVariant& value )
134 {
135 if ( name == wxPG_STRING_PASSWORD )
136 {
137 m_flags &= ~(wxPG_PROP_PASSWORD);
138 if ( value.GetLong() ) m_flags |= wxPG_PROP_PASSWORD;
139 RecreateEditor();
140 return false;
141 }
142 return true;
143 }
144
145 // -----------------------------------------------------------------------
146 // wxNumericPropertyValidator
147 // -----------------------------------------------------------------------
148
149 #if wxUSE_VALIDATORS
150
151 wxNumericPropertyValidator::
152 wxNumericPropertyValidator( NumericType numericType, int base )
153 : wxTextValidator(wxFILTER_INCLUDE_CHAR_LIST)
154 {
155 wxArrayString arr;
156 arr.Add(wxS("0"));
157 arr.Add(wxS("1"));
158 arr.Add(wxS("2"));
159 arr.Add(wxS("3"));
160 arr.Add(wxS("4"));
161 arr.Add(wxS("5"));
162 arr.Add(wxS("6"));
163 arr.Add(wxS("7"));
164
165 if ( base >= 10 )
166 {
167 arr.Add(wxS("8"));
168 arr.Add(wxS("9"));
169 if ( base >= 16 )
170 {
171 arr.Add(wxS("a")); arr.Add(wxS("A"));
172 arr.Add(wxS("b")); arr.Add(wxS("B"));
173 arr.Add(wxS("c")); arr.Add(wxS("C"));
174 arr.Add(wxS("d")); arr.Add(wxS("D"));
175 arr.Add(wxS("e")); arr.Add(wxS("E"));
176 arr.Add(wxS("f")); arr.Add(wxS("F"));
177 }
178 }
179
180 if ( numericType == Signed )
181 {
182 arr.Add(wxS("+"));
183 arr.Add(wxS("-"));
184 }
185 else if ( numericType == Float )
186 {
187 arr.Add(wxS("+"));
188 arr.Add(wxS("-"));
189 arr.Add(wxS("."));
190 arr.Add(wxS("e"));
191 }
192
193 SetIncludes(arr);
194 }
195
196 bool wxNumericPropertyValidator::Validate(wxWindow* parent)
197 {
198 if ( !wxTextValidator::Validate(parent) )
199 return false;
200
201 wxWindow* wnd = GetWindow();
202 if ( !wnd->IsKindOf(CLASSINFO(wxTextCtrl)) )
203 return true;
204
205 // Do not allow zero-length string
206 wxTextCtrl* tc = static_cast<wxTextCtrl*>(wnd);
207 wxString text = tc->GetValue();
208
209 if ( !text.length() )
210 return false;
211
212 return true;
213 }
214
215 #endif // wxUSE_VALIDATORS
216
217 // -----------------------------------------------------------------------
218 // wxIntProperty
219 // -----------------------------------------------------------------------
220
221 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxIntProperty,wxPGProperty,
222 long,long,TextCtrl)
223
224 wxIntProperty::wxIntProperty( const wxString& label, const wxString& name,
225 long value ) : wxPGProperty(label,name)
226 {
227 SetValue(value);
228 }
229
230 wxIntProperty::wxIntProperty( const wxString& label, const wxString& name,
231 const wxLongLong& value ) : wxPGProperty(label,name)
232 {
233 SetValue(WXVARIANT(value));
234 }
235
236 wxIntProperty::~wxIntProperty() { }
237
238 wxString wxIntProperty::ValueToString( wxVariant& value,
239 int WXUNUSED(argFlags) ) const
240 {
241 if ( value.GetType() == wxPG_VARIANT_TYPE_LONG )
242 {
243 return wxString::Format(wxS("%li"),value.GetLong());
244 }
245 else if ( value.GetType() == wxPG_VARIANT_TYPE_LONGLONG )
246 {
247 wxLongLong ll = value.GetLongLong();
248 return ll.ToString();
249 }
250
251 return wxEmptyString;
252 }
253
254 bool wxIntProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
255 {
256 wxString s;
257 long value32;
258
259 if ( text.length() == 0 )
260 {
261 variant.MakeNull();
262 return true;
263 }
264
265 // We know it is a number, but let's still check
266 // the return value.
267 if ( text.IsNumber() )
268 {
269 // Remove leading zeroes, so that the number is not interpreted as octal
270 wxString::const_iterator i = text.begin();
271 wxString::const_iterator iMax = text.end() - 1; // Let's allow one, last zero though
272
273 int firstNonZeroPos = 0;
274
275 for ( ; i != iMax; ++i )
276 {
277 wxChar c = *i;
278 if ( c != wxS('0') && c != wxS(' ') )
279 break;
280 firstNonZeroPos++;
281 }
282
283 wxString useText = text.substr(firstNonZeroPos, text.length() - firstNonZeroPos);
284
285 wxString variantType = variant.GetType();
286 bool isPrevLong = variantType == wxPG_VARIANT_TYPE_LONG;
287
288 wxLongLong_t value64 = 0;
289
290 if ( useText.ToLongLong(&value64, 10) &&
291 ( value64 >= INT_MAX || value64 <= INT_MIN )
292 )
293 {
294 bool doChangeValue = isPrevLong;
295
296 if ( !isPrevLong && variantType == wxPG_VARIANT_TYPE_LONGLONG )
297 {
298 wxLongLong oldValue = variant.GetLongLong();
299 if ( oldValue.GetValue() != value64 )
300 doChangeValue = true;
301 }
302
303 if ( doChangeValue )
304 {
305 wxLongLong ll(value64);
306 variant = ll;
307 return true;
308 }
309 }
310
311 if ( useText.ToLong( &value32, 0 ) )
312 {
313 if ( !isPrevLong || variant != value32 )
314 {
315 variant = value32;
316 return true;
317 }
318 }
319 }
320 else if ( argFlags & wxPG_REPORT_ERROR )
321 {
322 }
323 return false;
324 }
325
326 bool wxIntProperty::IntToValue( wxVariant& variant, int value, int WXUNUSED(argFlags) ) const
327 {
328 if ( variant.GetType() != wxPG_VARIANT_TYPE_LONG || variant != (long)value )
329 {
330 variant = (long)value;
331 return true;
332 }
333 return false;
334 }
335
336 //
337 // Common validation code to be called in ValidateValue()
338 // implementations.
339 //
340 // Note that 'value' is reference on purpose, so we can write
341 // back to it when mode is wxPG_PROPERTY_VALIDATION_SATURATE.
342 //
343 template<typename T>
344 bool NumericValidation( const wxPGProperty* property,
345 T& value,
346 wxPGValidationInfo* pValidationInfo,
347 int mode,
348 const wxString& strFmt )
349 {
350 T min = (T) wxINT64_MIN;
351 T max = (T) wxINT64_MAX;
352 wxVariant variant;
353 bool minOk = false;
354 bool maxOk = false;
355
356 variant = property->GetAttribute(wxPGGlobalVars->m_strMin);
357 if ( !variant.IsNull() )
358 {
359 variant.Convert(&min);
360 minOk = true;
361 }
362
363 variant = property->GetAttribute(wxPGGlobalVars->m_strMax);
364 if ( !variant.IsNull() )
365 {
366 variant.Convert(&max);
367 maxOk = true;
368 }
369
370 if ( minOk )
371 {
372 if ( value < min )
373 {
374 if ( mode == wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE )
375 {
376 wxString msg;
377 wxString smin = wxString::Format(strFmt, min);
378 wxString smax = wxString::Format(strFmt, max);
379 if ( !maxOk )
380 msg = wxString::Format(
381 _("Value must be %s or higher."),
382 smin.c_str());
383 else
384 msg = wxString::Format(
385 _("Value must be between %s and %s."),
386 smin.c_str(), smax.c_str());
387 pValidationInfo->SetFailureMessage(msg);
388 }
389 else if ( mode == wxPG_PROPERTY_VALIDATION_SATURATE )
390 value = min;
391 else
392 value = max - (min - value);
393 return false;
394 }
395 }
396
397 if ( maxOk )
398 {
399 if ( value > max )
400 {
401 if ( mode == wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE )
402 {
403 wxString msg;
404 wxString smin = wxString::Format(strFmt, min);
405 wxString smax = wxString::Format(strFmt, max);
406 if ( !minOk )
407 msg = wxString::Format(
408 _("Value must be %s or less."),
409 smax.c_str());
410 else
411 msg = wxString::Format(
412 _("Value must be between %s and %s."),
413 smin.c_str(), smax.c_str());
414 pValidationInfo->SetFailureMessage(msg);
415 }
416 else if ( mode == wxPG_PROPERTY_VALIDATION_SATURATE )
417 value = max;
418 else
419 value = min + (value - max);
420 return false;
421 }
422 }
423 return true;
424 }
425
426 bool wxIntProperty::DoValidation( const wxPGProperty* property,
427 wxLongLong_t& value,
428 wxPGValidationInfo* pValidationInfo,
429 int mode )
430 {
431 return NumericValidation<wxLongLong_t>(property,
432 value,
433 pValidationInfo,
434 mode,
435 wxS("%lld"));
436 }
437
438 bool wxIntProperty::ValidateValue( wxVariant& value,
439 wxPGValidationInfo& validationInfo ) const
440 {
441 wxLongLong_t ll = value.GetLongLong().GetValue();
442 return DoValidation(this, ll, &validationInfo,
443 wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE);
444 }
445
446 wxValidator* wxIntProperty::GetClassValidator()
447 {
448 #if wxUSE_VALIDATORS
449 WX_PG_DOGETVALIDATOR_ENTRY()
450
451 wxValidator* validator = new wxNumericPropertyValidator(
452 wxNumericPropertyValidator::Signed);
453
454 WX_PG_DOGETVALIDATOR_EXIT(validator)
455 #else
456 return NULL;
457 #endif
458 }
459
460 wxValidator* wxIntProperty::DoGetValidator() const
461 {
462 return GetClassValidator();
463 }
464
465 // -----------------------------------------------------------------------
466 // wxUIntProperty
467 // -----------------------------------------------------------------------
468
469
470 #define wxPG_UINT_TEMPLATE_MAX 8
471
472 static const wxChar* const gs_uintTemplates32[wxPG_UINT_TEMPLATE_MAX] = {
473 wxT("%x"),wxT("0x%x"),wxT("$%x"),
474 wxT("%X"),wxT("0x%X"),wxT("$%X"),
475 wxT("%u"),wxT("%o")
476 };
477
478 static const char* const gs_uintTemplates64[wxPG_UINT_TEMPLATE_MAX] = {
479 "%" wxLongLongFmtSpec "x",
480 "0x%" wxLongLongFmtSpec "x",
481 "$%" wxLongLongFmtSpec "x",
482 "%" wxLongLongFmtSpec "X",
483 "0x%" wxLongLongFmtSpec "X",
484 "$%" wxLongLongFmtSpec "X",
485 "%" wxLongLongFmtSpec "u",
486 "%" wxLongLongFmtSpec "o"
487 };
488
489 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxUIntProperty,wxPGProperty,
490 long,unsigned long,TextCtrl)
491
492 void wxUIntProperty::Init()
493 {
494 m_base = 6; // This is magic number for dec base (must be same as in setattribute)
495 m_realBase = 10;
496 m_prefix = wxPG_PREFIX_NONE;
497 }
498
499 wxUIntProperty::wxUIntProperty( const wxString& label, const wxString& name,
500 unsigned long value ) : wxPGProperty(label,name)
501 {
502 Init();
503 SetValue((long)value);
504 }
505
506 wxUIntProperty::wxUIntProperty( const wxString& label, const wxString& name,
507 const wxULongLong& value ) : wxPGProperty(label,name)
508 {
509 Init();
510 SetValue(WXVARIANT(value));
511 }
512
513 wxUIntProperty::~wxUIntProperty() { }
514
515 wxString wxUIntProperty::ValueToString( wxVariant& value,
516 int WXUNUSED(argFlags) ) const
517 {
518 size_t index = m_base + m_prefix;
519 if ( index >= wxPG_UINT_TEMPLATE_MAX )
520 index = wxPG_BASE_DEC;
521
522 if ( value.GetType() == wxPG_VARIANT_TYPE_LONG )
523 {
524 return wxString::Format(gs_uintTemplates32[index],
525 (unsigned long)value.GetLong());
526 }
527
528 wxULongLong ull = value.GetULongLong();
529
530 return wxString::Format(gs_uintTemplates64[index], ull.GetValue());
531 }
532
533 bool wxUIntProperty::StringToValue( wxVariant& variant, const wxString& text, int WXUNUSED(argFlags) ) const
534 {
535 wxString variantType = variant.GetType();
536 bool isPrevLong = variantType == wxPG_VARIANT_TYPE_LONG;
537
538 if ( text.length() == 0 )
539 {
540 variant.MakeNull();
541 return true;
542 }
543
544 size_t start = 0;
545 if ( text[0] == wxS('$') )
546 start++;
547
548 wxULongLong_t value64 = 0;
549 wxString s = text.substr(start, text.length() - start);
550
551 if ( s.ToULongLong(&value64, (unsigned int)m_realBase) )
552 {
553 if ( value64 >= LONG_MAX )
554 {
555 bool doChangeValue = isPrevLong;
556
557 if ( !isPrevLong && variantType == wxPG_VARIANT_TYPE_ULONGLONG )
558 {
559 wxULongLong oldValue = variant.GetULongLong();
560 if ( oldValue.GetValue() != value64 )
561 doChangeValue = true;
562 }
563
564 if ( doChangeValue )
565 {
566 variant = wxULongLong(value64);
567 return true;
568 }
569 }
570 else
571 {
572 unsigned long value32 = wxLongLong(value64).GetLo();
573 if ( !isPrevLong || m_value != (long)value32 )
574 {
575 variant = (long)value32;
576 return true;
577 }
578 }
579
580 }
581 return false;
582 }
583
584 bool wxUIntProperty::IntToValue( wxVariant& variant, int number, int WXUNUSED(argFlags) ) const
585 {
586 if ( variant != (long)number )
587 {
588 variant = (long)number;
589 return true;
590 }
591 return false;
592 }
593
594 bool wxUIntProperty::ValidateValue( wxVariant& value, wxPGValidationInfo& validationInfo ) const
595 {
596 wxULongLong_t uul = value.GetULongLong().GetValue();
597 return
598 NumericValidation<wxULongLong_t>(this,
599 uul,
600 &validationInfo,
601 wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE,
602 wxS("%llu"));
603 }
604
605 wxValidator* wxUIntProperty::DoGetValidator() const
606 {
607 #if wxUSE_VALIDATORS
608 WX_PG_DOGETVALIDATOR_ENTRY()
609
610 wxValidator* validator = new wxNumericPropertyValidator(
611 wxNumericPropertyValidator::Unsigned,
612 m_realBase);
613
614 WX_PG_DOGETVALIDATOR_EXIT(validator)
615 #else
616 return NULL;
617 #endif
618 }
619
620 bool wxUIntProperty::DoSetAttribute( const wxString& name, wxVariant& value )
621 {
622 if ( name == wxPG_UINT_BASE )
623 {
624 int val = value.GetLong();
625
626 m_realBase = (wxByte) val;
627 if ( m_realBase > 16 )
628 m_realBase = 16;
629
630 //
631 // Translate logical base to a template array index
632 m_base = 7; // oct
633 if ( val == wxPG_BASE_HEX )
634 m_base = 3;
635 else if ( val == wxPG_BASE_DEC )
636 m_base = 6;
637 else if ( val == wxPG_BASE_HEXL )
638 m_base = 0;
639 return true;
640 }
641 else if ( name == wxPG_UINT_PREFIX )
642 {
643 m_prefix = (wxByte) value.GetLong();
644 return true;
645 }
646 return false;
647 }
648
649 // -----------------------------------------------------------------------
650 // wxFloatProperty
651 // -----------------------------------------------------------------------
652
653 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFloatProperty,wxPGProperty,
654 double,double,TextCtrl)
655
656 wxFloatProperty::wxFloatProperty( const wxString& label,
657 const wxString& name,
658 double value )
659 : wxPGProperty(label,name)
660 {
661 m_precision = -1;
662 SetValue(value);
663 }
664
665 wxFloatProperty::~wxFloatProperty() { }
666
667 // This helper method provides standard way for floating point-using
668 // properties to convert values to string.
669 void wxPropertyGrid::DoubleToString(wxString& target,
670 double value,
671 int precision,
672 bool removeZeroes,
673 wxString* precTemplate)
674 {
675 if ( precision >= 0 )
676 {
677 wxString text1;
678 if (!precTemplate)
679 precTemplate = &text1;
680
681 if ( !precTemplate->length() )
682 {
683 *precTemplate = wxS("%.");
684 *precTemplate << wxString::Format( wxS("%i"), precision );
685 *precTemplate << wxS('f');
686 }
687
688 target.Printf( precTemplate->c_str(), value );
689 }
690 else
691 {
692 target.Printf( wxS("%f"), value );
693 }
694
695 if ( removeZeroes && precision != 0 && target.length() )
696 {
697 // Remove excess zeroes (do not remove this code just yet,
698 // since sprintf can't do the same consistently across platforms).
699 wxString::const_iterator i = target.end() - 1;
700 size_t new_len = target.length() - 1;
701
702 for ( ; i != target.begin(); --i )
703 {
704 if ( *i != wxS('0') )
705 break;
706 new_len--;
707 }
708
709 wxChar cur_char = *i;
710 if ( cur_char != wxS('.') && cur_char != wxS(',') )
711 new_len++;
712
713 if ( new_len != target.length() )
714 target.resize(new_len);
715 }
716 }
717
718 wxString wxFloatProperty::ValueToString( wxVariant& value,
719 int argFlags ) const
720 {
721 wxString text;
722 if ( !value.IsNull() )
723 {
724 wxPropertyGrid::DoubleToString(text,
725 value,
726 m_precision,
727 !(argFlags & wxPG_FULL_VALUE),
728 NULL);
729 }
730 return text;
731 }
732
733 bool wxFloatProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
734 {
735 wxString s;
736 double value;
737
738 if ( text.length() == 0 )
739 {
740 variant.MakeNull();
741 return true;
742 }
743
744 bool res = text.ToDouble(&value);
745 if ( res )
746 {
747 if ( variant != value )
748 {
749 variant = value;
750 return true;
751 }
752 }
753 else if ( argFlags & wxPG_REPORT_ERROR )
754 {
755 }
756 return false;
757 }
758
759 bool wxFloatProperty::DoValidation( const wxPGProperty* property,
760 double& value,
761 wxPGValidationInfo* pValidationInfo,
762 int mode )
763 {
764 return NumericValidation<double>(property,
765 value,
766 pValidationInfo,
767 mode,
768 wxS("%g"));
769 }
770
771 bool
772 wxFloatProperty::ValidateValue( wxVariant& value,
773 wxPGValidationInfo& validationInfo ) const
774 {
775 double fpv = value.GetDouble();
776 return DoValidation(this, fpv, &validationInfo,
777 wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE);
778 }
779
780 bool wxFloatProperty::DoSetAttribute( const wxString& name, wxVariant& value )
781 {
782 if ( name == wxPG_FLOAT_PRECISION )
783 {
784 m_precision = value.GetLong();
785 return true;
786 }
787 return false;
788 }
789
790 wxValidator*
791 wxFloatProperty::GetClassValidator()
792 {
793 #if wxUSE_VALIDATORS
794 WX_PG_DOGETVALIDATOR_ENTRY()
795
796 wxValidator* validator = new wxNumericPropertyValidator(
797 wxNumericPropertyValidator::Float);
798
799 WX_PG_DOGETVALIDATOR_EXIT(validator)
800 #else
801 return NULL;
802 #endif
803 }
804
805 wxValidator* wxFloatProperty::DoGetValidator() const
806 {
807 return GetClassValidator();
808 }
809
810 // -----------------------------------------------------------------------
811 // wxBoolProperty
812 // -----------------------------------------------------------------------
813
814 // We cannot use standard WX_PG_IMPLEMENT_PROPERTY_CLASS macro, since
815 // there is a custom GetEditorClass.
816
817 IMPLEMENT_DYNAMIC_CLASS(wxBoolProperty, wxPGProperty)
818
819 const wxPGEditor* wxBoolProperty::DoGetEditorClass() const
820 {
821 // Select correct editor control.
822 #if wxPG_INCLUDE_CHECKBOX
823 if ( !(m_flags & wxPG_PROP_USE_CHECKBOX) )
824 return wxPGEditor_Choice;
825 return wxPGEditor_CheckBox;
826 #else
827 return wxPGEditor_Choice;
828 #endif
829 }
830
831 wxBoolProperty::wxBoolProperty( const wxString& label, const wxString& name, bool value ) :
832 wxPGProperty(label,name)
833 {
834 m_choices.Assign(wxPGGlobalVars->m_boolChoices);
835
836 SetValue(wxPGVariant_Bool(value));
837
838 m_flags |= wxPG_PROP_USE_DCC;
839 }
840
841 wxBoolProperty::~wxBoolProperty() { }
842
843 wxString wxBoolProperty::ValueToString( wxVariant& value,
844 int argFlags ) const
845 {
846 bool boolValue = value.GetBool();
847
848 // As a fragment of composite string value,
849 // make it a little more readable.
850 if ( argFlags & wxPG_COMPOSITE_FRAGMENT )
851 {
852 if ( boolValue )
853 {
854 return m_label;
855 }
856 else
857 {
858 if ( argFlags & wxPG_UNEDITABLE_COMPOSITE_FRAGMENT )
859 return wxEmptyString;
860
861 wxString notFmt;
862 if ( wxPGGlobalVars->m_autoGetTranslation )
863 notFmt = _("Not %s");
864 else
865 notFmt = wxS("Not %s");
866
867 return wxString::Format(notFmt.c_str(), m_label.c_str());
868 }
869 }
870
871 if ( !(argFlags & wxPG_FULL_VALUE) )
872 {
873 return wxPGGlobalVars->m_boolChoices[boolValue?1:0].GetText();
874 }
875
876 wxString text;
877
878 if ( boolValue ) text = wxS("true");
879 else text = wxS("false");
880
881 return text;
882 }
883
884 bool wxBoolProperty::StringToValue( wxVariant& variant, const wxString& text, int WXUNUSED(argFlags) ) const
885 {
886 bool boolValue = false;
887 if ( text.CmpNoCase(wxPGGlobalVars->m_boolChoices[1].GetText()) == 0 ||
888 text.CmpNoCase(wxS("true")) == 0 ||
889 text.CmpNoCase(m_label) == 0 )
890 boolValue = true;
891
892 if ( text.length() == 0 )
893 {
894 variant.MakeNull();
895 return true;
896 }
897
898 if ( variant != boolValue )
899 {
900 variant = wxPGVariant_Bool(boolValue);
901 return true;
902 }
903 return false;
904 }
905
906 bool wxBoolProperty::IntToValue( wxVariant& variant, int value, int ) const
907 {
908 bool boolValue = value ? true : false;
909
910 if ( variant != boolValue )
911 {
912 variant = wxPGVariant_Bool(boolValue);
913 return true;
914 }
915 return false;
916 }
917
918 bool wxBoolProperty::DoSetAttribute( const wxString& name, wxVariant& value )
919 {
920 #if wxPG_INCLUDE_CHECKBOX
921 if ( name == wxPG_BOOL_USE_CHECKBOX )
922 {
923 if ( value.GetLong() )
924 m_flags |= wxPG_PROP_USE_CHECKBOX;
925 else
926 m_flags &= ~(wxPG_PROP_USE_CHECKBOX);
927 return true;
928 }
929 #endif
930 if ( name == wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING )
931 {
932 if ( value.GetLong() )
933 m_flags |= wxPG_PROP_USE_DCC;
934 else
935 m_flags &= ~(wxPG_PROP_USE_DCC);
936 return true;
937 }
938 return false;
939 }
940
941 // -----------------------------------------------------------------------
942 // wxEnumProperty
943 // -----------------------------------------------------------------------
944
945 IMPLEMENT_DYNAMIC_CLASS(wxEnumProperty, wxPGProperty)
946
947 WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxEnumProperty,long,Choice)
948
949 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
950 const long* values, int value ) : wxPGProperty(label,name)
951 {
952 SetIndex(0);
953
954 if ( labels )
955 {
956 m_choices.Add(labels,values);
957
958 if ( GetItemCount() )
959 SetValue( (long)value );
960 }
961 }
962
963 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
964 const long* values, wxPGChoices* choicesCache, int value )
965 : wxPGProperty(label,name)
966 {
967 SetIndex(0);
968
969 wxASSERT( choicesCache );
970
971 if ( choicesCache->IsOk() )
972 {
973 m_choices.Assign( *choicesCache );
974 m_value = wxPGVariant_Zero;
975 }
976 else if ( labels )
977 {
978 m_choices.Add(labels,values);
979
980 if ( GetItemCount() )
981 SetValue( (long)value );
982 }
983 }
984
985 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name,
986 const wxArrayString& labels, const wxArrayInt& values, int value )
987 : wxPGProperty(label,name)
988 {
989 SetIndex(0);
990
991 if ( &labels && labels.size() )
992 {
993 m_choices.Set(labels, values);
994
995 if ( GetItemCount() )
996 SetValue( (long)value );
997 }
998 }
999
1000 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name,
1001 wxPGChoices& choices, int value )
1002 : wxPGProperty(label,name)
1003 {
1004 m_choices.Assign( choices );
1005
1006 if ( GetItemCount() )
1007 SetValue( (long)value );
1008 }
1009
1010 int wxEnumProperty::GetIndexForValue( int value ) const
1011 {
1012 if ( !m_choices.IsOk() )
1013 return -1;
1014
1015 int intVal = m_choices.Index(value);
1016 if ( intVal >= 0 )
1017 return intVal;
1018
1019 return value;
1020 }
1021
1022 wxEnumProperty::~wxEnumProperty ()
1023 {
1024 }
1025
1026 int wxEnumProperty::ms_nextIndex = -2;
1027
1028 void wxEnumProperty::OnSetValue()
1029 {
1030 wxString variantType = m_value.GetType();
1031
1032 if ( variantType == wxPG_VARIANT_TYPE_LONG )
1033 {
1034 ValueFromInt_( m_value, m_value.GetLong(), wxPG_FULL_VALUE );
1035 }
1036 else if ( variantType == wxPG_VARIANT_TYPE_STRING )
1037 {
1038 ValueFromString_( m_value, m_value.GetString(), 0 );
1039 }
1040 else
1041 {
1042 wxFAIL;
1043 }
1044
1045 if ( ms_nextIndex != -2 )
1046 {
1047 m_index = ms_nextIndex;
1048 ms_nextIndex = -2;
1049 }
1050 }
1051
1052 bool wxEnumProperty::ValidateValue( wxVariant& value, wxPGValidationInfo& WXUNUSED(validationInfo) ) const
1053 {
1054 // Make sure string value is in the list,
1055 // unless property has string as preferred value type
1056 // To reduce code size, use conversion here as well
1057 if ( value.GetType() == wxPG_VARIANT_TYPE_STRING &&
1058 !this->IsKindOf(CLASSINFO(wxEditEnumProperty)) )
1059 return ValueFromString_( value, value.GetString(), wxPG_PROPERTY_SPECIFIC );
1060
1061 return true;
1062 }
1063
1064 wxString wxEnumProperty::ValueToString( wxVariant& value,
1065 int WXUNUSED(argFlags) ) const
1066 {
1067 if ( value.GetType() == wxPG_VARIANT_TYPE_STRING )
1068 return value.GetString();
1069
1070 int index = m_choices.Index(value.GetLong());
1071 if ( index < 0 )
1072 return wxEmptyString;
1073
1074 return m_choices.GetLabel(index);
1075 }
1076
1077 bool wxEnumProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
1078 {
1079 return ValueFromString_( variant, text, argFlags );
1080 }
1081
1082 bool wxEnumProperty::IntToValue( wxVariant& variant, int intVal, int argFlags ) const
1083 {
1084 return ValueFromInt_( variant, intVal, argFlags );
1085 }
1086
1087 bool wxEnumProperty::ValueFromString_( wxVariant& value, const wxString& text, int argFlags ) const
1088 {
1089 int useIndex = -1;
1090 long useValue = 0;
1091
1092 for ( unsigned int i=0; i<m_choices.GetCount(); i++ )
1093 {
1094 const wxString& entryLabel = m_choices.GetLabel(i);
1095 if ( text.CmpNoCase(entryLabel) == 0 )
1096 {
1097 useIndex = (int)i;
1098 useValue = m_choices.GetValue(i);
1099 break;
1100 }
1101 }
1102
1103 bool asText = false;
1104
1105 bool isEdit = this->IsKindOf(CLASSINFO(wxEditEnumProperty));
1106
1107 // If text not any of the choices, store as text instead
1108 // (but only if we are wxEditEnumProperty)
1109 if ( useIndex == -1 && isEdit )
1110 {
1111 asText = true;
1112 }
1113
1114 int setAsNextIndex = -2;
1115
1116 if ( asText )
1117 {
1118 setAsNextIndex = -1;
1119 value = text;
1120 }
1121 else if ( useIndex != GetIndex() )
1122 {
1123 if ( useIndex != -1 )
1124 {
1125 setAsNextIndex = useIndex;
1126 value = (long)useValue;
1127 }
1128 else
1129 {
1130 setAsNextIndex = -1;
1131 value = wxPGVariant_MinusOne;
1132 }
1133 }
1134
1135 if ( setAsNextIndex != -2 )
1136 {
1137 // If wxPG_PROPERTY_SPECIFIC is set, then this is done for
1138 // validation purposes only, and index must not be changed
1139 if ( !(argFlags & wxPG_PROPERTY_SPECIFIC) )
1140 ms_nextIndex = setAsNextIndex;
1141
1142 if ( isEdit || setAsNextIndex != -1 )
1143 return true;
1144 else
1145 return false;
1146 }
1147 return false;
1148 }
1149
1150 bool wxEnumProperty::ValueFromInt_( wxVariant& variant, int intVal, int argFlags ) const
1151 {
1152 // If wxPG_FULL_VALUE is *not* in argFlags, then intVal is index from combo box.
1153 //
1154 ms_nextIndex = -2;
1155
1156 if ( argFlags & wxPG_FULL_VALUE )
1157 {
1158 ms_nextIndex = GetIndexForValue( intVal );
1159 }
1160 else
1161 {
1162 if ( intVal != GetIndex() )
1163 {
1164 ms_nextIndex = intVal;
1165 }
1166 }
1167
1168 if ( ms_nextIndex != -2 )
1169 {
1170 if ( !(argFlags & wxPG_FULL_VALUE) )
1171 intVal = m_choices.GetValue(intVal);
1172
1173 variant = (long)intVal;
1174
1175 return true;
1176 }
1177
1178 return false;
1179 }
1180
1181 void
1182 wxEnumProperty::OnValidationFailure( wxVariant& WXUNUSED(pendingValue) )
1183 {
1184 // Revert index
1185 ResetNextIndex();
1186 }
1187
1188 void wxEnumProperty::SetIndex( int index )
1189 {
1190 ms_nextIndex = -2;
1191 m_index = index;
1192 }
1193
1194 int wxEnumProperty::GetIndex() const
1195 {
1196 if ( m_value.IsNull() )
1197 return -1;
1198
1199 if ( ms_nextIndex != -2 )
1200 return ms_nextIndex;
1201
1202 return m_index;
1203 }
1204
1205 // -----------------------------------------------------------------------
1206 // wxEditEnumProperty
1207 // -----------------------------------------------------------------------
1208
1209 IMPLEMENT_DYNAMIC_CLASS(wxEditEnumProperty, wxPGProperty)
1210
1211 WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxEditEnumProperty,wxString,ComboBox)
1212
1213 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
1214 const long* values, const wxString& value )
1215 : wxEnumProperty(label,name,labels,values,0)
1216 {
1217 SetValue( value );
1218 }
1219
1220 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
1221 const long* values, wxPGChoices* choicesCache, const wxString& value )
1222 : wxEnumProperty(label,name,labels,values,choicesCache,0)
1223 {
1224 SetValue( value );
1225 }
1226
1227 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name,
1228 const wxArrayString& labels, const wxArrayInt& values, const wxString& value )
1229 : wxEnumProperty(label,name,labels,values,0)
1230 {
1231 SetValue( value );
1232 }
1233
1234 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name,
1235 wxPGChoices& choices, const wxString& value )
1236 : wxEnumProperty(label,name,choices,0)
1237 {
1238 SetValue( value );
1239 }
1240
1241 wxEditEnumProperty::~wxEditEnumProperty()
1242 {
1243 }
1244
1245 // -----------------------------------------------------------------------
1246 // wxFlagsProperty
1247 // -----------------------------------------------------------------------
1248
1249 IMPLEMENT_DYNAMIC_CLASS(wxFlagsProperty,wxPGProperty)
1250
1251 WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxFlagsProperty,long,TextCtrl)
1252
1253 void wxFlagsProperty::Init()
1254 {
1255 long value = m_value;
1256
1257 //
1258 // Generate children
1259 //
1260 unsigned int i;
1261
1262 unsigned int prevChildCount = m_children.size();
1263
1264 int oldSel = -1;
1265 if ( prevChildCount )
1266 {
1267 wxPropertyGridPageState* state = GetParentState();
1268
1269 // State safety check (it may be NULL in immediate parent)
1270 wxASSERT( state );
1271
1272 if ( state )
1273 {
1274 wxPGProperty* selected = state->GetSelection();
1275 if ( selected )
1276 {
1277 if ( selected->GetParent() == this )
1278 oldSel = selected->GetIndexInParent();
1279 else if ( selected == this )
1280 oldSel = -2;
1281 }
1282 }
1283 state->DoClearSelection();
1284 }
1285
1286 // Delete old children
1287 for ( i=0; i<prevChildCount; i++ )
1288 delete m_children[i];
1289
1290 m_children.clear();
1291
1292 // Relay wxPG_BOOL_USE_CHECKBOX and wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING
1293 // to child bool property controls.
1294 long attrUseCheckBox = GetAttributeAsLong(wxPG_BOOL_USE_CHECKBOX, 0);
1295 long attrUseDCC = GetAttributeAsLong(wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING,
1296 0);
1297
1298 if ( m_choices.IsOk() )
1299 {
1300 const wxPGChoices& choices = m_choices;
1301
1302 for ( i=0; i<GetItemCount(); i++ )
1303 {
1304 bool child_val;
1305 child_val = ( value & choices.GetValue(i) )?true:false;
1306
1307 wxPGProperty* boolProp;
1308 wxString label = GetLabel(i);
1309
1310 #if wxUSE_INTL
1311 if ( wxPGGlobalVars->m_autoGetTranslation )
1312 {
1313 boolProp = new wxBoolProperty( ::wxGetTranslation(label), label, child_val );
1314 }
1315 else
1316 #endif
1317 {
1318 boolProp = new wxBoolProperty( label, label, child_val );
1319 }
1320 if ( attrUseCheckBox )
1321 boolProp->SetAttribute(wxPG_BOOL_USE_CHECKBOX,
1322 true);
1323 if ( attrUseDCC )
1324 boolProp->SetAttribute(wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING,
1325 true);
1326 AddPrivateChild(boolProp);
1327 }
1328
1329 m_oldChoicesData = m_choices.GetDataPtr();
1330 }
1331
1332 m_oldValue = m_value;
1333
1334 if ( prevChildCount )
1335 SubPropsChanged(oldSel);
1336 }
1337
1338 wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
1339 const wxChar* const* labels, const long* values, long value ) : wxPGProperty(label,name)
1340 {
1341 m_oldChoicesData = NULL;
1342
1343 if ( labels )
1344 {
1345 m_choices.Set(labels,values);
1346
1347 wxASSERT( GetItemCount() );
1348
1349 SetValue( value );
1350 }
1351 else
1352 {
1353 m_value = wxPGVariant_Zero;
1354 }
1355 }
1356
1357 wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
1358 const wxArrayString& labels, const wxArrayInt& values, int value )
1359 : wxPGProperty(label,name)
1360 {
1361 m_oldChoicesData = NULL;
1362
1363 if ( &labels && labels.size() )
1364 {
1365 m_choices.Set(labels,values);
1366
1367 wxASSERT( GetItemCount() );
1368
1369 SetValue( (long)value );
1370 }
1371 else
1372 {
1373 m_value = wxPGVariant_Zero;
1374 }
1375 }
1376
1377 wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
1378 wxPGChoices& choices, long value )
1379 : wxPGProperty(label,name)
1380 {
1381 m_oldChoicesData = NULL;
1382
1383 if ( choices.IsOk() )
1384 {
1385 m_choices.Assign(choices);
1386
1387 wxASSERT( GetItemCount() );
1388
1389 SetValue( value );
1390 }
1391 else
1392 {
1393 m_value = wxPGVariant_Zero;
1394 }
1395 }
1396
1397 wxFlagsProperty::~wxFlagsProperty()
1398 {
1399 }
1400
1401 void wxFlagsProperty::OnSetValue()
1402 {
1403 if ( !m_choices.IsOk() || !GetItemCount() )
1404 {
1405 m_value = wxPGVariant_Zero;
1406 }
1407 else
1408 {
1409 long val = m_value.GetLong();
1410
1411 long fullFlags = 0;
1412
1413 // normalize the value (i.e. remove extra flags)
1414 unsigned int i;
1415 const wxPGChoices& choices = m_choices;
1416 for ( i = 0; i < GetItemCount(); i++ )
1417 {
1418 fullFlags |= choices.GetValue(i);
1419 }
1420
1421 val &= fullFlags;
1422
1423 m_value = val;
1424
1425 // Need to (re)init now?
1426 if ( GetChildCount() != GetItemCount() ||
1427 m_choices.GetDataPtr() != m_oldChoicesData )
1428 {
1429 Init();
1430 }
1431 }
1432
1433 long newFlags = m_value;
1434
1435 if ( newFlags != m_oldValue )
1436 {
1437 // Set child modified states
1438 unsigned int i;
1439 const wxPGChoices& choices = m_choices;
1440 for ( i = 0; i<GetItemCount(); i++ )
1441 {
1442 int flag;
1443
1444 flag = choices.GetValue(i);
1445
1446 if ( (newFlags & flag) != (m_oldValue & flag) )
1447 Item(i)->ChangeFlag( wxPG_PROP_MODIFIED, true );
1448 }
1449
1450 m_oldValue = newFlags;
1451 }
1452 }
1453
1454 wxString wxFlagsProperty::ValueToString( wxVariant& value,
1455 int WXUNUSED(argFlags) ) const
1456 {
1457 wxString text;
1458
1459 if ( !m_choices.IsOk() )
1460 return text;
1461
1462 long flags = value;
1463 unsigned int i;
1464 const wxPGChoices& choices = m_choices;
1465
1466 for ( i = 0; i < GetItemCount(); i++ )
1467 {
1468 int doAdd;
1469 doAdd = ( flags & choices.GetValue(i) );
1470
1471 if ( doAdd )
1472 {
1473 text += choices.GetLabel(i);
1474 text += wxS(", ");
1475 }
1476 }
1477
1478 // remove last comma
1479 if ( text.Len() > 1 )
1480 text.Truncate ( text.Len() - 2 );
1481
1482 return text;
1483 }
1484
1485 // Translate string into flag tokens
1486 bool wxFlagsProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const
1487 {
1488 if ( !m_choices.IsOk() )
1489 return false;
1490
1491 long newFlags = 0;
1492
1493 // semicolons are no longer valid delimeters
1494 WX_PG_TOKENIZER1_BEGIN(text,wxS(','))
1495
1496 if ( token.length() )
1497 {
1498 // Determine which one it is
1499 long bit = IdToBit( token );
1500
1501 if ( bit != -1 )
1502 {
1503 // Changed?
1504 newFlags |= bit;
1505 }
1506 else
1507 {
1508 break;
1509 }
1510 }
1511
1512 WX_PG_TOKENIZER1_END()
1513
1514 if ( variant != (long)newFlags )
1515 {
1516 variant = (long)newFlags;
1517 return true;
1518 }
1519
1520 return false;
1521 }
1522
1523 // Converts string id to a relevant bit.
1524 long wxFlagsProperty::IdToBit( const wxString& id ) const
1525 {
1526 unsigned int i;
1527 for ( i = 0; i < GetItemCount(); i++ )
1528 {
1529 if ( id == GetLabel(i) )
1530 {
1531 return m_choices.GetValue(i);
1532 }
1533 }
1534 return -1;
1535 }
1536
1537 void wxFlagsProperty::RefreshChildren()
1538 {
1539 if ( !m_choices.IsOk() || !GetChildCount() ) return;
1540
1541 int flags = m_value.GetLong();
1542
1543 const wxPGChoices& choices = m_choices;
1544 unsigned int i;
1545 for ( i = 0; i < GetItemCount(); i++ )
1546 {
1547 long flag;
1548
1549 flag = choices.GetValue(i);
1550
1551 long subVal = flags & flag;
1552 wxPGProperty* p = Item(i);
1553
1554 if ( subVal != (m_oldValue & flag) )
1555 p->ChangeFlag( wxPG_PROP_MODIFIED, true );
1556
1557 p->SetValue( subVal?true:false );
1558 }
1559
1560 m_oldValue = flags;
1561 }
1562
1563 wxVariant wxFlagsProperty::ChildChanged( wxVariant& thisValue,
1564 int childIndex,
1565 wxVariant& childValue ) const
1566 {
1567 long oldValue = thisValue.GetLong();
1568 long val = childValue.GetLong();
1569 unsigned long vi = m_choices.GetValue(childIndex);
1570
1571 if ( val )
1572 return (long) (oldValue | vi);
1573
1574 return (long) (oldValue & ~(vi));
1575 }
1576
1577 bool wxFlagsProperty::DoSetAttribute( const wxString& name, wxVariant& value )
1578 {
1579 if ( name == wxPG_BOOL_USE_CHECKBOX ||
1580 name == wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING )
1581 {
1582 for ( size_t i=0; i<GetChildCount(); i++ )
1583 {
1584 Item(i)->SetAttribute(name, value);
1585 }
1586 // Must return false so that the attribute is stored in
1587 // flag property's actual property storage
1588 return false;
1589 }
1590 return false;
1591 }
1592
1593 // -----------------------------------------------------------------------
1594 // wxDirProperty
1595 // -----------------------------------------------------------------------
1596
1597 IMPLEMENT_DYNAMIC_CLASS(wxDirProperty, wxLongStringProperty)
1598
1599 wxDirProperty::wxDirProperty( const wxString& name, const wxString& label, const wxString& value )
1600 : wxLongStringProperty(name,label,value)
1601 {
1602 m_flags |= wxPG_PROP_NO_ESCAPE;
1603 }
1604
1605 wxDirProperty::~wxDirProperty() { }
1606
1607 wxValidator* wxDirProperty::DoGetValidator() const
1608 {
1609 return wxFileProperty::GetClassValidator();
1610 }
1611
1612 bool wxDirProperty::OnButtonClick( wxPropertyGrid* propGrid, wxString& value )
1613 {
1614 // Update property value from editor, if necessary
1615 wxSize dlg_sz(300,400);
1616
1617 wxString dlgMessage(m_dlgMessage);
1618 if ( dlgMessage.empty() )
1619 dlgMessage = _("Choose a directory:");
1620 wxDirDialog dlg( propGrid,
1621 dlgMessage,
1622 value,
1623 0,
1624 #if !wxPG_SMALL_SCREEN
1625 propGrid->GetGoodEditorDialogPosition(this,dlg_sz),
1626 dlg_sz
1627 #else
1628 wxDefaultPosition,
1629 wxDefaultSize
1630 #endif
1631 );
1632
1633 if ( dlg.ShowModal() == wxID_OK )
1634 {
1635 value = dlg.GetPath();
1636 return true;
1637 }
1638 return false;
1639 }
1640
1641 bool wxDirProperty::DoSetAttribute( const wxString& name, wxVariant& value )
1642 {
1643 if ( name == wxPG_DIR_DIALOG_MESSAGE )
1644 {
1645 m_dlgMessage = value.GetString();
1646 return true;
1647 }
1648 return false;
1649 }
1650
1651 // -----------------------------------------------------------------------
1652 // wxPGFileDialogAdapter
1653 // -----------------------------------------------------------------------
1654
1655 bool wxPGFileDialogAdapter::DoShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
1656 {
1657 wxFileProperty* fileProp = NULL;
1658 wxString path;
1659 int indFilter = -1;
1660
1661 if ( property->IsKindOf(CLASSINFO(wxFileProperty)) )
1662 {
1663 fileProp = ((wxFileProperty*)property);
1664 wxFileName filename = fileProp->GetValue().GetString();
1665 path = filename.GetPath();
1666 indFilter = fileProp->m_indFilter;
1667
1668 if ( !path.length() && fileProp->m_basePath.length() )
1669 path = fileProp->m_basePath;
1670 }
1671 else
1672 {
1673 wxFileName fn(property->GetValue().GetString());
1674 path = fn.GetPath();
1675 }
1676
1677 wxFileDialog dlg( propGrid->GetPanel(),
1678 property->GetAttribute(wxS("DialogTitle"), _("Choose a file")),
1679 property->GetAttribute(wxS("InitialPath"), path),
1680 wxEmptyString,
1681 property->GetAttribute(wxPG_FILE_WILDCARD, _("All files (*.*)|*.*")),
1682 0,
1683 wxDefaultPosition );
1684
1685 if ( indFilter >= 0 )
1686 dlg.SetFilterIndex( indFilter );
1687
1688 if ( dlg.ShowModal() == wxID_OK )
1689 {
1690 if ( fileProp )
1691 fileProp->m_indFilter = dlg.GetFilterIndex();
1692 SetValue( dlg.GetPath() );
1693 return true;
1694 }
1695 return false;
1696 }
1697
1698 // -----------------------------------------------------------------------
1699 // wxFileProperty
1700 // -----------------------------------------------------------------------
1701
1702 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFileProperty,wxPGProperty,
1703 wxString,const wxString&,TextCtrlAndButton)
1704
1705 wxFileProperty::wxFileProperty( const wxString& label, const wxString& name,
1706 const wxString& value ) : wxPGProperty(label,name)
1707 {
1708 m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
1709 m_indFilter = -1;
1710 SetAttribute( wxPG_FILE_WILDCARD, _("All files (*.*)|*.*") );
1711
1712 SetValue(value);
1713 }
1714
1715 wxFileProperty::~wxFileProperty() {}
1716
1717 wxValidator* wxFileProperty::GetClassValidator()
1718 {
1719 #if wxUSE_VALIDATORS
1720 WX_PG_DOGETVALIDATOR_ENTRY()
1721
1722 // Atleast wxPython 2.6.2.1 required that the string argument is given
1723 static wxString v;
1724 wxTextValidator* validator = new wxTextValidator(wxFILTER_EXCLUDE_CHAR_LIST,&v);
1725
1726 wxArrayString exChars;
1727 exChars.Add(wxS("?"));
1728 exChars.Add(wxS("*"));
1729 exChars.Add(wxS("|"));
1730 exChars.Add(wxS("<"));
1731 exChars.Add(wxS(">"));
1732 exChars.Add(wxS("\""));
1733
1734 validator->SetExcludes(exChars);
1735
1736 WX_PG_DOGETVALIDATOR_EXIT(validator)
1737 #else
1738 return NULL;
1739 #endif
1740 }
1741
1742 wxValidator* wxFileProperty::DoGetValidator() const
1743 {
1744 return GetClassValidator();
1745 }
1746
1747 void wxFileProperty::OnSetValue()
1748 {
1749 const wxString& fnstr = m_value.GetString();
1750
1751 wxFileName filename = fnstr;
1752
1753 if ( !filename.HasName() )
1754 {
1755 m_value = wxPGVariant_EmptyString;
1756 }
1757
1758 // Find index for extension.
1759 if ( m_indFilter < 0 && fnstr.length() )
1760 {
1761 wxString ext = filename.GetExt();
1762 int curind = 0;
1763 size_t pos = 0;
1764 size_t len = m_wildcard.length();
1765
1766 pos = m_wildcard.find(wxS("|"), pos);
1767 while ( pos != wxString::npos && pos < (len-3) )
1768 {
1769 size_t ext_begin = pos + 3;
1770
1771 pos = m_wildcard.find(wxS("|"), ext_begin);
1772 if ( pos == wxString::npos )
1773 pos = len;
1774 wxString found_ext = m_wildcard.substr(ext_begin, pos-ext_begin);
1775
1776 if ( found_ext.length() > 0 )
1777 {
1778 if ( found_ext[0] == wxS('*') )
1779 {
1780 m_indFilter = curind;
1781 break;
1782 }
1783 if ( ext.CmpNoCase(found_ext) == 0 )
1784 {
1785 m_indFilter = curind;
1786 break;
1787 }
1788 }
1789
1790 if ( pos != len )
1791 pos = m_wildcard.find(wxS("|"), pos+1);
1792
1793 curind++;
1794 }
1795 }
1796 }
1797
1798 wxFileName wxFileProperty::GetFileName() const
1799 {
1800 wxFileName filename;
1801
1802 if ( !m_value.IsNull() )
1803 filename = m_value.GetString();
1804
1805 return filename;
1806 }
1807
1808 wxString wxFileProperty::ValueToString( wxVariant& value,
1809 int argFlags ) const
1810 {
1811 wxFileName filename = value.GetString();
1812
1813 if ( !filename.HasName() )
1814 return wxEmptyString;
1815
1816 wxString fullName = filename.GetFullName();
1817 if ( !fullName.length() )
1818 return wxEmptyString;
1819
1820 if ( argFlags & wxPG_FULL_VALUE )
1821 {
1822 return filename.GetFullPath();
1823 }
1824 else if ( m_flags & wxPG_PROP_SHOW_FULL_FILENAME )
1825 {
1826 if ( m_basePath.Length() )
1827 {
1828 wxFileName fn2(filename);
1829 fn2.MakeRelativeTo(m_basePath);
1830 return fn2.GetFullPath();
1831 }
1832 return filename.GetFullPath();
1833 }
1834
1835 return filename.GetFullName();
1836 }
1837
1838 wxPGEditorDialogAdapter* wxFileProperty::GetEditorDialog() const
1839 {
1840 return new wxPGFileDialogAdapter();
1841 }
1842
1843 bool wxFileProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
1844 {
1845 wxFileName filename = variant.GetString();
1846
1847 if ( (m_flags & wxPG_PROP_SHOW_FULL_FILENAME) || (argFlags & wxPG_FULL_VALUE) )
1848 {
1849 if ( filename != text )
1850 {
1851 variant = text;
1852 return true;
1853 }
1854 }
1855 else
1856 {
1857 if ( filename.GetFullName() != text )
1858 {
1859 wxFileName fn = filename;
1860 fn.SetFullName(text);
1861 variant = fn.GetFullPath();
1862 return true;
1863 }
1864 }
1865
1866 return false;
1867 }
1868
1869 bool wxFileProperty::DoSetAttribute( const wxString& name, wxVariant& value )
1870 {
1871 // Return false on some occasions to make sure those attribs will get
1872 // stored in m_attributes.
1873 if ( name == wxPG_FILE_SHOW_FULL_PATH )
1874 {
1875 if ( value.GetLong() )
1876 m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
1877 else
1878 m_flags &= ~(wxPG_PROP_SHOW_FULL_FILENAME);
1879 return true;
1880 }
1881 else if ( name == wxPG_FILE_WILDCARD )
1882 {
1883 m_wildcard = value.GetString();
1884 }
1885 else if ( name == wxPG_FILE_SHOW_RELATIVE_PATH )
1886 {
1887 m_basePath = value.GetString();
1888
1889 // Make sure wxPG_FILE_SHOW_FULL_PATH is also set
1890 m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
1891 }
1892 else if ( name == wxPG_FILE_INITIAL_PATH )
1893 {
1894 m_initialPath = value.GetString();
1895 return true;
1896 }
1897 else if ( name == wxPG_FILE_DIALOG_TITLE )
1898 {
1899 m_dlgTitle = value.GetString();
1900 return true;
1901 }
1902 return false;
1903 }
1904
1905 // -----------------------------------------------------------------------
1906 // wxPGLongStringDialogAdapter
1907 // -----------------------------------------------------------------------
1908
1909 bool wxPGLongStringDialogAdapter::DoShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
1910 {
1911 wxString val1 = property->GetValueAsString(0);
1912 wxString val_orig = val1;
1913
1914 wxString value;
1915 if ( !property->HasFlag(wxPG_PROP_NO_ESCAPE) )
1916 wxPropertyGrid::ExpandEscapeSequences(value, val1);
1917 else
1918 value = wxString(val1);
1919
1920 // Run editor dialog.
1921 if ( wxLongStringProperty::DisplayEditorDialog(property, propGrid, value) )
1922 {
1923 if ( !property->HasFlag(wxPG_PROP_NO_ESCAPE) )
1924 wxPropertyGrid::CreateEscapeSequences(val1,value);
1925 else
1926 val1 = value;
1927
1928 if ( val1 != val_orig )
1929 {
1930 SetValue( val1 );
1931 return true;
1932 }
1933 }
1934 return false;
1935 }
1936
1937 // -----------------------------------------------------------------------
1938 // wxLongStringProperty
1939 // -----------------------------------------------------------------------
1940
1941 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxLongStringProperty,wxPGProperty,
1942 wxString,const wxString&,TextCtrlAndButton)
1943
1944 wxLongStringProperty::wxLongStringProperty( const wxString& label, const wxString& name,
1945 const wxString& value ) : wxPGProperty(label,name)
1946 {
1947 SetValue(value);
1948 }
1949
1950 wxLongStringProperty::~wxLongStringProperty() {}
1951
1952 wxString wxLongStringProperty::ValueToString( wxVariant& value,
1953 int WXUNUSED(argFlags) ) const
1954 {
1955 return value;
1956 }
1957
1958 bool wxLongStringProperty::OnEvent( wxPropertyGrid* propGrid, wxWindow* WXUNUSED(primary),
1959 wxEvent& event )
1960 {
1961 if ( propGrid->IsMainButtonEvent(event) )
1962 {
1963 // Update the value
1964 wxVariant useValue = propGrid->GetUncommittedPropertyValue();
1965
1966 wxString val1 = useValue.GetString();
1967 wxString val_orig = val1;
1968
1969 wxString value;
1970 if ( !(m_flags & wxPG_PROP_NO_ESCAPE) )
1971 wxPropertyGrid::ExpandEscapeSequences(value,val1);
1972 else
1973 value = wxString(val1);
1974
1975 // Run editor dialog.
1976 if ( OnButtonClick(propGrid,value) )
1977 {
1978 if ( !(m_flags & wxPG_PROP_NO_ESCAPE) )
1979 wxPropertyGrid::CreateEscapeSequences(val1,value);
1980 else
1981 val1 = value;
1982
1983 if ( val1 != val_orig )
1984 {
1985 SetValueInEvent( val1 );
1986 return true;
1987 }
1988 }
1989 }
1990 return false;
1991 }
1992
1993 bool wxLongStringProperty::OnButtonClick( wxPropertyGrid* propGrid, wxString& value )
1994 {
1995 return DisplayEditorDialog(this, propGrid, value);
1996 }
1997
1998 bool wxLongStringProperty::DisplayEditorDialog( wxPGProperty* prop, wxPropertyGrid* propGrid, wxString& value )
1999
2000 {
2001 // launch editor dialog
2002 wxDialog* dlg = new wxDialog(propGrid,-1,prop->GetLabel(),wxDefaultPosition,wxDefaultSize,
2003 wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxCLIP_CHILDREN);
2004
2005 dlg->SetFont(propGrid->GetFont()); // To allow entering chars of the same set as the propGrid
2006
2007 // Multi-line text editor dialog.
2008 #if !wxPG_SMALL_SCREEN
2009 const int spacing = 8;
2010 #else
2011 const int spacing = 4;
2012 #endif
2013 wxBoxSizer* topsizer = new wxBoxSizer( wxVERTICAL );
2014 wxBoxSizer* rowsizer = new wxBoxSizer( wxHORIZONTAL );
2015 wxTextCtrl* ed = new wxTextCtrl(dlg,11,value,
2016 wxDefaultPosition,wxDefaultSize,wxTE_MULTILINE);
2017
2018 rowsizer->Add( ed, 1, wxEXPAND|wxALL, spacing );
2019 topsizer->Add( rowsizer, 1, wxEXPAND, 0 );
2020
2021 wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer();
2022 buttonSizer->AddButton(new wxButton(dlg, wxID_OK));
2023 buttonSizer->AddButton(new wxButton(dlg, wxID_CANCEL));
2024 buttonSizer->Realize();
2025 topsizer->Add( buttonSizer, 0,
2026 wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL|wxBOTTOM|wxRIGHT,
2027 spacing );
2028
2029 dlg->SetSizer( topsizer );
2030 topsizer->SetSizeHints( dlg );
2031
2032 #if !wxPG_SMALL_SCREEN
2033 dlg->SetSize(400,300);
2034
2035 dlg->Move( propGrid->GetGoodEditorDialogPosition(prop,dlg->GetSize()) );
2036 #endif
2037
2038 int res = dlg->ShowModal();
2039
2040 if ( res == wxID_OK )
2041 {
2042 value = ed->GetValue();
2043 dlg->Destroy();
2044 return true;
2045 }
2046 dlg->Destroy();
2047 return false;
2048 }
2049
2050 bool wxLongStringProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const
2051 {
2052 if ( variant != text )
2053 {
2054 variant = text;
2055 return true;
2056 }
2057 return false;
2058 }
2059
2060 #if wxUSE_EDITABLELISTBOX
2061
2062 // -----------------------------------------------------------------------
2063 // wxPGArrayEditorDialog
2064 // -----------------------------------------------------------------------
2065
2066 BEGIN_EVENT_TABLE(wxPGArrayEditorDialog, wxDialog)
2067 EVT_IDLE(wxPGArrayEditorDialog::OnIdle)
2068 END_EVENT_TABLE()
2069
2070 IMPLEMENT_ABSTRACT_CLASS(wxPGArrayEditorDialog, wxDialog)
2071
2072 #include "wx/editlbox.h"
2073 #include "wx/listctrl.h"
2074
2075 // -----------------------------------------------------------------------
2076
2077 void wxPGArrayEditorDialog::OnIdle(wxIdleEvent& event)
2078 {
2079 // Repair focus - wxEditableListBox has bitmap buttons, which
2080 // get focus, and lose focus (into the oblivion) when they
2081 // become disabled due to change in control state.
2082
2083 wxWindow* lastFocused = m_lastFocused;
2084 wxWindow* focus = ::wxWindow::FindFocus();
2085
2086 // If last focused control became disabled, set focus back to
2087 // wxEditableListBox
2088 if ( lastFocused && focus != lastFocused &&
2089 lastFocused->GetParent() == m_elbSubPanel &&
2090 !lastFocused->IsEnabled() )
2091 {
2092 m_elb->GetListCtrl()->SetFocus();
2093 }
2094
2095 m_lastFocused = focus;
2096
2097 event.Skip();
2098 }
2099
2100 // -----------------------------------------------------------------------
2101
2102 wxPGArrayEditorDialog::wxPGArrayEditorDialog()
2103 : wxDialog()
2104 {
2105 Init();
2106 }
2107
2108 // -----------------------------------------------------------------------
2109
2110 void wxPGArrayEditorDialog::Init()
2111 {
2112 m_lastFocused = NULL;
2113 m_hasCustomNewAction = false;
2114 m_itemPendingAtIndex = -1;
2115 }
2116
2117 // -----------------------------------------------------------------------
2118
2119 wxPGArrayEditorDialog::wxPGArrayEditorDialog( wxWindow *parent,
2120 const wxString& message,
2121 const wxString& caption,
2122 long style,
2123 const wxPoint& pos,
2124 const wxSize& sz )
2125 : wxDialog()
2126 {
2127 Init();
2128 Create(parent,message,caption,style,pos,sz);
2129 }
2130
2131 // -----------------------------------------------------------------------
2132
2133 bool wxPGArrayEditorDialog::Create( wxWindow *parent,
2134 const wxString& message,
2135 const wxString& caption,
2136 long style,
2137 const wxPoint& pos,
2138 const wxSize& sz )
2139 {
2140 // On wxMAC the dialog shows incorrectly if style is not exactly wxCAPTION
2141 // FIXME: This should be only a temporary fix.
2142 #ifdef __WXMAC__
2143 wxUnusedVar(style);
2144 int useStyle = wxCAPTION;
2145 #else
2146 int useStyle = style;
2147 #endif
2148
2149 bool res = wxDialog::Create(parent, wxID_ANY, caption, pos, sz, useStyle);
2150
2151 SetFont(parent->GetFont()); // To allow entering chars of the same set as the propGrid
2152
2153 #if !wxPG_SMALL_SCREEN
2154 const int spacing = 4;
2155 #else
2156 const int spacing = 3;
2157 #endif
2158
2159 m_modified = false;
2160
2161 wxBoxSizer* topsizer = new wxBoxSizer( wxVERTICAL );
2162
2163 // Message
2164 if ( message.length() )
2165 topsizer->Add( new wxStaticText(this,-1,message),
2166 0, wxALIGN_LEFT|wxALIGN_CENTRE_VERTICAL|wxALL, spacing );
2167
2168 m_elb = new wxEditableListBox(this, wxID_ANY, message,
2169 wxDefaultPosition,
2170 wxDefaultSize,
2171 wxEL_ALLOW_NEW |
2172 wxEL_ALLOW_EDIT |
2173 wxEL_ALLOW_DELETE);
2174
2175 // Populate the list box
2176 wxArrayString arr;
2177 for ( unsigned int i=0; i<ArrayGetCount(); i++ )
2178 arr.push_back(ArrayGet(i));
2179 m_elb->SetStrings(arr);
2180
2181 // Connect event handlers
2182 wxButton* but;
2183 wxListCtrl* lc = m_elb->GetListCtrl();
2184
2185 but = m_elb->GetNewButton();
2186 m_elbSubPanel = but->GetParent();
2187 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2188 wxCommandEventHandler(wxPGArrayEditorDialog::OnAddClick),
2189 NULL, this);
2190
2191 but = m_elb->GetDelButton();
2192 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2193 wxCommandEventHandler(wxPGArrayEditorDialog::OnDeleteClick),
2194 NULL, this);
2195
2196 but = m_elb->GetUpButton();
2197 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2198 wxCommandEventHandler(wxPGArrayEditorDialog::OnUpClick),
2199 NULL, this);
2200
2201 but = m_elb->GetDownButton();
2202 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2203 wxCommandEventHandler(wxPGArrayEditorDialog::OnDownClick),
2204 NULL, this);
2205
2206 lc->Connect(lc->GetId(), wxEVT_COMMAND_LIST_END_LABEL_EDIT,
2207 wxListEventHandler(wxPGArrayEditorDialog::OnEndLabelEdit),
2208 NULL, this);
2209
2210 topsizer->Add( m_elb, 1, wxEXPAND, spacing );
2211
2212 // Standard dialog buttons
2213 wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer();
2214 buttonSizer->AddButton(new wxButton(this, wxID_OK));
2215 buttonSizer->AddButton(new wxButton(this, wxID_CANCEL));
2216 buttonSizer->Realize();
2217 topsizer->Add( buttonSizer, 0,
2218 wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL|wxALL,
2219 spacing );
2220
2221 m_elb->SetFocus();
2222
2223 SetSizer( topsizer );
2224 topsizer->SetSizeHints( this );
2225
2226 #if !wxPG_SMALL_SCREEN
2227 if ( sz.x == wxDefaultSize.x &&
2228 sz.y == wxDefaultSize.y )
2229 SetSize( wxSize(275,360) );
2230 else
2231 SetSize(sz);
2232 #endif
2233
2234 return res;
2235 }
2236
2237 // -----------------------------------------------------------------------
2238
2239 int wxPGArrayEditorDialog::GetSelection() const
2240 {
2241 wxListCtrl* lc = m_elb->GetListCtrl();
2242
2243 int index = lc->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
2244 if ( index == -1 )
2245 return wxNOT_FOUND;
2246
2247 return index;
2248 }
2249
2250 // -----------------------------------------------------------------------
2251
2252 void wxPGArrayEditorDialog::OnAddClick(wxCommandEvent& event)
2253 {
2254 wxListCtrl* lc = m_elb->GetListCtrl();
2255 int newItemIndex = lc->GetItemCount() - 1;
2256
2257 if ( m_hasCustomNewAction )
2258 {
2259 wxString str;
2260 if ( OnCustomNewAction(&str) )
2261 {
2262 if ( ArrayInsert(str, newItemIndex) )
2263 {
2264 lc->InsertItem(newItemIndex, str);
2265 m_modified = true;
2266 }
2267 }
2268
2269 // Do *not* skip the event! We do not want the wxEditableListBox
2270 // to do anything.
2271 }
2272 else
2273 {
2274 m_itemPendingAtIndex = newItemIndex;
2275
2276 event.Skip();
2277 }
2278 }
2279
2280 // -----------------------------------------------------------------------
2281
2282 void wxPGArrayEditorDialog::OnDeleteClick(wxCommandEvent& event)
2283 {
2284 int index = GetSelection();
2285 if ( index >= 0 )
2286 {
2287 ArrayRemoveAt( index );
2288 m_modified = true;
2289 }
2290
2291 event.Skip();
2292 }
2293
2294 // -----------------------------------------------------------------------
2295
2296 void wxPGArrayEditorDialog::OnUpClick(wxCommandEvent& event)
2297 {
2298 int index = GetSelection();
2299 if ( index > 0 )
2300 {
2301 ArraySwap(index-1,index);
2302 m_modified = true;
2303 }
2304
2305 event.Skip();
2306 }
2307
2308 // -----------------------------------------------------------------------
2309
2310 void wxPGArrayEditorDialog::OnDownClick(wxCommandEvent& event)
2311 {
2312 wxListCtrl* lc = m_elb->GetListCtrl();
2313 int index = GetSelection();
2314 int lastStringIndex = lc->GetItemCount() - 1;
2315 if ( index >= 0 && index < lastStringIndex )
2316 {
2317 ArraySwap(index, index+1);
2318 m_modified = true;
2319 }
2320
2321 event.Skip();
2322 }
2323
2324 // -----------------------------------------------------------------------
2325
2326 void wxPGArrayEditorDialog::OnEndLabelEdit(wxListEvent& event)
2327 {
2328 wxString str = event.GetLabel();
2329
2330 if ( m_itemPendingAtIndex >= 0 )
2331 {
2332 // Add a new item
2333 if ( ArrayInsert(str, m_itemPendingAtIndex) )
2334 {
2335 m_modified = true;
2336 }
2337 else
2338 {
2339 // Editable list box doesn't really respect Veto(), but
2340 // it recognizes if no text was added, so we simulate
2341 // Veto() using it.
2342 event.m_item.SetText(wxEmptyString);
2343 m_elb->GetListCtrl()->SetItemText(m_itemPendingAtIndex,
2344 wxEmptyString);
2345
2346 event.Veto();
2347 }
2348 }
2349 else
2350 {
2351 // Change an existing item
2352 int index = GetSelection();
2353 wxASSERT( index != wxNOT_FOUND );
2354 if ( ArraySet(index, str) )
2355 m_modified = true;
2356 else
2357 event.Veto();
2358 }
2359
2360 event.Skip();
2361 }
2362
2363 #endif // wxUSE_EDITABLELISTBOX
2364
2365 // -----------------------------------------------------------------------
2366 // wxPGArrayStringEditorDialog
2367 // -----------------------------------------------------------------------
2368
2369 IMPLEMENT_DYNAMIC_CLASS(wxPGArrayStringEditorDialog, wxPGArrayEditorDialog)
2370
2371 BEGIN_EVENT_TABLE(wxPGArrayStringEditorDialog, wxPGArrayEditorDialog)
2372 END_EVENT_TABLE()
2373
2374 // -----------------------------------------------------------------------
2375
2376 wxString wxPGArrayStringEditorDialog::ArrayGet( size_t index )
2377 {
2378 return m_array[index];
2379 }
2380
2381 size_t wxPGArrayStringEditorDialog::ArrayGetCount()
2382 {
2383 return m_array.size();
2384 }
2385
2386 bool wxPGArrayStringEditorDialog::ArrayInsert( const wxString& str, int index )
2387 {
2388 if (index<0)
2389 m_array.Add(str);
2390 else
2391 m_array.Insert(str,index);
2392 return true;
2393 }
2394
2395 bool wxPGArrayStringEditorDialog::ArraySet( size_t index, const wxString& str )
2396 {
2397 m_array[index] = str;
2398 return true;
2399 }
2400
2401 void wxPGArrayStringEditorDialog::ArrayRemoveAt( int index )
2402 {
2403 m_array.RemoveAt(index);
2404 }
2405
2406 void wxPGArrayStringEditorDialog::ArraySwap( size_t first, size_t second )
2407 {
2408 wxString old_str = m_array[first];
2409 wxString new_str = m_array[second];
2410 m_array[first] = new_str;
2411 m_array[second] = old_str;
2412 }
2413
2414 wxPGArrayStringEditorDialog::wxPGArrayStringEditorDialog()
2415 : wxPGArrayEditorDialog()
2416 {
2417 Init();
2418 }
2419
2420 void wxPGArrayStringEditorDialog::Init()
2421 {
2422 m_pCallingClass = NULL;
2423 }
2424
2425 bool
2426 wxPGArrayStringEditorDialog::OnCustomNewAction(wxString* resString)
2427 {
2428 return m_pCallingClass->OnCustomStringEdit(m_parent, *resString);
2429 }
2430
2431 // -----------------------------------------------------------------------
2432 // wxArrayStringProperty
2433 // -----------------------------------------------------------------------
2434
2435 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxArrayStringProperty, // Property name
2436 wxPGProperty, // Property we inherit from
2437 wxArrayString, // Value type name
2438 const wxArrayString&, // Value type, as given in constructor
2439 TextCtrlAndButton) // Initial editor
2440
2441 wxArrayStringProperty::wxArrayStringProperty( const wxString& label,
2442 const wxString& name,
2443 const wxArrayString& array )
2444 : wxPGProperty(label,name)
2445 {
2446 m_delimiter = ',';
2447 SetValue( array );
2448 }
2449
2450 wxArrayStringProperty::~wxArrayStringProperty() { }
2451
2452 void wxArrayStringProperty::OnSetValue()
2453 {
2454 GenerateValueAsString();
2455 }
2456
2457 void
2458 wxArrayStringProperty::ConvertArrayToString(const wxArrayString& arr,
2459 wxString* pString,
2460 const wxUniChar& delimiter) const
2461 {
2462 if ( delimiter == '"' || delimiter == '\'' )
2463 {
2464 // Quoted strings
2465 ArrayStringToString(*pString,
2466 arr,
2467 delimiter,
2468 Escape | QuoteStrings);
2469 }
2470 else
2471 {
2472 // Regular delimiter
2473 ArrayStringToString(*pString,
2474 arr,
2475 delimiter,
2476 0);
2477 }
2478 }
2479
2480 wxString wxArrayStringProperty::ValueToString( wxVariant& WXUNUSED(value),
2481 int argFlags ) const
2482 {
2483 //
2484 // If this is called from GetValueAsString(), return cached string
2485 if ( argFlags & wxPG_VALUE_IS_CURRENT )
2486 {
2487 return m_display;
2488 }
2489
2490 wxArrayString arr = m_value.GetArrayString();
2491 wxString s;
2492 ConvertArrayToString(arr, &s, m_delimiter);
2493 return s;
2494 }
2495
2496 // Converts wxArrayString to a string separated by delimeters and spaces.
2497 // preDelim is useful for "str1" "str2" style. Set flags to 1 to do slash
2498 // conversion.
2499 void
2500 wxArrayStringProperty::ArrayStringToString( wxString& dst,
2501 const wxArrayString& src,
2502 wxUniChar delimiter, int flags )
2503 {
2504 wxString pdr;
2505 wxString preas;
2506
2507 unsigned int i;
2508 unsigned int itemCount = src.size();
2509
2510 dst.Empty();
2511
2512 if ( flags & Escape )
2513 {
2514 preas = delimiter;
2515 pdr = wxS("\\") + static_cast<wchar_t>(delimiter);
2516 }
2517
2518 if ( itemCount )
2519 dst.append( preas );
2520
2521 wxString delimStr(delimiter);
2522
2523 for ( i = 0; i < itemCount; i++ )
2524 {
2525 wxString str( src.Item(i) );
2526
2527 // Do some character conversion.
2528 // Converts \ to \\ and $delimiter to \$delimiter
2529 // Useful when quoting.
2530 if ( flags & Escape )
2531 {
2532 str.Replace( wxS("\\"), wxS("\\\\"), true );
2533 if ( pdr.length() )
2534 str.Replace( preas, pdr, true );
2535 }
2536
2537 dst.append( str );
2538
2539 if ( i < (itemCount-1) )
2540 {
2541 dst.append( delimStr );
2542 dst.append( wxS(" ") );
2543 dst.append( preas );
2544 }
2545 else if ( flags & QuoteStrings )
2546 dst.append( delimStr );
2547 }
2548 }
2549
2550 void wxArrayStringProperty::GenerateValueAsString()
2551 {
2552 wxArrayString arr = m_value.GetArrayString();
2553 ConvertArrayToString(arr, &m_display, m_delimiter);
2554 }
2555
2556 // Default implementation doesn't do anything.
2557 bool wxArrayStringProperty::OnCustomStringEdit( wxWindow*, wxString& )
2558 {
2559 return false;
2560 }
2561
2562 wxPGArrayEditorDialog* wxArrayStringProperty::CreateEditorDialog()
2563 {
2564 return new wxPGArrayStringEditorDialog();
2565 }
2566
2567 bool wxArrayStringProperty::OnButtonClick( wxPropertyGrid* propGrid,
2568 wxWindow* WXUNUSED(primaryCtrl),
2569 const wxChar* cbt )
2570 {
2571 // Update the value
2572 wxVariant useValue = propGrid->GetUncommittedPropertyValue();
2573
2574 if ( !propGrid->EditorValidate() )
2575 return false;
2576
2577 // Create editor dialog.
2578 wxPGArrayEditorDialog* dlg = CreateEditorDialog();
2579 #if wxUSE_VALIDATORS
2580 wxValidator* validator = GetValidator();
2581 wxPGInDialogValidator dialogValidator;
2582 #endif
2583
2584 wxPGArrayStringEditorDialog* strEdDlg = wxDynamicCast(dlg, wxPGArrayStringEditorDialog);
2585
2586 if ( strEdDlg )
2587 strEdDlg->SetCustomButton(cbt, this);
2588
2589 dlg->SetDialogValue( useValue );
2590 dlg->Create(propGrid, wxEmptyString, m_label);
2591
2592 #if !wxPG_SMALL_SCREEN
2593 dlg->Move( propGrid->GetGoodEditorDialogPosition(this,dlg->GetSize()) );
2594 #endif
2595
2596 bool retVal;
2597
2598 for (;;)
2599 {
2600 retVal = false;
2601
2602 int res = dlg->ShowModal();
2603
2604 if ( res == wxID_OK && dlg->IsModified() )
2605 {
2606 wxVariant value = dlg->GetDialogValue();
2607 if ( !value.IsNull() )
2608 {
2609 wxArrayString actualValue = value.GetArrayString();
2610 wxString tempStr;
2611 ConvertArrayToString(actualValue, &tempStr, m_delimiter);
2612 #if wxUSE_VALIDATORS
2613 if ( dialogValidator.DoValidate(propGrid, validator,
2614 tempStr) )
2615 #endif
2616 {
2617 SetValueInEvent( actualValue );
2618 retVal = true;
2619 break;
2620 }
2621 }
2622 else
2623 break;
2624 }
2625 else
2626 break;
2627 }
2628
2629 delete dlg;
2630
2631 return retVal;
2632 }
2633
2634 bool wxArrayStringProperty::OnEvent( wxPropertyGrid* propGrid,
2635 wxWindow* primary,
2636 wxEvent& event )
2637 {
2638 if ( propGrid->IsMainButtonEvent(event) )
2639 return OnButtonClick(propGrid,primary,(const wxChar*) NULL);
2640 return false;
2641 }
2642
2643 bool wxArrayStringProperty::StringToValue( wxVariant& variant,
2644 const wxString& text, int ) const
2645 {
2646 wxArrayString arr;
2647
2648 if ( m_delimiter == '"' || m_delimiter == '\'' )
2649 {
2650 // Quoted strings
2651 WX_PG_TOKENIZER2_BEGIN(text, m_delimiter)
2652
2653 // Need to replace backslashes with empty characters
2654 // (opposite what is done in ConvertArrayToString()).
2655 token.Replace ( wxS("\\\\"), wxS("\\"), true );
2656
2657 arr.Add( token );
2658
2659 WX_PG_TOKENIZER2_END()
2660 }
2661 else
2662 {
2663 // Regular delimiter
2664 WX_PG_TOKENIZER1_BEGIN(text, m_delimiter)
2665 arr.Add( token );
2666 WX_PG_TOKENIZER1_END()
2667 }
2668
2669 variant = arr;
2670
2671 return true;
2672 }
2673
2674 bool wxArrayStringProperty::DoSetAttribute( const wxString& name, wxVariant& value )
2675 {
2676 if ( name == wxPG_ARRAY_DELIMITER )
2677 {
2678 m_delimiter = value.GetChar();
2679 GenerateValueAsString();
2680 return false;
2681 }
2682 return true;
2683 }
2684
2685 // -----------------------------------------------------------------------
2686 // wxPGInDialogValidator
2687 // -----------------------------------------------------------------------
2688
2689 #if wxUSE_VALIDATORS
2690 bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* propGrid,
2691 wxValidator* validator,
2692 const wxString& value )
2693 {
2694 if ( !validator )
2695 return true;
2696
2697 wxTextCtrl* tc = m_textCtrl;
2698
2699 if ( !tc )
2700 {
2701 {
2702 tc = new wxTextCtrl( propGrid, wxPG_SUBID_TEMP1, wxEmptyString,
2703 wxPoint(30000,30000));
2704 tc->Hide();
2705 }
2706
2707 m_textCtrl = tc;
2708 }
2709
2710 tc->SetValue(value);
2711
2712 validator->SetWindow(tc);
2713 bool res = validator->Validate(propGrid);
2714
2715 return res;
2716 }
2717 #else
2718 bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* WXUNUSED(propGrid),
2719 wxValidator* WXUNUSED(validator),
2720 const wxString& WXUNUSED(value) )
2721 {
2722 return true;
2723 }
2724 #endif
2725
2726 // -----------------------------------------------------------------------
2727
2728 #endif // wxUSE_PROPGRID