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