]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/propgrid/props.cpp
new file added
[wxWidgets.git] / src / propgrid / props.cpp
... / ...
CommitLineData
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
64WX_PG_IMPLEMENT_PROPERTY_CLASS(wxStringProperty,wxPGProperty,
65 wxString,const wxString&,TextCtrl)
66
67wxStringProperty::wxStringProperty( const wxString& label,
68 const wxString& name,
69 const wxString& value )
70 : wxPGProperty(label,name)
71{
72 SetValue(value);
73}
74
75void 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
88wxStringProperty::~wxStringProperty() { }
89
90wxString 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
121bool 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
135bool 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
153wxNumericPropertyValidator::
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
200bool wxNumericPropertyValidator::Validate(wxWindow* parent)
201{
202 if ( !wxTextValidator::Validate(parent) )
203 return false;
204
205 wxWindow* wnd = GetWindow();
206 if ( !wxDynamicCast(wnd, 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
225WX_PG_IMPLEMENT_PROPERTY_CLASS(wxIntProperty,wxPGProperty,
226 long,long,TextCtrl)
227
228wxIntProperty::wxIntProperty( const wxString& label, const wxString& name,
229 long value ) : wxPGProperty(label,name)
230{
231 SetValue(value);
232}
233
234wxIntProperty::wxIntProperty( const wxString& label, const wxString& name,
235 const wxLongLong& value ) : wxPGProperty(label,name)
236{
237 SetValue(WXVARIANT(value));
238}
239
240wxIntProperty::~wxIntProperty() { }
241
242wxString 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
258bool 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
330bool 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//
347template<typename T>
348bool 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
430bool 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
442bool 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
450wxValidator* 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
464wxValidator* wxIntProperty::DoGetValidator() const
465{
466 return GetClassValidator();
467}
468
469// -----------------------------------------------------------------------
470// wxUIntProperty
471// -----------------------------------------------------------------------
472
473
474#define wxPG_UINT_TEMPLATE_MAX 8
475
476static 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
482static 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
493WX_PG_IMPLEMENT_PROPERTY_CLASS(wxUIntProperty,wxPGProperty,
494 long,unsigned long,TextCtrl)
495
496void 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
503wxUIntProperty::wxUIntProperty( const wxString& label, const wxString& name,
504 unsigned long value ) : wxPGProperty(label,name)
505{
506 Init();
507 SetValue((long)value);
508}
509
510wxUIntProperty::wxUIntProperty( const wxString& label, const wxString& name,
511 const wxULongLong& value ) : wxPGProperty(label,name)
512{
513 Init();
514 SetValue(WXVARIANT(value));
515}
516
517wxUIntProperty::~wxUIntProperty() { }
518
519wxString 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
537bool 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
588bool 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
598bool 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
609wxValidator* 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
624bool 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
657WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFloatProperty,wxPGProperty,
658 double,double,TextCtrl)
659
660wxFloatProperty::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
669wxFloatProperty::~wxFloatProperty() { }
670
671// This helper method provides standard way for floating point-using
672// properties to convert values to string.
673const 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
743wxString 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
758bool 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
784bool 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
796bool
797wxFloatProperty::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
805bool 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
815wxValidator*
816wxFloatProperty::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
830wxValidator* 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
842IMPLEMENT_DYNAMIC_CLASS(wxBoolProperty, wxPGProperty)
843
844const 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
856wxBoolProperty::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
866wxBoolProperty::~wxBoolProperty() { }
867
868wxString 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
909bool 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
931bool 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
943bool 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
970IMPLEMENT_DYNAMIC_CLASS(wxEnumProperty, wxPGProperty)
971
972WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxEnumProperty,long,Choice)
973
974wxEnumProperty::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
988wxEnumProperty::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
1010wxEnumProperty::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
1025wxEnumProperty::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
1035int 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
1047wxEnumProperty::~wxEnumProperty ()
1048{
1049}
1050
1051int wxEnumProperty::ms_nextIndex = -2;
1052
1053void 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
1077bool 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 !wxDynamicCastThis(wxEditEnumProperty) )
1084 return ValueFromString_( value, value.GetString(), wxPG_PROPERTY_SPECIFIC );
1085
1086 return true;
1087}
1088
1089wxString 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
1102bool wxEnumProperty::StringToValue( wxVariant& variant, const wxString& text, int argFlags ) const
1103{
1104 return ValueFromString_( variant, text, argFlags );
1105}
1106
1107bool wxEnumProperty::IntToValue( wxVariant& variant, int intVal, int argFlags ) const
1108{
1109 return ValueFromInt_( variant, intVal, argFlags );
1110}
1111
1112bool 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(wxCLASSINFO(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
1175bool 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
1206void
1207wxEnumProperty::OnValidationFailure( wxVariant& WXUNUSED(pendingValue) )
1208{
1209 // Revert index
1210 ResetNextIndex();
1211}
1212
1213void wxEnumProperty::SetIndex( int index )
1214{
1215 ms_nextIndex = -2;
1216 m_index = index;
1217}
1218
1219int 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
1234IMPLEMENT_DYNAMIC_CLASS(wxEditEnumProperty, wxPGProperty)
1235
1236WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxEditEnumProperty,wxString,ComboBox)
1237
1238wxEditEnumProperty::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
1245wxEditEnumProperty::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
1252wxEditEnumProperty::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
1259wxEditEnumProperty::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
1266wxEditEnumProperty::~wxEditEnumProperty()
1267{
1268}
1269
1270// -----------------------------------------------------------------------
1271// wxFlagsProperty
1272// -----------------------------------------------------------------------
1273
1274IMPLEMENT_DYNAMIC_CLASS(wxFlagsProperty,wxPGProperty)
1275
1276WX_PG_IMPLEMENT_PROPERTY_CLASS_PLAIN(wxFlagsProperty,long,TextCtrl)
1277
1278void 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
1363wxFlagsProperty::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
1382wxFlagsProperty::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
1402wxFlagsProperty::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
1422wxFlagsProperty::~wxFlagsProperty()
1423{
1424}
1425
1426void 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
1479wxString 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
1511bool 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.
1549long 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
1562void 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
1588wxVariant 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
1602bool 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
1622IMPLEMENT_DYNAMIC_CLASS(wxDirProperty, wxLongStringProperty)
1623
1624wxDirProperty::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
1630wxDirProperty::~wxDirProperty() { }
1631
1632wxValidator* wxDirProperty::DoGetValidator() const
1633{
1634 return wxFileProperty::GetClassValidator();
1635}
1636
1637bool 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
1666bool 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
1680bool wxPGFileDialogAdapter::DoShowDialog( wxPropertyGrid* propGrid, wxPGProperty* property )
1681{
1682 wxFileProperty* fileProp = NULL;
1683 wxString path;
1684 int indFilter = -1;
1685
1686 if ( wxDynamicCast(property, 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
1727WX_PG_IMPLEMENT_PROPERTY_CLASS(wxFileProperty,wxPGProperty,
1728 wxString,const wxString&,TextCtrlAndButton)
1729
1730wxFileProperty::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
1740wxFileProperty::~wxFileProperty() {}
1741
1742wxValidator* 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
1767wxValidator* wxFileProperty::DoGetValidator() const
1768{
1769 return GetClassValidator();
1770}
1771
1772void 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
1823wxFileName wxFileProperty::GetFileName() const
1824{
1825 wxFileName filename;
1826
1827 if ( !m_value.IsNull() )
1828 filename = m_value.GetString();
1829
1830 return filename;
1831}
1832
1833wxString 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
1863wxPGEditorDialogAdapter* wxFileProperty::GetEditorDialog() const
1864{
1865 return new wxPGFileDialogAdapter();
1866}
1867
1868bool 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
1894bool 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
1934bool 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
1966WX_PG_IMPLEMENT_PROPERTY_CLASS(wxLongStringProperty,wxPGProperty,
1967 wxString,const wxString&,TextCtrlAndButton)
1968
1969wxLongStringProperty::wxLongStringProperty( const wxString& label, const wxString& name,
1970 const wxString& value ) : wxPGProperty(label,name)
1971{
1972 SetValue(value);
1973}
1974
1975wxLongStringProperty::~wxLongStringProperty() {}
1976
1977wxString wxLongStringProperty::ValueToString( wxVariant& value,
1978 int WXUNUSED(argFlags) ) const
1979{
1980 return value;
1981}
1982
1983bool 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
2018bool wxLongStringProperty::OnButtonClick( wxPropertyGrid* propGrid, wxString& value )
2019{
2020 return DisplayEditorDialog(this, propGrid, value);
2021}
2022
2023bool 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
2075bool 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
2091BEGIN_EVENT_TABLE(wxPGArrayEditorDialog, wxDialog)
2092 EVT_IDLE(wxPGArrayEditorDialog::OnIdle)
2093END_EVENT_TABLE()
2094
2095IMPLEMENT_ABSTRACT_CLASS(wxPGArrayEditorDialog, wxDialog)
2096
2097#include "wx/editlbox.h"
2098#include "wx/listctrl.h"
2099
2100// -----------------------------------------------------------------------
2101
2102void 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
2127wxPGArrayEditorDialog::wxPGArrayEditorDialog()
2128 : wxDialog()
2129{
2130 Init();
2131}
2132
2133// -----------------------------------------------------------------------
2134
2135void wxPGArrayEditorDialog::Init()
2136{
2137 m_lastFocused = NULL;
2138 m_hasCustomNewAction = false;
2139 m_itemPendingAtIndex = -1;
2140}
2141
2142// -----------------------------------------------------------------------
2143
2144wxPGArrayEditorDialog::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
2158bool 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
2264int 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
2277void 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
2307void 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
2321void 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
2335void 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
2351void 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
2394IMPLEMENT_DYNAMIC_CLASS(wxPGArrayStringEditorDialog, wxPGArrayEditorDialog)
2395
2396BEGIN_EVENT_TABLE(wxPGArrayStringEditorDialog, wxPGArrayEditorDialog)
2397END_EVENT_TABLE()
2398
2399// -----------------------------------------------------------------------
2400
2401wxString wxPGArrayStringEditorDialog::ArrayGet( size_t index )
2402{
2403 return m_array[index];
2404}
2405
2406size_t wxPGArrayStringEditorDialog::ArrayGetCount()
2407{
2408 return m_array.size();
2409}
2410
2411bool 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
2420bool wxPGArrayStringEditorDialog::ArraySet( size_t index, const wxString& str )
2421{
2422 m_array[index] = str;
2423 return true;
2424}
2425
2426void wxPGArrayStringEditorDialog::ArrayRemoveAt( int index )
2427{
2428 m_array.RemoveAt(index);
2429}
2430
2431void 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
2439wxPGArrayStringEditorDialog::wxPGArrayStringEditorDialog()
2440 : wxPGArrayEditorDialog()
2441{
2442 Init();
2443}
2444
2445void wxPGArrayStringEditorDialog::Init()
2446{
2447 m_pCallingClass = NULL;
2448}
2449
2450bool
2451wxPGArrayStringEditorDialog::OnCustomNewAction(wxString* resString)
2452{
2453 return m_pCallingClass->OnCustomStringEdit(m_parent, *resString);
2454}
2455
2456// -----------------------------------------------------------------------
2457// wxArrayStringProperty
2458// -----------------------------------------------------------------------
2459
2460WX_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
2466wxArrayStringProperty::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
2475wxArrayStringProperty::~wxArrayStringProperty() { }
2476
2477void wxArrayStringProperty::OnSetValue()
2478{
2479 GenerateValueAsString();
2480}
2481
2482void
2483wxArrayStringProperty::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
2505wxString 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.
2524void
2525wxArrayStringProperty::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
2575void 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.
2582bool wxArrayStringProperty::OnCustomStringEdit( wxWindow*, wxString& )
2583{
2584 return false;
2585}
2586
2587wxPGArrayEditorDialog* wxArrayStringProperty::CreateEditorDialog()
2588{
2589 return new wxPGArrayStringEditorDialog();
2590}
2591
2592bool 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
2659bool 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
2668bool 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
2699bool 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
2715bool 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
2743bool 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