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