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