1 /////////////////////////////////////////////////////////////////////////////
2 // Name: configitem.cpp
3 // Purpose: wxWidgets Configuration Tool config item class
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
10 /////////////////////////////////////////////////////////////////////////////
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "configitem.h"
16 // For compilers that support precompilation, includes "wx/wx.h".
17 #include "wx/wxprec.h"
23 #include "wx/tokenzr.h"
24 #include "configitem.h"
25 #include "configtree.h"
26 #include "configtooldoc.h"
27 #include "configtoolview.h"
28 #include "wxconfigtool.h"
29 #include "mainframe.h"
31 IMPLEMENT_CLASS(ctConfigItem
, wxObject
)
33 ctConfigItem::ctConfigItem()
36 m_type
= ctTypeBoolCheck
;
37 m_treeItemId
= wxTreeItemId();
43 ctConfigItem::ctConfigItem(ctConfigItem
* parent
, ctConfigType type
, const wxString
& name
)
47 m_treeItemId
= wxTreeItemId();
53 parent
->AddChild(this);
56 ctConfigItem::~ctConfigItem()
58 ctConfigTreeCtrl
* treeCtrl
= wxGetApp().GetMainFrame()->GetConfigTreeCtrl();
59 if (m_treeItemId
.IsOk() && treeCtrl
)
61 ctTreeItemData
* data
= (ctTreeItemData
*) treeCtrl
->GetItemData(m_treeItemId
);
63 data
->SetConfigItem(NULL
);
66 GetParent()->RemoveChild(this);
69 if (wxGetApp().GetMainFrame()->GetDocument() &&
70 wxGetApp().GetMainFrame()->GetDocument()->GetTopItem() == this)
71 wxGetApp().GetMainFrame()->GetDocument()->SetTopItem(NULL
);
77 /// Can we edit this property?
78 bool ctConfigItem::CanEditProperty(const wxString
& propName
) const
80 ctProperty
* prop
= m_properties
.FindProperty(propName
);
82 return !prop
->GetReadOnly();
87 /// Assignment operator.
88 void ctConfigItem::operator= (const ctConfigItem
& item
)
90 m_properties
= item
.m_properties
;
91 m_modified
= item
.m_modified
;
92 m_defaultProperty
= item
.m_defaultProperty
;
94 m_enabled
= item
.m_enabled
;
95 m_active
= item
.m_active
;
98 /// Sets the name property.
99 void ctConfigItem::SetName(const wxString
& name
)
101 m_properties
.SetProperty(wxT("name"), name
);
105 void ctConfigItem::Clear()
107 wxObjectList::compatibility_iterator node
= m_children
.GetFirst();
110 wxObjectList::compatibility_iterator next
= node
->GetNext();
111 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
113 // This should delete 'node' too, assuming
114 // child's m_parent points to 'this'. If not,
115 // it'll be cleaned up by m_children.Clear().
124 ctConfigItem
* ctConfigItem::GetChild(int n
) const
126 wxASSERT ( n
< GetChildCount() && n
> -1 );
128 if ( n
< GetChildCount() && n
> -1 )
130 ctConfigItem
* child
= wxDynamicCast(m_children
.Item(n
)->GetData(), ctConfigItem
);
137 // Get the child count
138 int ctConfigItem::GetChildCount() const
140 return m_children
.GetCount();
144 void ctConfigItem::AddChild(ctConfigItem
* item
)
146 m_children
.Append(item
);
147 item
->SetParent(this);
150 /// Remove (but don't delete) a child
151 void ctConfigItem::RemoveChild(ctConfigItem
* item
)
153 m_children
.DeleteObject(item
);
154 item
->SetParent(NULL
);
157 /// Initialise standard properties
158 void ctConfigItem::InitProperties()
160 ctProperty
* prop
= m_properties
.FindProperty(wxT("name"));
163 prop
= new ctProperty
;
164 m_properties
.AddProperty(prop
);
166 prop
->SetDescription(_("<B>Name</B><P> The name of the configuration setting."));
167 prop
->SetReadOnly(true);
169 m_properties
.AddProperty(
171 wxT("<B>Description</B><P> The setting description."),
172 wxVariant(wxT(""), wxT("description")),
175 m_properties
.AddProperty(
177 wxT("<B>Default-state</B><P> The default state."),
178 wxVariant(true, wxT("default-state")),
181 if (GetType() == ctTypeString
)
183 m_properties
.AddProperty(
185 wxT("<B>Default-value</B><P> The default value."),
186 wxVariant(true, wxT("default-value")),
189 else if (GetType() == ctTypeInteger
)
191 m_properties
.AddProperty(
193 wxT("<B>Default-value</B><P> The default value."),
194 wxVariant((long) 0, wxT("default-value")),
198 m_properties
.AddProperty(
200 wxT("<B>Requires</B><P> When any of the given settings are 0, this setting <I>must</I> be 0. Taking wxUSE_ZIPSTREAM as an example:<P> If wxUSE_STREAMS is 0, then wxUSE_ZIPSTREAM must be 0.<BR>If wxUSE_STREAMS is 1, then wxUSE_ZIPSTREAM may be 0 or 1."),
201 wxVariant(wxT(""), wxT("requires")),
202 wxT("configitems")));
204 m_properties
.AddProperty(
206 wxT("<B>Precludes</B><P> When any of these settings are 1, this setting <I>must</I> be 0. Taking wxUSE_ODBC as an example:<P> If wxUSE_UNICODE is 1, then wxUSE_ODBC must be 0.<BR>If wxUSE_UNICODE is 0, then wxUSE_ODBC may be 0 or 1."),
207 wxVariant(wxT(""), wxT("precludes")),
208 wxT("configitems")));
210 m_properties
.AddProperty(
212 wxT("<B>Enabled-if</B><P> When any of these settings are 1, this setting <I>must</I> be 1."),
213 wxVariant(wxT(""), wxT("enabled-if")),
214 wxT("configitems")));
216 m_properties
.AddProperty(
218 wxT("<B>Enabled-if-not</B><P> When any of these settings are 0, this setting <I>must</I> be 1. Taking wxUSE_TOOLBAR_SIMPLE as an example:<P>If wxUSE_TOOLBAR_NATIVE is 0, wxUSE_TOOLBAR_SIMPLE must be 1.<BR>If wxUSE_TOOLBAR_NATIVE is 1, wxUSE_TOOLBAR_SIMPLE may be 0 or 1."),
219 wxVariant(wxT(""), wxT("enabled-if-not")),
220 wxT("configitems")));
222 m_properties
.AddProperty(
224 wxT("<B>Indeterminate-if</B><P> When any of these settings are 1, this setting becomes active and indeterminate. Taking wxUSE_UNICODE as an example:<P>If Custom is 1, wxUSE_UNICODE is indeterminate."),
225 wxVariant(wxT(""), wxT("indeterminate-if")),
226 wxT("configitems")));
228 m_properties
.AddProperty(
230 wxT("<B>Exclusivity</B><P> The settings that are mutually exclusive with this one."),
231 wxVariant(wxT(""), wxT("exclusivity")),
232 wxT("configitems")));
234 m_properties
.AddProperty(
236 wxT("<B>Context</B><P> A list of symbols (config settings), at least one of which must be enabled for this item to participate in dependency rules.<P>\nIf empty, this item will always be used in dependency rules.<P>\nMostly this will be used to specify the applicable platforms, but it can contain other symbols, for example compilers."),
237 wxVariant(wxT(""), wxT("context")),
238 wxT("configitems")));
240 m_properties
.AddProperty(
242 wxT("<B>Configure-command</B><P> Configure command to generate if this is on."),
243 wxVariant(wxT(""), wxT("configure-command")),
246 m_properties
.AddProperty(
248 wxT("<B>Help-topic</B><P> The help topic in the wxWidgets manual for this component or setting."),
249 wxVariant(wxT(""), wxT("help-topic")),
252 m_properties
.AddProperty(
254 wxT("<B>Notes</B><P> User notes."),
255 wxVariant(wxT(""), wxT("notes")),
258 m_defaultProperty
= wxT("description");
261 /// Do additional actions to apply the property to the internal
263 void ctConfigItem::ApplyProperty(ctProperty
* prop
, const wxVariant
& WXUNUSED(oldValue
))
265 ctConfigToolDoc
* doc
= GetDocument();
266 bool oldModified
= doc
->IsModified();
269 wxString name
= prop
->GetName();
270 if (name
== wxT("requires") ||
271 name
== wxT("precludes") ||
272 name
== wxT("enabled-if") ||
273 name
== wxT("enabled-if-not") ||
274 name
== wxT("indeterminate-if") ||
275 name
== wxT("context"))
277 doc
->RefreshDependencies();
279 if (doc
&& doc
->GetFirstView() && oldModified
!= doc
->IsModified())
280 ((ctConfigToolView
*)doc
->GetFirstView())->OnChangeFilename();
283 /// Get the associated document (currently, assumes
284 /// there's only ever one document active)
285 ctConfigToolDoc
* ctConfigItem::GetDocument()
287 ctConfigToolDoc
* doc
= wxGetApp().GetMainFrame()->GetDocument();
291 /// Convert string containing config item names to
292 /// an array of config item names
293 void ctConfigItem::StringToArray(const wxString
& items
, wxArrayString
& itemsArray
)
295 wxStringTokenizer
tokenizer(items
, wxT(","));
296 while (tokenizer
.HasMoreTokens())
298 wxString token
= tokenizer
.GetNextToken();
299 itemsArray
.Add(token
);
303 /// Convert array containing config item names to
305 void ctConfigItem::ArrayToString(const wxArrayString
& itemsArray
, wxString
& items
)
307 items
= wxEmptyString
;
309 for (i
= 0; i
< itemsArray
.GetCount(); i
++)
311 items
+= itemsArray
[i
];
312 if (i
< (itemsArray
.GetCount() - 1))
317 /// Populate a list of items found in the string.
318 void ctConfigItem::StringToItems(ctConfigItem
* topItem
, const wxString
& items
, wxList
& list
)
320 wxArrayString strArray
;
321 StringToArray(items
, strArray
);
323 for (i
= 0; i
< strArray
.GetCount(); i
++)
325 wxString
str(strArray
[i
]);
326 ctConfigItem
* item
= topItem
->FindItem(str
);
332 /// Find an item in this hierarchy
333 ctConfigItem
* ctConfigItem::FindItem(const wxString
& name
)
335 if (GetName() == name
)
338 for ( wxObjectList::compatibility_iterator node
= GetChildren().GetFirst(); node
; node
= node
->GetNext() )
340 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
341 ctConfigItem
* found
= child
->FindItem(name
);
348 /// Find the next sibling
349 ctConfigItem
* ctConfigItem::FindNextSibling()
353 wxObjectList::compatibility_iterator node
= GetParent()->GetChildren().Member(this);
354 if (node
&& node
->GetNext())
356 return (ctConfigItem
*) node
->GetNext()->GetData();
361 /// Find the previous sibling
362 ctConfigItem
* ctConfigItem::FindPreviousSibling()
366 wxObjectList::compatibility_iterator node
= GetParent()->GetChildren().Member(this);
367 if (node
&& node
->GetPrevious())
369 return (ctConfigItem
*) node
->GetPrevious()->GetData();
375 void ctConfigItem::Sync()
379 ctConfigToolView
* view
= (ctConfigToolView
*) GetDocument()->GetFirstView();
382 view
->SyncItem(wxGetApp().GetMainFrame()->GetConfigTreeCtrl(), this);
387 /// Create a clone of this and children
388 ctConfigItem
* ctConfigItem::DeepClone()
390 ctConfigItem
* newItem
= Clone();
392 for ( wxObjectList::compatibility_iterator node
= GetChildren().GetFirst(); node
; node
= node
->GetNext() )
394 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
395 ctConfigItem
* newChild
= child
->DeepClone();
396 newItem
->AddChild(newChild
);
401 /// Detach: remove from parent, and remove tree items
402 void ctConfigItem::Detach()
405 GetParent()->RemoveChild(this);
407 GetDocument()->SetTopItem(NULL
);
410 wxTreeItemId treeItem
= GetTreeItemId();
414 // Will delete the branch, but not the config items.
415 wxGetApp().GetMainFrame()->GetConfigTreeCtrl()->Delete(treeItem
);
418 /// Hide from tree: make sure tree deletions won't delete
420 void ctConfigItem::DetachFromTree()
422 wxTreeItemId item
= GetTreeItemId();
424 ctTreeItemData
* data
= (ctTreeItemData
*) wxGetApp().GetMainFrame()->GetConfigTreeCtrl()->GetItemData(item
);
425 data
->SetConfigItem(NULL
);
426 m_treeItemId
= wxTreeItemId();
428 for ( wxObjectList::compatibility_iterator node
= GetChildren().GetFirst(); node
; node
= node
->GetNext() )
430 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
431 child
->DetachFromTree();
435 /// Attach: insert after the given position
436 void ctConfigItem::Attach(ctConfigItem
* parent
, ctConfigItem
* insertBefore
)
443 wxObjectList::compatibility_iterator node
= parent
->GetChildren().Member(insertBefore
);
445 parent
->GetChildren().Insert(node
, this);
447 parent
->GetChildren().Append(this);
450 parent
->GetChildren().Append(this);
454 GetDocument()->SetTopItem(this);
458 /// Can have children?
459 bool ctConfigItem::CanHaveChildren() const
461 return (GetType() == ctTypeGroup
||
462 GetType() == ctTypeCheckGroup
||
463 GetType() == ctTypeRadioGroup
);
466 // An item is in the active context if:
467 // The context field is empty; or
468 // The context field contains a symbol that is currently enabled.
469 bool ctConfigItem::IsInActiveContext()
471 wxString context
= GetPropertyString(wxT("context"));
472 if (context
.IsEmpty())
476 StringToItems(GetDocument()->GetTopItem(), context
, contextItems
);
478 for ( wxObjectList::compatibility_iterator node
= contextItems
.GetFirst(); node
; node
= node
->GetNext() )
480 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
481 if (otherItem
->IsEnabled())
487 /// Evaluate the requires properties:
488 /// if any of the 'requires' items are disabled,
489 /// then this one is disabled (and inactive).
490 void ctConfigItem::EvaluateDependencies()
492 // For debugging purposes
493 wxString name
= GetName();
495 wxString
requires = GetPropertyString(wxT("requires"));
496 wxString precludes
= GetPropertyString(wxT("precludes"));
497 wxString enabledIf
= GetPropertyString(wxT("enabled-if"));
498 wxString enabledIfNot
= GetPropertyString(wxT("enabled-if-not"));
499 wxString indeterminateIf
= GetPropertyString(wxT("indeterminate-if"));
502 bool enabled
= IsEnabled();
503 bool oldEnabled
= enabled
;
504 bool oldActive
= IsActive();
505 bool explicitlyEnabled
= false;
506 bool explicitlyDisabled
= false;
507 bool inActiveContext
= IsInActiveContext();
509 // Add the parent to the list of dependencies, if the
510 // parent is a check or radio group.
511 ctConfigItem
* parent
= GetParent();
513 (parent
->GetType() == ctTypeCheckGroup
||
514 parent
->GetType() == ctTypeRadioGroup
))
515 items
.Append(parent
);
518 StringToItems(GetDocument()->GetTopItem(), requires, tempItems
);
520 wxObjectList::compatibility_iterator node
;
521 for ( node
= tempItems
.GetFirst(); node
; node
= node
->GetNext() )
523 // Only consider the dependency if both items are in
524 // an active context.
525 // Each is in the active context if:
526 // The context field is empty; or
527 // The context field contains a symbol that is currently enabled.
528 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
529 if (inActiveContext
&& otherItem
->IsInActiveContext())
530 items
.Append(otherItem
);
534 int enabledCount
= 0;
535 for ( node
= items
.GetFirst(); node
; node
= node
->GetNext() )
537 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
539 if (otherItem
->IsEnabled())
544 if (items
.GetCount() > 0 && enabledCount
== 0)
546 // None of the items were enabled
549 explicitlyDisabled
= true;
554 if (!enabledIfNot
.IsEmpty())
556 StringToItems(GetDocument()->GetTopItem(), enabledIfNot
, items
);
557 int disabledCount
= 0;
558 int inContextCount
= 0;
560 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
562 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
564 if (inActiveContext
&& otherItem
->IsInActiveContext())
566 // Make this enabled and inactive, _unless_ it's
567 // already been explicitly disabled in the previous
568 // requires evaluation (it really _has_ to be off)
569 if (!otherItem
->IsEnabled())
576 // Enable if there were no related items that were enabled
577 if (inContextCount
> 0 && (disabledCount
== inContextCount
) && !explicitlyDisabled
)
579 explicitlyEnabled
= true;
586 if (!enabledIf
.IsEmpty())
588 StringToItems(GetDocument()->GetTopItem(), enabledIf
, items
);
589 int enabledCount
= 0;
590 int inContextCount
= 0;
592 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
594 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
595 wxString otherName
= otherItem
->GetName();
597 if (inActiveContext
&& otherItem
->IsInActiveContext())
599 // Make this enabled and inactive, _unless_ it's
600 // already been explicitly disabled in the previous
601 // requires evaluation (it really _has_ to be off)
602 if (otherItem
->IsEnabled())
609 // Enable if there were no related items that were disabled
610 if (inContextCount
> 0 && (enabledCount
> 0) && !explicitlyDisabled
)
612 explicitlyEnabled
= true;
619 if (!precludes
.IsEmpty())
621 StringToItems(GetDocument()->GetTopItem(), precludes
, items
);
622 int enabledCount
= 0;
623 // int disabledCount = 0;
624 int inContextCount
= 0;
626 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
628 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
630 if (inActiveContext
&& otherItem
->IsInActiveContext())
632 // Make this disabled and inactive, _unless_ it's
633 // already been explicitly enabled in the previous
634 // requires evaluation (it really _has_ to be on)
635 // if (!otherItem->IsEnabled())
636 if (otherItem
->IsEnabled())
644 // Disable if there were no related items that were disabled
645 if (inContextCount
> 0 && (enabledCount
> 0) && !explicitlyEnabled
)
646 // if (inContextCount > 0 && (disabledCount > 0) && !explicitlyEnabled)
650 explicitlyDisabled
= true;
654 // Indeterminate overrides the others, and
655 // makes the item active.
657 if (!indeterminateIf
.IsEmpty())
659 StringToItems(GetDocument()->GetTopItem(), indeterminateIf
, items
);
660 int enabledCount
= 0;
661 int inContextCount
= 0;
663 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
665 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
667 if (inActiveContext
&& otherItem
->IsInActiveContext())
669 if (otherItem
->IsEnabled())
676 if (inContextCount
> 0 && enabledCount
> 0)
679 explicitlyEnabled
= false;
680 explicitlyDisabled
= false;
684 // Finally check a sort of dependency: whether our
685 // context is active. If not, make this inactive.
686 if (!IsInActiveContext())
690 // If we didn't explicitly enable or disable it,
691 // then we should make it active.
692 if (!explicitlyEnabled
&& !explicitlyDisabled
)
698 // If going active, set enabled state to the default state
700 oldActive
!= active
&&
701 (GetType() == ctTypeBoolCheck
|| GetType() == ctTypeCheckGroup
) &&
702 m_properties
.FindProperty(wxT("default-state")))
704 bool defaultState
= m_properties
.FindProperty(wxT("default-state"))->GetVariant().GetBool();
705 enabled
= defaultState
;
709 // Deal with setting a radio button
710 if (enabled
&& enabled
!= oldEnabled
&&
711 (GetType() == ctTypeBoolRadio
|| GetType() == ctTypeRadioGroup
))
714 PropagateRadioButton(considered
);
718 /// Get description, which may be dynamically
719 /// generated depending on the property.
720 wxString
ctConfigItem::GetDescription(ctProperty
* property
)
722 if (property
->GetName() == wxT("description"))
724 wxString
value(property
->GetValue());
726 return wxT("Double-click on <B>description</B> to write a brief explanation of the setting.<P>");
730 else if (property
->GetName() == wxT("notes"))
732 wxString
value(property
->GetValue());
734 return wxT("Double-click on <B>notes</B> to write notes about this setting.<P>");
738 return property
->GetDescription();
741 /// Get the title for the property editor
742 wxString
ctConfigItem::GetTitle()
744 wxString
title(GetName());
745 if (GetType() == ctTypeCheckGroup
||
746 GetType() == ctTypeRadioGroup
||
747 GetType() == ctTypeBoolCheck
||
748 GetType() == ctTypeBoolRadio
)
751 title
= title
+ _T(" - enabled");
753 title
= title
+ _T(" - disabled");
758 /// Propagate a change in enabled/disabled status
759 void ctConfigItem::PropagateChange(wxList
& considered
)
761 if (GetType() == ctTypeCheckGroup
||
762 GetType() == ctTypeRadioGroup
||
763 GetType() == ctTypeBoolCheck
||
764 GetType() == ctTypeBoolRadio
)
766 // TODO: what about string, integer? Can they have
769 for ( wxObjectList::compatibility_iterator node
= GetDependents().GetFirst(); node
; node
= node
->GetNext() )
771 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
774 if (!considered
.Member(child
))
776 considered
.Append(child
);
778 child
->EvaluateDependencies();
781 child
->PropagateChange(considered
);
787 /// Process radio button selection
788 void ctConfigItem::PropagateRadioButton(wxList
& considered
)
790 if ((GetType() == ctTypeBoolRadio
|| GetType() == ctTypeRadioGroup
) && IsEnabled())
792 wxString
mutuallyExclusive(GetPropertyString(wxT("exclusivity")));
795 StringToItems(GetDocument()->GetTopItem(), mutuallyExclusive
, list
);
797 for ( wxObjectList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext() )
799 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
800 if (child
->IsEnabled() && child
!= this)
802 child
->Enable(false);
805 if (!considered
.Member(child
))
806 child
->PropagateChange(considered
);