Have wxPropertyGrid::DoubleToString() to also take comma into account as a decimal...
[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 const wxString& 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 // Remove sign from zero
718 if ( target.length() >= 2 && target[0] == wxS('-') )
719 {
720 bool isZero = true;
721 wxString::const_iterator i = target.begin() + 1;
722
723 for ( ; i != target.end(); i++ )
724 {
725 if ( *i != wxS('0') && *i != wxS('.') && *i != wxS(',') )
726 {
727 isZero = false;
728 break;
729 }
730 }
731
732 if ( isZero )
733 target.erase(target.begin());
734 }
735
736 return target;
737 }
738
739 wxString wxFloatProperty::ValueToString( wxVariant& value,
740 int argFlags ) const
741 {
742 wxString text;
743 if ( !value.IsNull() )
744 {
745 wxPropertyGrid::DoubleToString(text,
746 value,
747 m_precision,
748 !(argFlags & wxPG_FULL_VALUE),
749 NULL);
750 }
751 return text;
752 }
753
754 bool wxFloatProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
755 {
756 wxString s;
757 double value;
758
759 if ( text.length() == 0 )
760 {
761 variant.MakeNull();
762 return true;
763 }
764
765 bool res = text.ToDouble(&value);
766 if ( res )
767 {
768 if ( variant != value )
769 {
770 variant = value;
771 return true;
772 }
773 }
774 else if ( argFlags & wxPG_REPORT_ERROR )
775 {
776 }
777 return false;
778 }
779
780 bool wxFloatProperty::DoValidation( const wxPGProperty* property,
781 double& value,
782 wxPGValidationInfo* pValidationInfo,
783 int mode )
784 {
785 return NumericValidation<double>(property,
786 value,
787 pValidationInfo,
788 mode,
789 wxS("%g"));
790 }
791
792 bool
793 wxFloatProperty::ValidateValue( wxVariant& value,
794 wxPGValidationInfo& validationInfo ) const
795 {
796 double fpv = value.GetDouble();
797 return DoValidation(this, fpv, &validationInfo,
798 wxPG_PROPERTY_VALIDATION_ERROR_MESSAGE);
799 }
800
801 bool wxFloatProperty::DoSetAttribute( const wxString& name, wxVariant& value )
802 {
803 if ( name == wxPG_FLOAT_PRECISION )
804 {
805 m_precision = value.GetLong();
806 return true;
807 }
808 return false;
809 }
810
811 wxValidator*
812 wxFloatProperty::GetClassValidator()
813 {
814 #if wxUSE_VALIDATORS
815 WX_PG_DOGETVALIDATOR_ENTRY()
816
817 wxValidator* validator = new wxNumericPropertyValidator(
818 wxNumericPropertyValidator::Float);
819
820 WX_PG_DOGETVALIDATOR_EXIT(validator)
821 #else
822 return NULL;
823 #endif
824 }
825
826 wxValidator* wxFloatProperty::DoGetValidator() const
827 {
828 return GetClassValidator();
829 }
830
831 // -----------------------------------------------------------------------
832 // wxBoolProperty
833 // -----------------------------------------------------------------------
834
835 // We cannot use standard WX_PG_IMPLEMENT_PROPERTY_CLASS macro, since
836 // there is a custom GetEditorClass.
837
838 IMPLEMENT_DYNAMIC_CLASS(wxBoolProperty, wxPGProperty)
839
840 const wxPGEditor* wxBoolProperty::DoGetEditorClass() const
841 {
842 // Select correct editor control.
843 #if wxPG_INCLUDE_CHECKBOX
844 if ( !(m_flags & wxPG_PROP_USE_CHECKBOX) )
845 return wxPGEditor_Choice;
846 return wxPGEditor_CheckBox;
847 #else
848 return wxPGEditor_Choice;
849 #endif
850 }
851
852 wxBoolProperty::wxBoolProperty( const wxString& label, const wxString& name, bool value ) :
853 wxPGProperty(label,name)
854 {
855 m_choices.Assign(wxPGGlobalVars->m_boolChoices);
856
857 SetValue(wxPGVariant_Bool(value));
858
859 m_flags |= wxPG_PROP_USE_DCC;
860 }
861
862 wxBoolProperty::~wxBoolProperty() { }
863
864 wxString wxBoolProperty::ValueToString( wxVariant& value,
865 int argFlags ) const
866 {
867 bool boolValue = value.GetBool();
868
869 // As a fragment of composite string value,
870 // make it a little more readable.
871 if ( argFlags & wxPG_COMPOSITE_FRAGMENT )
872 {
873 if ( boolValue )
874 {
875 return m_label;
876 }
877 else
878 {
879 if ( argFlags & wxPG_UNEDITABLE_COMPOSITE_FRAGMENT )
880 return wxEmptyString;
881
882 wxString notFmt;
883 if ( wxPGGlobalVars->m_autoGetTranslation )
884 notFmt = _("Not %s");
885 else
886 notFmt = wxS("Not %s");
887
888 return wxString::Format(notFmt.c_str(), m_label.c_str());
889 }
890 }
891
892 if ( !(argFlags & wxPG_FULL_VALUE) )
893 {
894 return wxPGGlobalVars->m_boolChoices[boolValue?1:0].GetText();
895 }
896
897 wxString text;
898
899 if ( boolValue ) text = wxS("true");
900 else text = wxS("false");
901
902 return text;
903 }
904
905 bool wxBoolProperty::StringToValue( wxVariant& variant, const wxString& text, int WXUNUSED(argFlags) ) const
906 {
907 bool boolValue = false;
908 if ( text.CmpNoCase(wxPGGlobalVars->m_boolChoices[1].GetText()) == 0 ||
909 text.CmpNoCase(wxS("true")) == 0 ||
910 text.CmpNoCase(m_label) == 0 )
911 boolValue = true;
912
913 if ( text.length() == 0 )
914 {
915 variant.MakeNull();
916 return true;
917 }
918
919 if ( variant != boolValue )
920 {
921 variant = wxPGVariant_Bool(boolValue);
922 return true;
923 }
924 return false;
925 }
926
927 bool wxBoolProperty::IntToValue( wxVariant& variant, int value, int ) const
928 {
929 bool boolValue = value ? true : false;
930
931 if ( variant != boolValue )
932 {
933 variant = wxPGVariant_Bool(boolValue);
934 return true;
935 }
936 return false;
937 }
938
939 bool wxBoolProperty::DoSetAttribute( const wxString& name, wxVariant& value )
940 {
941 #if wxPG_INCLUDE_CHECKBOX
942 if ( name == wxPG_BOOL_USE_CHECKBOX )
943 {
944 if ( value.GetLong() )
945 m_flags |= wxPG_PROP_USE_CHECKBOX;
946 else
947 m_flags &= ~(wxPG_PROP_USE_CHECKBOX);
948 return true;
949 }
950 #endif
951 if ( name == wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING )
952 {
953 if ( value.GetLong() )
954 m_flags |= wxPG_PROP_USE_DCC;
955 else
956 m_flags &= ~(wxPG_PROP_USE_DCC);
957 return true;
958 }
959 return false;
960 }
961
962 // -----------------------------------------------------------------------
963 // wxEnumProperty
964 // -----------------------------------------------------------------------
965
966 IMPLEMENT_DYNAMIC_CLASS(wxEnumProperty, wxPGProperty)
967
968 WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxEnumProperty,long,Choice)
969
970 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
971 const long* values, int value ) : wxPGProperty(label,name)
972 {
973 SetIndex(0);
974
975 if ( labels )
976 {
977 m_choices.Add(labels,values);
978
979 if ( GetItemCount() )
980 SetValue( (long)value );
981 }
982 }
983
984 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
985 const long* values, wxPGChoices* choicesCache, int value )
986 : wxPGProperty(label,name)
987 {
988 SetIndex(0);
989
990 wxASSERT( choicesCache );
991
992 if ( choicesCache->IsOk() )
993 {
994 m_choices.Assign( *choicesCache );
995 m_value = wxPGVariant_Zero;
996 }
997 else if ( labels )
998 {
999 m_choices.Add(labels,values);
1000
1001 if ( GetItemCount() )
1002 SetValue( (long)value );
1003 }
1004 }
1005
1006 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name,
1007 const wxArrayString& labels, const wxArrayInt& values, int value )
1008 : wxPGProperty(label,name)
1009 {
1010 SetIndex(0);
1011
1012 if ( &labels && labels.size() )
1013 {
1014 m_choices.Set(labels, values);
1015
1016 if ( GetItemCount() )
1017 SetValue( (long)value );
1018 }
1019 }
1020
1021 wxEnumProperty::wxEnumProperty( const wxString& label, const wxString& name,
1022 wxPGChoices& choices, int value )
1023 : wxPGProperty(label,name)
1024 {
1025 m_choices.Assign( choices );
1026
1027 if ( GetItemCount() )
1028 SetValue( (long)value );
1029 }
1030
1031 int wxEnumProperty::GetIndexForValue( int value ) const
1032 {
1033 if ( !m_choices.IsOk() )
1034 return -1;
1035
1036 int intVal = m_choices.Index(value);
1037 if ( intVal >= 0 )
1038 return intVal;
1039
1040 return value;
1041 }
1042
1043 wxEnumProperty::~wxEnumProperty ()
1044 {
1045 }
1046
1047 int wxEnumProperty::ms_nextIndex = -2;
1048
1049 void wxEnumProperty::OnSetValue()
1050 {
1051 wxString variantType = m_value.GetType();
1052
1053 if ( variantType == wxPG_VARIANT_TYPE_LONG )
1054 {
1055 ValueFromInt_( m_value, m_value.GetLong(), wxPG_FULL_VALUE );
1056 }
1057 else if ( variantType == wxPG_VARIANT_TYPE_STRING )
1058 {
1059 ValueFromString_( m_value, m_value.GetString(), 0 );
1060 }
1061 else
1062 {
1063 wxFAIL;
1064 }
1065
1066 if ( ms_nextIndex != -2 )
1067 {
1068 m_index = ms_nextIndex;
1069 ms_nextIndex = -2;
1070 }
1071 }
1072
1073 bool wxEnumProperty::ValidateValue( wxVariant& value, wxPGValidationInfo& WXUNUSED(validationInfo) ) const
1074 {
1075 // Make sure string value is in the list,
1076 // unless property has string as preferred value type
1077 // To reduce code size, use conversion here as well
1078 if ( value.GetType() == wxPG_VARIANT_TYPE_STRING &&
1079 !this->IsKindOf(CLASSINFO(wxEditEnumProperty)) )
1080 return ValueFromString_( value, value.GetString(), wxPG_PROPERTY_SPECIFIC );
1081
1082 return true;
1083 }
1084
1085 wxString wxEnumProperty::ValueToString( wxVariant& value,
1086 int WXUNUSED(argFlags) ) const
1087 {
1088 if ( value.GetType() == wxPG_VARIANT_TYPE_STRING )
1089 return value.GetString();
1090
1091 int index = m_choices.Index(value.GetLong());
1092 if ( index < 0 )
1093 return wxEmptyString;
1094
1095 return m_choices.GetLabel(index);
1096 }
1097
1098 bool wxEnumProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
1099 {
1100 return ValueFromString_( variant, text, argFlags );
1101 }
1102
1103 bool wxEnumProperty::IntToValue( wxVariant& variant, int intVal, int argFlags ) const
1104 {
1105 return ValueFromInt_( variant, intVal, argFlags );
1106 }
1107
1108 bool wxEnumProperty::ValueFromString_( wxVariant& value, const wxString& text, int argFlags ) const
1109 {
1110 int useIndex = -1;
1111 long useValue = 0;
1112
1113 for ( unsigned int i=0; i<m_choices.GetCount(); i++ )
1114 {
1115 const wxString& entryLabel = m_choices.GetLabel(i);
1116 if ( text.CmpNoCase(entryLabel) == 0 )
1117 {
1118 useIndex = (int)i;
1119 useValue = m_choices.GetValue(i);
1120 break;
1121 }
1122 }
1123
1124 bool asText = false;
1125
1126 bool isEdit = this->IsKindOf(CLASSINFO(wxEditEnumProperty));
1127
1128 // If text not any of the choices, store as text instead
1129 // (but only if we are wxEditEnumProperty)
1130 if ( useIndex == -1 && isEdit )
1131 {
1132 asText = true;
1133 }
1134
1135 int setAsNextIndex = -2;
1136
1137 if ( asText )
1138 {
1139 setAsNextIndex = -1;
1140 value = text;
1141 }
1142 else if ( useIndex != GetIndex() )
1143 {
1144 if ( useIndex != -1 )
1145 {
1146 setAsNextIndex = useIndex;
1147 value = (long)useValue;
1148 }
1149 else
1150 {
1151 setAsNextIndex = -1;
1152 value = wxPGVariant_MinusOne;
1153 }
1154 }
1155
1156 if ( setAsNextIndex != -2 )
1157 {
1158 // If wxPG_PROPERTY_SPECIFIC is set, then this is done for
1159 // validation purposes only, and index must not be changed
1160 if ( !(argFlags & wxPG_PROPERTY_SPECIFIC) )
1161 ms_nextIndex = setAsNextIndex;
1162
1163 if ( isEdit || setAsNextIndex != -1 )
1164 return true;
1165 else
1166 return false;
1167 }
1168 return false;
1169 }
1170
1171 bool wxEnumProperty::ValueFromInt_( wxVariant& variant, int intVal, int argFlags ) const
1172 {
1173 // If wxPG_FULL_VALUE is *not* in argFlags, then intVal is index from combo box.
1174 //
1175 ms_nextIndex = -2;
1176
1177 if ( argFlags & wxPG_FULL_VALUE )
1178 {
1179 ms_nextIndex = GetIndexForValue( intVal );
1180 }
1181 else
1182 {
1183 if ( intVal != GetIndex() )
1184 {
1185 ms_nextIndex = intVal;
1186 }
1187 }
1188
1189 if ( ms_nextIndex != -2 )
1190 {
1191 if ( !(argFlags & wxPG_FULL_VALUE) )
1192 intVal = m_choices.GetValue(intVal);
1193
1194 variant = (long)intVal;
1195
1196 return true;
1197 }
1198
1199 return false;
1200 }
1201
1202 void
1203 wxEnumProperty::OnValidationFailure( wxVariant& WXUNUSED(pendingValue) )
1204 {
1205 // Revert index
1206 ResetNextIndex();
1207 }
1208
1209 void wxEnumProperty::SetIndex( int index )
1210 {
1211 ms_nextIndex = -2;
1212 m_index = index;
1213 }
1214
1215 int wxEnumProperty::GetIndex() const
1216 {
1217 if ( m_value.IsNull() )
1218 return -1;
1219
1220 if ( ms_nextIndex != -2 )
1221 return ms_nextIndex;
1222
1223 return m_index;
1224 }
1225
1226 // -----------------------------------------------------------------------
1227 // wxEditEnumProperty
1228 // -----------------------------------------------------------------------
1229
1230 IMPLEMENT_DYNAMIC_CLASS(wxEditEnumProperty, wxPGProperty)
1231
1232 WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxEditEnumProperty,wxString,ComboBox)
1233
1234 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
1235 const long* values, const wxString& value )
1236 : wxEnumProperty(label,name,labels,values,0)
1237 {
1238 SetValue( value );
1239 }
1240
1241 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name, const wxChar* const* labels,
1242 const long* values, wxPGChoices* choicesCache, const wxString& value )
1243 : wxEnumProperty(label,name,labels,values,choicesCache,0)
1244 {
1245 SetValue( value );
1246 }
1247
1248 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name,
1249 const wxArrayString& labels, const wxArrayInt& values, const wxString& value )
1250 : wxEnumProperty(label,name,labels,values,0)
1251 {
1252 SetValue( value );
1253 }
1254
1255 wxEditEnumProperty::wxEditEnumProperty( const wxString& label, const wxString& name,
1256 wxPGChoices& choices, const wxString& value )
1257 : wxEnumProperty(label,name,choices,0)
1258 {
1259 SetValue( value );
1260 }
1261
1262 wxEditEnumProperty::~wxEditEnumProperty()
1263 {
1264 }
1265
1266 // -----------------------------------------------------------------------
1267 // wxFlagsProperty
1268 // -----------------------------------------------------------------------
1269
1270 IMPLEMENT_DYNAMIC_CLASS(wxFlagsProperty,wxPGProperty)
1271
1272 WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxFlagsProperty,long,TextCtrl)
1273
1274 void wxFlagsProperty::Init()
1275 {
1276 long value = m_value;
1277
1278 //
1279 // Generate children
1280 //
1281 unsigned int i;
1282
1283 unsigned int prevChildCount = m_children.size();
1284
1285 int oldSel = -1;
1286 if ( prevChildCount )
1287 {
1288 wxPropertyGridPageState* state = GetParentState();
1289
1290 // State safety check (it may be NULL in immediate parent)
1291 wxASSERT( state );
1292
1293 if ( state )
1294 {
1295 wxPGProperty* selected = state->GetSelection();
1296 if ( selected )
1297 {
1298 if ( selected->GetParent() == this )
1299 oldSel = selected->GetIndexInParent();
1300 else if ( selected == this )
1301 oldSel = -2;
1302 }
1303 }
1304 state->DoClearSelection();
1305 }
1306
1307 // Delete old children
1308 for ( i=0; i<prevChildCount; i++ )
1309 delete m_children[i];
1310
1311 m_children.clear();
1312
1313 // Relay wxPG_BOOL_USE_CHECKBOX and wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING
1314 // to child bool property controls.
1315 long attrUseCheckBox = GetAttributeAsLong(wxPG_BOOL_USE_CHECKBOX, 0);
1316 long attrUseDCC = GetAttributeAsLong(wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING,
1317 0);
1318
1319 if ( m_choices.IsOk() )
1320 {
1321 const wxPGChoices& choices = m_choices;
1322
1323 for ( i=0; i<GetItemCount(); i++ )
1324 {
1325 bool child_val;
1326 child_val = ( value & choices.GetValue(i) )?true:false;
1327
1328 wxPGProperty* boolProp;
1329 wxString label = GetLabel(i);
1330
1331 #if wxUSE_INTL
1332 if ( wxPGGlobalVars->m_autoGetTranslation )
1333 {
1334 boolProp = new wxBoolProperty( ::wxGetTranslation(label), label, child_val );
1335 }
1336 else
1337 #endif
1338 {
1339 boolProp = new wxBoolProperty( label, label, child_val );
1340 }
1341 if ( attrUseCheckBox )
1342 boolProp->SetAttribute(wxPG_BOOL_USE_CHECKBOX,
1343 true);
1344 if ( attrUseDCC )
1345 boolProp->SetAttribute(wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING,
1346 true);
1347 AddPrivateChild(boolProp);
1348 }
1349
1350 m_oldChoicesData = m_choices.GetDataPtr();
1351 }
1352
1353 m_oldValue = m_value;
1354
1355 if ( prevChildCount )
1356 SubPropsChanged(oldSel);
1357 }
1358
1359 wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
1360 const wxChar* const* labels, const long* values, long value ) : wxPGProperty(label,name)
1361 {
1362 m_oldChoicesData = NULL;
1363
1364 if ( labels )
1365 {
1366 m_choices.Set(labels,values);
1367
1368 wxASSERT( GetItemCount() );
1369
1370 SetValue( value );
1371 }
1372 else
1373 {
1374 m_value = wxPGVariant_Zero;
1375 }
1376 }
1377
1378 wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
1379 const wxArrayString& labels, const wxArrayInt& values, int value )
1380 : wxPGProperty(label,name)
1381 {
1382 m_oldChoicesData = NULL;
1383
1384 if ( &labels && labels.size() )
1385 {
1386 m_choices.Set(labels,values);
1387
1388 wxASSERT( GetItemCount() );
1389
1390 SetValue( (long)value );
1391 }
1392 else
1393 {
1394 m_value = wxPGVariant_Zero;
1395 }
1396 }
1397
1398 wxFlagsProperty::wxFlagsProperty( const wxString& label, const wxString& name,
1399 wxPGChoices& choices, long value )
1400 : wxPGProperty(label,name)
1401 {
1402 m_oldChoicesData = NULL;
1403
1404 if ( choices.IsOk() )
1405 {
1406 m_choices.Assign(choices);
1407
1408 wxASSERT( GetItemCount() );
1409
1410 SetValue( value );
1411 }
1412 else
1413 {
1414 m_value = wxPGVariant_Zero;
1415 }
1416 }
1417
1418 wxFlagsProperty::~wxFlagsProperty()
1419 {
1420 }
1421
1422 void wxFlagsProperty::OnSetValue()
1423 {
1424 if ( !m_choices.IsOk() || !GetItemCount() )
1425 {
1426 m_value = wxPGVariant_Zero;
1427 }
1428 else
1429 {
1430 long val = m_value.GetLong();
1431
1432 long fullFlags = 0;
1433
1434 // normalize the value (i.e. remove extra flags)
1435 unsigned int i;
1436 const wxPGChoices& choices = m_choices;
1437 for ( i = 0; i < GetItemCount(); i++ )
1438 {
1439 fullFlags |= choices.GetValue(i);
1440 }
1441
1442 val &= fullFlags;
1443
1444 m_value = val;
1445
1446 // Need to (re)init now?
1447 if ( GetChildCount() != GetItemCount() ||
1448 m_choices.GetDataPtr() != m_oldChoicesData )
1449 {
1450 Init();
1451 }
1452 }
1453
1454 long newFlags = m_value;
1455
1456 if ( newFlags != m_oldValue )
1457 {
1458 // Set child modified states
1459 unsigned int i;
1460 const wxPGChoices& choices = m_choices;
1461 for ( i = 0; i<GetItemCount(); i++ )
1462 {
1463 int flag;
1464
1465 flag = choices.GetValue(i);
1466
1467 if ( (newFlags & flag) != (m_oldValue & flag) )
1468 Item(i)->ChangeFlag( wxPG_PROP_MODIFIED, true );
1469 }
1470
1471 m_oldValue = newFlags;
1472 }
1473 }
1474
1475 wxString wxFlagsProperty::ValueToString( wxVariant& value,
1476 int WXUNUSED(argFlags) ) const
1477 {
1478 wxString text;
1479
1480 if ( !m_choices.IsOk() )
1481 return text;
1482
1483 long flags = value;
1484 unsigned int i;
1485 const wxPGChoices& choices = m_choices;
1486
1487 for ( i = 0; i < GetItemCount(); i++ )
1488 {
1489 int doAdd;
1490 doAdd = ( flags & choices.GetValue(i) );
1491
1492 if ( doAdd )
1493 {
1494 text += choices.GetLabel(i);
1495 text += wxS(", ");
1496 }
1497 }
1498
1499 // remove last comma
1500 if ( text.Len() > 1 )
1501 text.Truncate ( text.Len() - 2 );
1502
1503 return text;
1504 }
1505
1506 // Translate string into flag tokens
1507 bool wxFlagsProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const
1508 {
1509 if ( !m_choices.IsOk() )
1510 return false;
1511
1512 long newFlags = 0;
1513
1514 // semicolons are no longer valid delimeters
1515 WX_PG_TOKENIZER1_BEGIN(text,wxS(','))
1516
1517 if ( token.length() )
1518 {
1519 // Determine which one it is
1520 long bit = IdToBit( token );
1521
1522 if ( bit != -1 )
1523 {
1524 // Changed?
1525 newFlags |= bit;
1526 }
1527 else
1528 {
1529 break;
1530 }
1531 }
1532
1533 WX_PG_TOKENIZER1_END()
1534
1535 if ( variant != (long)newFlags )
1536 {
1537 variant = (long)newFlags;
1538 return true;
1539 }
1540
1541 return false;
1542 }
1543
1544 // Converts string id to a relevant bit.
1545 long wxFlagsProperty::IdToBit( const wxString& id ) const
1546 {
1547 unsigned int i;
1548 for ( i = 0; i < GetItemCount(); i++ )
1549 {
1550 if ( id == GetLabel(i) )
1551 {
1552 return m_choices.GetValue(i);
1553 }
1554 }
1555 return -1;
1556 }
1557
1558 void wxFlagsProperty::RefreshChildren()
1559 {
1560 if ( !m_choices.IsOk() || !GetChildCount() ) return;
1561
1562 int flags = m_value.GetLong();
1563
1564 const wxPGChoices& choices = m_choices;
1565 unsigned int i;
1566 for ( i = 0; i < GetItemCount(); i++ )
1567 {
1568 long flag;
1569
1570 flag = choices.GetValue(i);
1571
1572 long subVal = flags & flag;
1573 wxPGProperty* p = Item(i);
1574
1575 if ( subVal != (m_oldValue & flag) )
1576 p->ChangeFlag( wxPG_PROP_MODIFIED, true );
1577
1578 p->SetValue( subVal?true:false );
1579 }
1580
1581 m_oldValue = flags;
1582 }
1583
1584 wxVariant wxFlagsProperty::ChildChanged( wxVariant& thisValue,
1585 int childIndex,
1586 wxVariant& childValue ) const
1587 {
1588 long oldValue = thisValue.GetLong();
1589 long val = childValue.GetLong();
1590 unsigned long vi = m_choices.GetValue(childIndex);
1591
1592 if ( val )
1593 return (long) (oldValue | vi);
1594
1595 return (long) (oldValue & ~(vi));
1596 }
1597
1598 bool wxFlagsProperty::DoSetAttribute( const wxString& name, wxVariant& value )
1599 {
1600 if ( name == wxPG_BOOL_USE_CHECKBOX ||
1601 name == wxPG_BOOL_USE_DOUBLE_CLICK_CYCLING )
1602 {
1603 for ( size_t i=0; i<GetChildCount(); i++ )
1604 {
1605 Item(i)->SetAttribute(name, value);
1606 }
1607 // Must return false so that the attribute is stored in
1608 // flag property's actual property storage
1609 return false;
1610 }
1611 return false;
1612 }
1613
1614 // -----------------------------------------------------------------------
1615 // wxDirProperty
1616 // -----------------------------------------------------------------------
1617
1618 IMPLEMENT_DYNAMIC_CLASS(wxDirProperty, wxLongStringProperty)
1619
1620 wxDirProperty::wxDirProperty( const wxString& name, const wxString& label, const wxString& value )
1621 : wxLongStringProperty(name,label,value)
1622 {
1623 m_flags |= wxPG_PROP_NO_ESCAPE;
1624 }
1625
1626 wxDirProperty::~wxDirProperty() { }
1627
1628 wxValidator* wxDirProperty::DoGetValidator() const
1629 {
1630 return wxFileProperty::GetClassValidator();
1631 }
1632
1633 bool wxDirProperty::OnButtonClick( wxPropertyGrid* propGrid, wxString& value )
1634 {
1635 // Update property value from editor, if necessary
1636 wxSize dlg_sz(300,400);
1637
1638 wxString dlgMessage(m_dlgMessage);
1639 if ( dlgMessage.empty() )
1640 dlgMessage = _("Choose a directory:");
1641 wxDirDialog dlg( propGrid,
1642 dlgMessage,
1643 value,
1644 0,
1645 #if !wxPG_SMALL_SCREEN
1646 propGrid->GetGoodEditorDialogPosition(this,dlg_sz),
1647 dlg_sz
1648 #else
1649 wxDefaultPosition,
1650 wxDefaultSize
1651 #endif
1652 );
1653
1654 if ( dlg.ShowModal() == wxID_OK )
1655 {
1656 value = dlg.GetPath();
1657 return true;
1658 }
1659 return false;
1660 }
1661
1662 bool wxDirProperty::DoSetAttribute( const wxString& name, wxVariant& value )
1663 {
1664 if ( name == wxPG_DIR_DIALOG_MESSAGE )
1665 {
1666 m_dlgMessage = value.GetString();
1667 return true;
1668 }
1669 return false;
1670 }
1671
1672 // -----------------------------------------------------------------------
1673 // wxPGFileDialogAdapter
1674 // -----------------------------------------------------------------------
1675
1676 bool wxPGFileDialogAdapter::DoShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
1677 {
1678 wxFileProperty* fileProp = NULL;
1679 wxString path;
1680 int indFilter = -1;
1681
1682 if ( property->IsKindOf(CLASSINFO(wxFileProperty)) )
1683 {
1684 fileProp = ((wxFileProperty*)property);
1685 wxFileName filename = fileProp->GetValue().GetString();
1686 path = filename.GetPath();
1687 indFilter = fileProp->m_indFilter;
1688
1689 if ( !path.length() && fileProp->m_basePath.length() )
1690 path = fileProp->m_basePath;
1691 }
1692 else
1693 {
1694 wxFileName fn(property->GetValue().GetString());
1695 path = fn.GetPath();
1696 }
1697
1698 wxFileDialog dlg( propGrid->GetPanel(),
1699 property->GetAttribute(wxS("DialogTitle"), _("Choose a file")),
1700 property->GetAttribute(wxS("InitialPath"), path),
1701 wxEmptyString,
1702 property->GetAttribute(wxPG_FILE_WILDCARD, _("All files (*.*)|*.*")),
1703 0,
1704 wxDefaultPosition );
1705
1706 if ( indFilter >= 0 )
1707 dlg.SetFilterIndex( indFilter );
1708
1709 if ( dlg.ShowModal() == wxID_OK )
1710 {
1711 if ( fileProp )
1712 fileProp->m_indFilter = dlg.GetFilterIndex();
1713 SetValue( dlg.GetPath() );
1714 return true;
1715 }
1716 return false;
1717 }
1718
1719 // -----------------------------------------------------------------------
1720 // wxFileProperty
1721 // -----------------------------------------------------------------------
1722
1723 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFileProperty,wxPGProperty,
1724 wxString,const wxString&,TextCtrlAndButton)
1725
1726 wxFileProperty::wxFileProperty( const wxString& label, const wxString& name,
1727 const wxString& value ) : wxPGProperty(label,name)
1728 {
1729 m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
1730 m_indFilter = -1;
1731 SetAttribute( wxPG_FILE_WILDCARD, _("All files (*.*)|*.*") );
1732
1733 SetValue(value);
1734 }
1735
1736 wxFileProperty::~wxFileProperty() {}
1737
1738 wxValidator* wxFileProperty::GetClassValidator()
1739 {
1740 #if wxUSE_VALIDATORS
1741 WX_PG_DOGETVALIDATOR_ENTRY()
1742
1743 // Atleast wxPython 2.6.2.1 required that the string argument is given
1744 static wxString v;
1745 wxTextValidator* validator = new wxTextValidator(wxFILTER_EXCLUDE_CHAR_LIST,&v);
1746
1747 wxArrayString exChars;
1748 exChars.Add(wxS("?"));
1749 exChars.Add(wxS("*"));
1750 exChars.Add(wxS("|"));
1751 exChars.Add(wxS("<"));
1752 exChars.Add(wxS(">"));
1753 exChars.Add(wxS("\""));
1754
1755 validator->SetExcludes(exChars);
1756
1757 WX_PG_DOGETVALIDATOR_EXIT(validator)
1758 #else
1759 return NULL;
1760 #endif
1761 }
1762
1763 wxValidator* wxFileProperty::DoGetValidator() const
1764 {
1765 return GetClassValidator();
1766 }
1767
1768 void wxFileProperty::OnSetValue()
1769 {
1770 const wxString& fnstr = m_value.GetString();
1771
1772 wxFileName filename = fnstr;
1773
1774 if ( !filename.HasName() )
1775 {
1776 m_value = wxPGVariant_EmptyString;
1777 }
1778
1779 // Find index for extension.
1780 if ( m_indFilter < 0 && fnstr.length() )
1781 {
1782 wxString ext = filename.GetExt();
1783 int curind = 0;
1784 size_t pos = 0;
1785 size_t len = m_wildcard.length();
1786
1787 pos = m_wildcard.find(wxS("|"), pos);
1788 while ( pos != wxString::npos && pos < (len-3) )
1789 {
1790 size_t ext_begin = pos + 3;
1791
1792 pos = m_wildcard.find(wxS("|"), ext_begin);
1793 if ( pos == wxString::npos )
1794 pos = len;
1795 wxString found_ext = m_wildcard.substr(ext_begin, pos-ext_begin);
1796
1797 if ( found_ext.length() > 0 )
1798 {
1799 if ( found_ext[0] == wxS('*') )
1800 {
1801 m_indFilter = curind;
1802 break;
1803 }
1804 if ( ext.CmpNoCase(found_ext) == 0 )
1805 {
1806 m_indFilter = curind;
1807 break;
1808 }
1809 }
1810
1811 if ( pos != len )
1812 pos = m_wildcard.find(wxS("|"), pos+1);
1813
1814 curind++;
1815 }
1816 }
1817 }
1818
1819 wxFileName wxFileProperty::GetFileName() const
1820 {
1821 wxFileName filename;
1822
1823 if ( !m_value.IsNull() )
1824 filename = m_value.GetString();
1825
1826 return filename;
1827 }
1828
1829 wxString wxFileProperty::ValueToString( wxVariant& value,
1830 int argFlags ) const
1831 {
1832 wxFileName filename = value.GetString();
1833
1834 if ( !filename.HasName() )
1835 return wxEmptyString;
1836
1837 wxString fullName = filename.GetFullName();
1838 if ( !fullName.length() )
1839 return wxEmptyString;
1840
1841 if ( argFlags & wxPG_FULL_VALUE )
1842 {
1843 return filename.GetFullPath();
1844 }
1845 else if ( m_flags & wxPG_PROP_SHOW_FULL_FILENAME )
1846 {
1847 if ( m_basePath.Length() )
1848 {
1849 wxFileName fn2(filename);
1850 fn2.MakeRelativeTo(m_basePath);
1851 return fn2.GetFullPath();
1852 }
1853 return filename.GetFullPath();
1854 }
1855
1856 return filename.GetFullName();
1857 }
1858
1859 wxPGEditorDialogAdapter* wxFileProperty::GetEditorDialog() const
1860 {
1861 return new wxPGFileDialogAdapter();
1862 }
1863
1864 bool wxFileProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
1865 {
1866 wxFileName filename = variant.GetString();
1867
1868 if ( (m_flags & wxPG_PROP_SHOW_FULL_FILENAME) || (argFlags & wxPG_FULL_VALUE) )
1869 {
1870 if ( filename != text )
1871 {
1872 variant = text;
1873 return true;
1874 }
1875 }
1876 else
1877 {
1878 if ( filename.GetFullName() != text )
1879 {
1880 wxFileName fn = filename;
1881 fn.SetFullName(text);
1882 variant = fn.GetFullPath();
1883 return true;
1884 }
1885 }
1886
1887 return false;
1888 }
1889
1890 bool wxFileProperty::DoSetAttribute( const wxString& name, wxVariant& value )
1891 {
1892 // Return false on some occasions to make sure those attribs will get
1893 // stored in m_attributes.
1894 if ( name == wxPG_FILE_SHOW_FULL_PATH )
1895 {
1896 if ( value.GetLong() )
1897 m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
1898 else
1899 m_flags &= ~(wxPG_PROP_SHOW_FULL_FILENAME);
1900 return true;
1901 }
1902 else if ( name == wxPG_FILE_WILDCARD )
1903 {
1904 m_wildcard = value.GetString();
1905 }
1906 else if ( name == wxPG_FILE_SHOW_RELATIVE_PATH )
1907 {
1908 m_basePath = value.GetString();
1909
1910 // Make sure wxPG_FILE_SHOW_FULL_PATH is also set
1911 m_flags |= wxPG_PROP_SHOW_FULL_FILENAME;
1912 }
1913 else if ( name == wxPG_FILE_INITIAL_PATH )
1914 {
1915 m_initialPath = value.GetString();
1916 return true;
1917 }
1918 else if ( name == wxPG_FILE_DIALOG_TITLE )
1919 {
1920 m_dlgTitle = value.GetString();
1921 return true;
1922 }
1923 return false;
1924 }
1925
1926 // -----------------------------------------------------------------------
1927 // wxPGLongStringDialogAdapter
1928 // -----------------------------------------------------------------------
1929
1930 bool wxPGLongStringDialogAdapter::DoShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
1931 {
1932 wxString val1 = property->GetValueAsString(0);
1933 wxString val_orig = val1;
1934
1935 wxString value;
1936 if ( !property->HasFlag(wxPG_PROP_NO_ESCAPE) )
1937 wxPropertyGrid::ExpandEscapeSequences(value, val1);
1938 else
1939 value = wxString(val1);
1940
1941 // Run editor dialog.
1942 if ( wxLongStringProperty::DisplayEditorDialog(property, propGrid, value) )
1943 {
1944 if ( !property->HasFlag(wxPG_PROP_NO_ESCAPE) )
1945 wxPropertyGrid::CreateEscapeSequences(val1,value);
1946 else
1947 val1 = value;
1948
1949 if ( val1 != val_orig )
1950 {
1951 SetValue( val1 );
1952 return true;
1953 }
1954 }
1955 return false;
1956 }
1957
1958 // -----------------------------------------------------------------------
1959 // wxLongStringProperty
1960 // -----------------------------------------------------------------------
1961
1962 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxLongStringProperty,wxPGProperty,
1963 wxString,const wxString&,TextCtrlAndButton)
1964
1965 wxLongStringProperty::wxLongStringProperty( const wxString& label, const wxString& name,
1966 const wxString& value ) : wxPGProperty(label,name)
1967 {
1968 SetValue(value);
1969 }
1970
1971 wxLongStringProperty::~wxLongStringProperty() {}
1972
1973 wxString wxLongStringProperty::ValueToString( wxVariant& value,
1974 int WXUNUSED(argFlags) ) const
1975 {
1976 return value;
1977 }
1978
1979 bool wxLongStringProperty::OnEvent( wxPropertyGrid* propGrid, wxWindow* WXUNUSED(primary),
1980 wxEvent& event )
1981 {
1982 if ( propGrid->IsMainButtonEvent(event) )
1983 {
1984 // Update the value
1985 wxVariant useValue = propGrid->GetUncommittedPropertyValue();
1986
1987 wxString val1 = useValue.GetString();
1988 wxString val_orig = val1;
1989
1990 wxString value;
1991 if ( !(m_flags & wxPG_PROP_NO_ESCAPE) )
1992 wxPropertyGrid::ExpandEscapeSequences(value,val1);
1993 else
1994 value = wxString(val1);
1995
1996 // Run editor dialog.
1997 if ( OnButtonClick(propGrid,value) )
1998 {
1999 if ( !(m_flags & wxPG_PROP_NO_ESCAPE) )
2000 wxPropertyGrid::CreateEscapeSequences(val1,value);
2001 else
2002 val1 = value;
2003
2004 if ( val1 != val_orig )
2005 {
2006 SetValueInEvent( val1 );
2007 return true;
2008 }
2009 }
2010 }
2011 return false;
2012 }
2013
2014 bool wxLongStringProperty::OnButtonClick( wxPropertyGrid* propGrid, wxString& value )
2015 {
2016 return DisplayEditorDialog(this, propGrid, value);
2017 }
2018
2019 bool wxLongStringProperty::DisplayEditorDialog( wxPGProperty* prop, wxPropertyGrid* propGrid, wxString& value )
2020
2021 {
2022 // launch editor dialog
2023 wxDialog* dlg = new wxDialog(propGrid,-1,prop->GetLabel(),wxDefaultPosition,wxDefaultSize,
2024 wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER|wxCLIP_CHILDREN);
2025
2026 dlg->SetFont(propGrid->GetFont()); // To allow entering chars of the same set as the propGrid
2027
2028 // Multi-line text editor dialog.
2029 #if !wxPG_SMALL_SCREEN
2030 const int spacing = 8;
2031 #else
2032 const int spacing = 4;
2033 #endif
2034 wxBoxSizer* topsizer = new wxBoxSizer( wxVERTICAL );
2035 wxBoxSizer* rowsizer = new wxBoxSizer( wxHORIZONTAL );
2036 wxTextCtrl* ed = new wxTextCtrl(dlg,11,value,
2037 wxDefaultPosition,wxDefaultSize,wxTE_MULTILINE);
2038
2039 rowsizer->Add( ed, 1, wxEXPAND|wxALL, spacing );
2040 topsizer->Add( rowsizer, 1, wxEXPAND, 0 );
2041
2042 wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer();
2043 buttonSizer->AddButton(new wxButton(dlg, wxID_OK));
2044 buttonSizer->AddButton(new wxButton(dlg, wxID_CANCEL));
2045 buttonSizer->Realize();
2046 topsizer->Add( buttonSizer, 0,
2047 wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL|wxBOTTOM|wxRIGHT,
2048 spacing );
2049
2050 dlg->SetSizer( topsizer );
2051 topsizer->SetSizeHints( dlg );
2052
2053 #if !wxPG_SMALL_SCREEN
2054 dlg->SetSize(400,300);
2055
2056 dlg->Move( propGrid->GetGoodEditorDialogPosition(prop,dlg->GetSize()) );
2057 #endif
2058
2059 int res = dlg->ShowModal();
2060
2061 if ( res == wxID_OK )
2062 {
2063 value = ed->GetValue();
2064 dlg->Destroy();
2065 return true;
2066 }
2067 dlg->Destroy();
2068 return false;
2069 }
2070
2071 bool wxLongStringProperty::StringToValue( wxVariant& variant, const wxString& text, int ) const
2072 {
2073 if ( variant != text )
2074 {
2075 variant = text;
2076 return true;
2077 }
2078 return false;
2079 }
2080
2081 #if wxUSE_EDITABLELISTBOX
2082
2083 // -----------------------------------------------------------------------
2084 // wxPGArrayEditorDialog
2085 // -----------------------------------------------------------------------
2086
2087 BEGIN_EVENT_TABLE(wxPGArrayEditorDialog, wxDialog)
2088 EVT_IDLE(wxPGArrayEditorDialog::OnIdle)
2089 END_EVENT_TABLE()
2090
2091 IMPLEMENT_ABSTRACT_CLASS(wxPGArrayEditorDialog, wxDialog)
2092
2093 #include "wx/editlbox.h"
2094 #include "wx/listctrl.h"
2095
2096 // -----------------------------------------------------------------------
2097
2098 void wxPGArrayEditorDialog::OnIdle(wxIdleEvent& event)
2099 {
2100 // Repair focus - wxEditableListBox has bitmap buttons, which
2101 // get focus, and lose focus (into the oblivion) when they
2102 // become disabled due to change in control state.
2103
2104 wxWindow* lastFocused = m_lastFocused;
2105 wxWindow* focus = ::wxWindow::FindFocus();
2106
2107 // If last focused control became disabled, set focus back to
2108 // wxEditableListBox
2109 if ( lastFocused && focus != lastFocused &&
2110 lastFocused->GetParent() == m_elbSubPanel &&
2111 !lastFocused->IsEnabled() )
2112 {
2113 m_elb->GetListCtrl()->SetFocus();
2114 }
2115
2116 m_lastFocused = focus;
2117
2118 event.Skip();
2119 }
2120
2121 // -----------------------------------------------------------------------
2122
2123 wxPGArrayEditorDialog::wxPGArrayEditorDialog()
2124 : wxDialog()
2125 {
2126 Init();
2127 }
2128
2129 // -----------------------------------------------------------------------
2130
2131 void wxPGArrayEditorDialog::Init()
2132 {
2133 m_lastFocused = NULL;
2134 m_hasCustomNewAction = false;
2135 m_itemPendingAtIndex = -1;
2136 }
2137
2138 // -----------------------------------------------------------------------
2139
2140 wxPGArrayEditorDialog::wxPGArrayEditorDialog( wxWindow *parent,
2141 const wxString& message,
2142 const wxString& caption,
2143 long style,
2144 const wxPoint& pos,
2145 const wxSize& sz )
2146 : wxDialog()
2147 {
2148 Init();
2149 Create(parent,message,caption,style,pos,sz);
2150 }
2151
2152 // -----------------------------------------------------------------------
2153
2154 bool wxPGArrayEditorDialog::Create( wxWindow *parent,
2155 const wxString& message,
2156 const wxString& caption,
2157 long style,
2158 const wxPoint& pos,
2159 const wxSize& sz )
2160 {
2161 // On wxMAC the dialog shows incorrectly if style is not exactly wxCAPTION
2162 // FIXME: This should be only a temporary fix.
2163 #ifdef __WXMAC__
2164 wxUnusedVar(style);
2165 int useStyle = wxCAPTION;
2166 #else
2167 int useStyle = style;
2168 #endif
2169
2170 bool res = wxDialog::Create(parent, wxID_ANY, caption, pos, sz, useStyle);
2171
2172 SetFont(parent->GetFont()); // To allow entering chars of the same set as the propGrid
2173
2174 #if !wxPG_SMALL_SCREEN
2175 const int spacing = 4;
2176 #else
2177 const int spacing = 3;
2178 #endif
2179
2180 m_modified = false;
2181
2182 wxBoxSizer* topsizer = new wxBoxSizer( wxVERTICAL );
2183
2184 // Message
2185 if ( message.length() )
2186 topsizer->Add( new wxStaticText(this,-1,message),
2187 0, wxALIGN_LEFT|wxALIGN_CENTRE_VERTICAL|wxALL, spacing );
2188
2189 m_elb = new wxEditableListBox(this, wxID_ANY, message,
2190 wxDefaultPosition,
2191 wxDefaultSize,
2192 wxEL_ALLOW_NEW |
2193 wxEL_ALLOW_EDIT |
2194 wxEL_ALLOW_DELETE);
2195
2196 // Populate the list box
2197 wxArrayString arr;
2198 for ( unsigned int i=0; i<ArrayGetCount(); i++ )
2199 arr.push_back(ArrayGet(i));
2200 m_elb->SetStrings(arr);
2201
2202 // Connect event handlers
2203 wxButton* but;
2204 wxListCtrl* lc = m_elb->GetListCtrl();
2205
2206 but = m_elb->GetNewButton();
2207 m_elbSubPanel = but->GetParent();
2208 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2209 wxCommandEventHandler(wxPGArrayEditorDialog::OnAddClick),
2210 NULL, this);
2211
2212 but = m_elb->GetDelButton();
2213 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2214 wxCommandEventHandler(wxPGArrayEditorDialog::OnDeleteClick),
2215 NULL, this);
2216
2217 but = m_elb->GetUpButton();
2218 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2219 wxCommandEventHandler(wxPGArrayEditorDialog::OnUpClick),
2220 NULL, this);
2221
2222 but = m_elb->GetDownButton();
2223 but->Connect(but->GetId(), wxEVT_COMMAND_BUTTON_CLICKED,
2224 wxCommandEventHandler(wxPGArrayEditorDialog::OnDownClick),
2225 NULL, this);
2226
2227 lc->Connect(lc->GetId(), wxEVT_COMMAND_LIST_END_LABEL_EDIT,
2228 wxListEventHandler(wxPGArrayEditorDialog::OnEndLabelEdit),
2229 NULL, this);
2230
2231 topsizer->Add( m_elb, 1, wxEXPAND, spacing );
2232
2233 // Standard dialog buttons
2234 wxStdDialogButtonSizer* buttonSizer = new wxStdDialogButtonSizer();
2235 buttonSizer->AddButton(new wxButton(this, wxID_OK));
2236 buttonSizer->AddButton(new wxButton(this, wxID_CANCEL));
2237 buttonSizer->Realize();
2238 topsizer->Add( buttonSizer, 0,
2239 wxALIGN_RIGHT|wxALIGN_CENTRE_VERTICAL|wxALL,
2240 spacing );
2241
2242 m_elb->SetFocus();
2243
2244 SetSizer( topsizer );
2245 topsizer->SetSizeHints( this );
2246
2247 #if !wxPG_SMALL_SCREEN
2248 if ( sz.x == wxDefaultSize.x &&
2249 sz.y == wxDefaultSize.y )
2250 SetSize( wxSize(275,360) );
2251 else
2252 SetSize(sz);
2253 #endif
2254
2255 return res;
2256 }
2257
2258 // -----------------------------------------------------------------------
2259
2260 int wxPGArrayEditorDialog::GetSelection() const
2261 {
2262 wxListCtrl* lc = m_elb->GetListCtrl();
2263
2264 int index = lc->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
2265 if ( index == -1 )
2266 return wxNOT_FOUND;
2267
2268 return index;
2269 }
2270
2271 // -----------------------------------------------------------------------
2272
2273 void wxPGArrayEditorDialog::OnAddClick(wxCommandEvent& event)
2274 {
2275 wxListCtrl* lc = m_elb->GetListCtrl();
2276 int newItemIndex = lc->GetItemCount() - 1;
2277
2278 if ( m_hasCustomNewAction )
2279 {
2280 wxString str;
2281 if ( OnCustomNewAction(&str) )
2282 {
2283 if ( ArrayInsert(str, newItemIndex) )
2284 {
2285 lc->InsertItem(newItemIndex, str);
2286 m_modified = true;
2287 }
2288 }
2289
2290 // Do *not* skip the event! We do not want the wxEditableListBox
2291 // to do anything.
2292 }
2293 else
2294 {
2295 m_itemPendingAtIndex = newItemIndex;
2296
2297 event.Skip();
2298 }
2299 }
2300
2301 // -----------------------------------------------------------------------
2302
2303 void wxPGArrayEditorDialog::OnDeleteClick(wxCommandEvent& event)
2304 {
2305 int index = GetSelection();
2306 if ( index >= 0 )
2307 {
2308 ArrayRemoveAt( index );
2309 m_modified = true;
2310 }
2311
2312 event.Skip();
2313 }
2314
2315 // -----------------------------------------------------------------------
2316
2317 void wxPGArrayEditorDialog::OnUpClick(wxCommandEvent& event)
2318 {
2319 int index = GetSelection();
2320 if ( index > 0 )
2321 {
2322 ArraySwap(index-1,index);
2323 m_modified = true;
2324 }
2325
2326 event.Skip();
2327 }
2328
2329 // -----------------------------------------------------------------------
2330
2331 void wxPGArrayEditorDialog::OnDownClick(wxCommandEvent& event)
2332 {
2333 wxListCtrl* lc = m_elb->GetListCtrl();
2334 int index = GetSelection();
2335 int lastStringIndex = lc->GetItemCount() - 1;
2336 if ( index >= 0 && index < lastStringIndex )
2337 {
2338 ArraySwap(index, index+1);
2339 m_modified = true;
2340 }
2341
2342 event.Skip();
2343 }
2344
2345 // -----------------------------------------------------------------------
2346
2347 void wxPGArrayEditorDialog::OnEndLabelEdit(wxListEvent& event)
2348 {
2349 wxString str = event.GetLabel();
2350
2351 if ( m_itemPendingAtIndex >= 0 )
2352 {
2353 // Add a new item
2354 if ( ArrayInsert(str, m_itemPendingAtIndex) )
2355 {
2356 m_modified = true;
2357 }
2358 else
2359 {
2360 // Editable list box doesn't really respect Veto(), but
2361 // it recognizes if no text was added, so we simulate
2362 // Veto() using it.
2363 event.m_item.SetText(wxEmptyString);
2364 m_elb->GetListCtrl()->SetItemText(m_itemPendingAtIndex,
2365 wxEmptyString);
2366
2367 event.Veto();
2368 }
2369 }
2370 else
2371 {
2372 // Change an existing item
2373 int index = GetSelection();
2374 wxASSERT( index != wxNOT_FOUND );
2375 if ( ArraySet(index, str) )
2376 m_modified = true;
2377 else
2378 event.Veto();
2379 }
2380
2381 event.Skip();
2382 }
2383
2384 #endif // wxUSE_EDITABLELISTBOX
2385
2386 // -----------------------------------------------------------------------
2387 // wxPGArrayStringEditorDialog
2388 // -----------------------------------------------------------------------
2389
2390 IMPLEMENT_DYNAMIC_CLASS(wxPGArrayStringEditorDialog, wxPGArrayEditorDialog)
2391
2392 BEGIN_EVENT_TABLE(wxPGArrayStringEditorDialog, wxPGArrayEditorDialog)
2393 END_EVENT_TABLE()
2394
2395 // -----------------------------------------------------------------------
2396
2397 wxString wxPGArrayStringEditorDialog::ArrayGet( size_t index )
2398 {
2399 return m_array[index];
2400 }
2401
2402 size_t wxPGArrayStringEditorDialog::ArrayGetCount()
2403 {
2404 return m_array.size();
2405 }
2406
2407 bool wxPGArrayStringEditorDialog::ArrayInsert( const wxString& str, int index )
2408 {
2409 if (index<0)
2410 m_array.Add(str);
2411 else
2412 m_array.Insert(str,index);
2413 return true;
2414 }
2415
2416 bool wxPGArrayStringEditorDialog::ArraySet( size_t index, const wxString& str )
2417 {
2418 m_array[index] = str;
2419 return true;
2420 }
2421
2422 void wxPGArrayStringEditorDialog::ArrayRemoveAt( int index )
2423 {
2424 m_array.RemoveAt(index);
2425 }
2426
2427 void wxPGArrayStringEditorDialog::ArraySwap( size_t first, size_t second )
2428 {
2429 wxString old_str = m_array[first];
2430 wxString new_str = m_array[second];
2431 m_array[first] = new_str;
2432 m_array[second] = old_str;
2433 }
2434
2435 wxPGArrayStringEditorDialog::wxPGArrayStringEditorDialog()
2436 : wxPGArrayEditorDialog()
2437 {
2438 Init();
2439 }
2440
2441 void wxPGArrayStringEditorDialog::Init()
2442 {
2443 m_pCallingClass = NULL;
2444 }
2445
2446 bool
2447 wxPGArrayStringEditorDialog::OnCustomNewAction(wxString* resString)
2448 {
2449 return m_pCallingClass->OnCustomStringEdit(m_parent, *resString);
2450 }
2451
2452 // -----------------------------------------------------------------------
2453 // wxArrayStringProperty
2454 // -----------------------------------------------------------------------
2455
2456 WX_PG_IMPLEMENT_PROPERTY_CLASS(wxArrayStringProperty, // Property name
2457 wxPGProperty, // Property we inherit from
2458 wxArrayString, // Value type name
2459 const wxArrayString&, // Value type, as given in constructor
2460 TextCtrlAndButton) // Initial editor
2461
2462 wxArrayStringProperty::wxArrayStringProperty( const wxString& label,
2463 const wxString& name,
2464 const wxArrayString& array )
2465 : wxPGProperty(label,name)
2466 {
2467 m_delimiter = ',';
2468 SetValue( array );
2469 }
2470
2471 wxArrayStringProperty::~wxArrayStringProperty() { }
2472
2473 void wxArrayStringProperty::OnSetValue()
2474 {
2475 GenerateValueAsString();
2476 }
2477
2478 void
2479 wxArrayStringProperty::ConvertArrayToString(const wxArrayString& arr,
2480 wxString* pString,
2481 const wxUniChar& delimiter) const
2482 {
2483 if ( delimiter == '"' || delimiter == '\'' )
2484 {
2485 // Quoted strings
2486 ArrayStringToString(*pString,
2487 arr,
2488 delimiter,
2489 Escape | QuoteStrings);
2490 }
2491 else
2492 {
2493 // Regular delimiter
2494 ArrayStringToString(*pString,
2495 arr,
2496 delimiter,
2497 0);
2498 }
2499 }
2500
2501 wxString wxArrayStringProperty::ValueToString( wxVariant& WXUNUSED(value),
2502 int argFlags ) const
2503 {
2504 //
2505 // If this is called from GetValueAsString(), return cached string
2506 if ( argFlags & wxPG_VALUE_IS_CURRENT )
2507 {
2508 return m_display;
2509 }
2510
2511 wxArrayString arr = m_value.GetArrayString();
2512 wxString s;
2513 ConvertArrayToString(arr, &s, m_delimiter);
2514 return s;
2515 }
2516
2517 // Converts wxArrayString to a string separated by delimeters and spaces.
2518 // preDelim is useful for "str1" "str2" style. Set flags to 1 to do slash
2519 // conversion.
2520 void
2521 wxArrayStringProperty::ArrayStringToString( wxString& dst,
2522 const wxArrayString& src,
2523 wxUniChar delimiter, int flags )
2524 {
2525 wxString pdr;
2526 wxString preas;
2527
2528 unsigned int i;
2529 unsigned int itemCount = src.size();
2530
2531 dst.Empty();
2532
2533 if ( flags & Escape )
2534 {
2535 preas = delimiter;
2536 pdr = wxS("\\") + static_cast<wchar_t>(delimiter);
2537 }
2538
2539 if ( itemCount )
2540 dst.append( preas );
2541
2542 wxString delimStr(delimiter);
2543
2544 for ( i = 0; i < itemCount; i++ )
2545 {
2546 wxString str( src.Item(i) );
2547
2548 // Do some character conversion.
2549 // Converts \ to \\ and $delimiter to \$delimiter
2550 // Useful when quoting.
2551 if ( flags & Escape )
2552 {
2553 str.Replace( wxS("\\"), wxS("\\\\"), true );
2554 if ( pdr.length() )
2555 str.Replace( preas, pdr, true );
2556 }
2557
2558 dst.append( str );
2559
2560 if ( i < (itemCount-1) )
2561 {
2562 dst.append( delimStr );
2563 dst.append( wxS(" ") );
2564 dst.append( preas );
2565 }
2566 else if ( flags & QuoteStrings )
2567 dst.append( delimStr );
2568 }
2569 }
2570
2571 void wxArrayStringProperty::GenerateValueAsString()
2572 {
2573 wxArrayString arr = m_value.GetArrayString();
2574 ConvertArrayToString(arr, &m_display, m_delimiter);
2575 }
2576
2577 // Default implementation doesn't do anything.
2578 bool wxArrayStringProperty::OnCustomStringEdit( wxWindow*, wxString& )
2579 {
2580 return false;
2581 }
2582
2583 wxPGArrayEditorDialog* wxArrayStringProperty::CreateEditorDialog()
2584 {
2585 return new wxPGArrayStringEditorDialog();
2586 }
2587
2588 bool wxArrayStringProperty::OnButtonClick( wxPropertyGrid* propGrid,
2589 wxWindow* WXUNUSED(primaryCtrl),
2590 const wxChar* cbt )
2591 {
2592 // Update the value
2593 wxVariant useValue = propGrid->GetUncommittedPropertyValue();
2594
2595 if ( !propGrid->EditorValidate() )
2596 return false;
2597
2598 // Create editor dialog.
2599 wxPGArrayEditorDialog* dlg = CreateEditorDialog();
2600 #if wxUSE_VALIDATORS
2601 wxValidator* validator = GetValidator();
2602 wxPGInDialogValidator dialogValidator;
2603 #endif
2604
2605 wxPGArrayStringEditorDialog* strEdDlg = wxDynamicCast(dlg, wxPGArrayStringEditorDialog);
2606
2607 if ( strEdDlg )
2608 strEdDlg->SetCustomButton(cbt, this);
2609
2610 dlg->SetDialogValue( useValue );
2611 dlg->Create(propGrid, wxEmptyString, m_label);
2612
2613 #if !wxPG_SMALL_SCREEN
2614 dlg->Move( propGrid->GetGoodEditorDialogPosition(this,dlg->GetSize()) );
2615 #endif
2616
2617 bool retVal;
2618
2619 for (;;)
2620 {
2621 retVal = false;
2622
2623 int res = dlg->ShowModal();
2624
2625 if ( res == wxID_OK && dlg->IsModified() )
2626 {
2627 wxVariant value = dlg->GetDialogValue();
2628 if ( !value.IsNull() )
2629 {
2630 wxArrayString actualValue = value.GetArrayString();
2631 wxString tempStr;
2632 ConvertArrayToString(actualValue, &tempStr, m_delimiter);
2633 #if wxUSE_VALIDATORS
2634 if ( dialogValidator.DoValidate(propGrid, validator,
2635 tempStr) )
2636 #endif
2637 {
2638 SetValueInEvent( actualValue );
2639 retVal = true;
2640 break;
2641 }
2642 }
2643 else
2644 break;
2645 }
2646 else
2647 break;
2648 }
2649
2650 delete dlg;
2651
2652 return retVal;
2653 }
2654
2655 bool wxArrayStringProperty::OnEvent( wxPropertyGrid* propGrid,
2656 wxWindow* primary,
2657 wxEvent& event )
2658 {
2659 if ( propGrid->IsMainButtonEvent(event) )
2660 return OnButtonClick(propGrid,primary,(const wxChar*) NULL);
2661 return false;
2662 }
2663
2664 bool wxArrayStringProperty::StringToValue( wxVariant& variant,
2665 const wxString& text, int ) const
2666 {
2667 wxArrayString arr;
2668
2669 if ( m_delimiter == '"' || m_delimiter == '\'' )
2670 {
2671 // Quoted strings
2672 WX_PG_TOKENIZER2_BEGIN(text, m_delimiter)
2673
2674 // Need to replace backslashes with empty characters
2675 // (opposite what is done in ConvertArrayToString()).
2676 token.Replace ( wxS("\\\\"), wxS("\\"), true );
2677
2678 arr.Add( token );
2679
2680 WX_PG_TOKENIZER2_END()
2681 }
2682 else
2683 {
2684 // Regular delimiter
2685 WX_PG_TOKENIZER1_BEGIN(text, m_delimiter)
2686 arr.Add( token );
2687 WX_PG_TOKENIZER1_END()
2688 }
2689
2690 variant = arr;
2691
2692 return true;
2693 }
2694
2695 bool wxArrayStringProperty::DoSetAttribute( const wxString& name, wxVariant& value )
2696 {
2697 if ( name == wxPG_ARRAY_DELIMITER )
2698 {
2699 m_delimiter = value.GetChar();
2700 GenerateValueAsString();
2701 return false;
2702 }
2703 return true;
2704 }
2705
2706 // -----------------------------------------------------------------------
2707 // wxPGInDialogValidator
2708 // -----------------------------------------------------------------------
2709
2710 #if wxUSE_VALIDATORS
2711 bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* propGrid,
2712 wxValidator* validator,
2713 const wxString& value )
2714 {
2715 if ( !validator )
2716 return true;
2717
2718 wxTextCtrl* tc = m_textCtrl;
2719
2720 if ( !tc )
2721 {
2722 {
2723 tc = new wxTextCtrl( propGrid, wxPG_SUBID_TEMP1, wxEmptyString,
2724 wxPoint(30000,30000));
2725 tc->Hide();
2726 }
2727
2728 m_textCtrl = tc;
2729 }
2730
2731 tc->SetValue(value);
2732
2733 validator->SetWindow(tc);
2734 bool res = validator->Validate(propGrid);
2735
2736 return res;
2737 }
2738 #else
2739 bool wxPGInDialogValidator::DoValidate( wxPropertyGrid* WXUNUSED(propGrid),
2740 wxValidator* WXUNUSED(validator),
2741 const wxString& WXUNUSED(value) )
2742 {
2743 return true;
2744 }
2745 #endif
2746
2747 // -----------------------------------------------------------------------
2748
2749 #endif // wxUSE_PROPGRID