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