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