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