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