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