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