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