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 // Includes other headers for precompiled compilation
23 #include "wx/tokenzr.h"
25 #include "configitem.h"
26 #include "configtree.h"
27 #include "configtooldoc.h"
28 #include "configtoolview.h"
29 #include "wxconfigtool.h"
30 #include "mainframe.h"
32 IMPLEMENT_CLASS(ctConfigItem
, wxObject
)
34 ctConfigItem::ctConfigItem()
37 m_type
= ctTypeBoolCheck
;
38 m_treeItemId
= wxTreeItemId();
44 ctConfigItem::ctConfigItem(ctConfigItem
* parent
, ctConfigType type
, const wxString
& name
)
48 m_treeItemId
= wxTreeItemId();
54 parent
->AddChild(this);
57 ctConfigItem::~ctConfigItem()
59 ctConfigTreeCtrl
* treeCtrl
= wxGetApp().GetMainFrame()->GetConfigTreeCtrl();
60 if (m_treeItemId
.IsOk() && treeCtrl
)
62 ctTreeItemData
* data
= (ctTreeItemData
*) treeCtrl
->GetItemData(m_treeItemId
);
64 data
->SetConfigItem(NULL
);
67 GetParent()->RemoveChild(this);
70 if (wxGetApp().GetMainFrame()->GetDocument() &&
71 wxGetApp().GetMainFrame()->GetDocument()->GetTopItem() == this)
72 wxGetApp().GetMainFrame()->GetDocument()->SetTopItem(NULL
);
78 /// Can we edit this property?
79 bool ctConfigItem::CanEditProperty(const wxString
& propName
) const
81 ctProperty
* prop
= m_properties
.FindProperty(propName
);
83 return !prop
->GetReadOnly();
88 /// Assignment operator.
89 void ctConfigItem::operator= (const ctConfigItem
& item
)
91 m_properties
= item
.m_properties
;
92 m_modified
= item
.m_modified
;
93 m_defaultProperty
= item
.m_defaultProperty
;
95 m_enabled
= item
.m_enabled
;
96 m_active
= item
.m_active
;
99 /// Sets the name property.
100 void ctConfigItem::SetName(const wxString
& name
)
102 m_properties
.SetProperty(wxT("name"), name
);
106 void ctConfigItem::Clear()
108 wxObjectList::compatibility_iterator node
= m_children
.GetFirst();
111 wxObjectList::compatibility_iterator next
= node
->GetNext();
112 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
114 // This should delete 'node' too, assuming
115 // child's m_parent points to 'this'. If not,
116 // it'll be cleaned up by m_children.Clear().
125 ctConfigItem
* ctConfigItem::GetChild(int n
) const
127 wxASSERT ( n
< GetChildCount() && n
> -1 );
129 if ( n
< GetChildCount() && n
> -1 )
131 ctConfigItem
* child
= wxDynamicCast(m_children
.Item(n
)->GetData(), ctConfigItem
);
138 // Get the child count
139 int ctConfigItem::GetChildCount() const
141 return m_children
.GetCount();
145 void ctConfigItem::AddChild(ctConfigItem
* item
)
147 m_children
.Append(item
);
148 item
->SetParent(this);
151 /// Remove (but don't delete) a child
152 void ctConfigItem::RemoveChild(ctConfigItem
* item
)
154 m_children
.DeleteObject(item
);
155 item
->SetParent(NULL
);
158 /// Initialise standard properties
159 void ctConfigItem::InitProperties()
161 ctProperty
* prop
= m_properties
.FindProperty(wxT("name"));
164 prop
= new ctProperty
;
165 m_properties
.AddProperty(prop
);
167 prop
->SetDescription(_("<B>Name</B><P> The name of the configuration setting."));
168 prop
->SetReadOnly(true);
170 m_properties
.AddProperty(
172 wxT("<B>Description</B><P> The setting description."),
173 wxVariant(wxT(""), wxT("description")),
176 m_properties
.AddProperty(
178 wxT("<B>Default-state</B><P> The default state."),
179 wxVariant(true, wxT("default-state")),
182 if (GetType() == ctTypeString
)
184 m_properties
.AddProperty(
186 wxT("<B>Default-value</B><P> The default value."),
187 wxVariant(true, wxT("default-value")),
190 else if (GetType() == ctTypeInteger
)
192 m_properties
.AddProperty(
194 wxT("<B>Default-value</B><P> The default value."),
195 wxVariant((long) 0, wxT("default-value")),
199 m_properties
.AddProperty(
201 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."),
202 wxVariant(wxT(""), wxT("requires")),
203 wxT("configitems")));
205 m_properties
.AddProperty(
207 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."),
208 wxVariant(wxT(""), wxT("precludes")),
209 wxT("configitems")));
211 m_properties
.AddProperty(
213 wxT("<B>Enabled-if</B><P> When any of these settings are 1, this setting <I>must</I> be 1."),
214 wxVariant(wxT(""), wxT("enabled-if")),
215 wxT("configitems")));
217 m_properties
.AddProperty(
219 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."),
220 wxVariant(wxT(""), wxT("enabled-if-not")),
221 wxT("configitems")));
223 m_properties
.AddProperty(
225 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."),
226 wxVariant(wxT(""), wxT("indeterminate-if")),
227 wxT("configitems")));
229 m_properties
.AddProperty(
231 wxT("<B>Exclusivity</B><P> The settings that are mutually exclusive with this one."),
232 wxVariant(wxT(""), wxT("exclusivity")),
233 wxT("configitems")));
235 m_properties
.AddProperty(
237 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."),
238 wxVariant(wxT(""), wxT("context")),
239 wxT("configitems")));
241 m_properties
.AddProperty(
243 wxT("<B>Configure-command</B><P> Configure command to generate if this is on."),
244 wxVariant(wxT(""), wxT("configure-command")),
247 m_properties
.AddProperty(
249 wxT("<B>Help-topic</B><P> The help topic in the wxWidgets manual for this component or setting."),
250 wxVariant(wxT(""), wxT("help-topic")),
253 m_properties
.AddProperty(
255 wxT("<B>Notes</B><P> User notes."),
256 wxVariant(wxT(""), wxT("notes")),
259 m_defaultProperty
= wxT("description");
262 /// Do additional actions to apply the property to the internal
264 void ctConfigItem::ApplyProperty(ctProperty
* prop
, const wxVariant
& WXUNUSED(oldValue
))
266 ctConfigToolDoc
* doc
= GetDocument();
267 bool oldModified
= doc
->IsModified();
270 wxString name
= prop
->GetName();
271 if (name
== wxT("requires") ||
272 name
== wxT("precludes") ||
273 name
== wxT("enabled-if") ||
274 name
== wxT("enabled-if-not") ||
275 name
== wxT("indeterminate-if") ||
276 name
== wxT("context"))
278 doc
->RefreshDependencies();
280 if (doc
&& doc
->GetFirstView() && oldModified
!= doc
->IsModified())
281 ((ctConfigToolView
*)doc
->GetFirstView())->OnChangeFilename();
284 /// Get the associated document (currently, assumes
285 /// there's only ever one document active)
286 ctConfigToolDoc
* ctConfigItem::GetDocument()
288 ctConfigToolDoc
* doc
= wxGetApp().GetMainFrame()->GetDocument();
292 /// Convert string containing config item names to
293 /// an array of config item names
294 void ctConfigItem::StringToArray(const wxString
& items
, wxArrayString
& itemsArray
)
296 wxStringTokenizer
tokenizer(items
, wxT(","));
297 while (tokenizer
.HasMoreTokens())
299 wxString token
= tokenizer
.GetNextToken();
300 itemsArray
.Add(token
);
304 /// Convert array containing config item names to
306 void ctConfigItem::ArrayToString(const wxArrayString
& itemsArray
, wxString
& items
)
308 items
= wxEmptyString
;
310 for (i
= 0; i
< itemsArray
.GetCount(); i
++)
312 items
+= itemsArray
[i
];
313 if (i
< (itemsArray
.GetCount() - 1))
318 /// Populate a list of items found in the string.
319 void ctConfigItem::StringToItems(ctConfigItem
* topItem
, const wxString
& items
, wxList
& list
)
321 wxArrayString strArray
;
322 StringToArray(items
, strArray
);
324 for (i
= 0; i
< strArray
.GetCount(); i
++)
326 wxString
str(strArray
[i
]);
327 ctConfigItem
* item
= topItem
->FindItem(str
);
333 /// Find an item in this hierarchy
334 ctConfigItem
* ctConfigItem::FindItem(const wxString
& name
)
336 if (GetName() == name
)
339 for ( wxObjectList::compatibility_iterator node
= GetChildren().GetFirst(); node
; node
= node
->GetNext() )
341 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
342 ctConfigItem
* found
= child
->FindItem(name
);
349 /// Find the next sibling
350 ctConfigItem
* ctConfigItem::FindNextSibling()
354 wxObjectList::compatibility_iterator node
= GetParent()->GetChildren().Member(this);
355 if (node
&& node
->GetNext())
357 return (ctConfigItem
*) node
->GetNext()->GetData();
362 /// Find the previous sibling
363 ctConfigItem
* ctConfigItem::FindPreviousSibling()
367 wxObjectList::compatibility_iterator node
= GetParent()->GetChildren().Member(this);
368 if (node
&& node
->GetPrevious())
370 return (ctConfigItem
*) node
->GetPrevious()->GetData();
376 void ctConfigItem::Sync()
380 ctConfigToolView
* view
= (ctConfigToolView
*) GetDocument()->GetFirstView();
383 view
->SyncItem(wxGetApp().GetMainFrame()->GetConfigTreeCtrl(), this);
388 /// Create a clone of this and children
389 ctConfigItem
* ctConfigItem::DeepClone()
391 ctConfigItem
* newItem
= Clone();
393 for ( wxObjectList::compatibility_iterator node
= GetChildren().GetFirst(); node
; node
= node
->GetNext() )
395 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
396 ctConfigItem
* newChild
= child
->DeepClone();
397 newItem
->AddChild(newChild
);
402 /// Detach: remove from parent, and remove tree items
403 void ctConfigItem::Detach()
406 GetParent()->RemoveChild(this);
408 GetDocument()->SetTopItem(NULL
);
411 wxTreeItemId treeItem
= GetTreeItemId();
415 // Will delete the branch, but not the config items.
416 wxGetApp().GetMainFrame()->GetConfigTreeCtrl()->Delete(treeItem
);
419 /// Hide from tree: make sure tree deletions won't delete
421 void ctConfigItem::DetachFromTree()
423 wxTreeItemId item
= GetTreeItemId();
425 ctTreeItemData
* data
= (ctTreeItemData
*) wxGetApp().GetMainFrame()->GetConfigTreeCtrl()->GetItemData(item
);
426 data
->SetConfigItem(NULL
);
427 m_treeItemId
= wxTreeItemId();
429 for ( wxObjectList::compatibility_iterator node
= GetChildren().GetFirst(); node
; node
= node
->GetNext() )
431 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
432 child
->DetachFromTree();
436 /// Attach: insert after the given position
437 void ctConfigItem::Attach(ctConfigItem
* parent
, ctConfigItem
* insertBefore
)
442 wxObjectList::compatibility_iterator node
= (wxObjectList::compatibility_iterator
)NULL
;
444 node
= parent
->GetChildren().Member(insertBefore
);
447 parent
->GetChildren().Insert(node
, this);
449 parent
->GetChildren().Append(this);
453 GetDocument()->SetTopItem(this);
457 /// Can have children?
458 bool ctConfigItem::CanHaveChildren() const
460 return (GetType() == ctTypeGroup
||
461 GetType() == ctTypeCheckGroup
||
462 GetType() == ctTypeRadioGroup
);
465 // An item is in the active context if:
466 // The context field is empty; or
467 // The context field contains a symbol that is currently enabled.
468 bool ctConfigItem::IsInActiveContext()
470 wxString context
= GetPropertyString(wxT("context"));
471 if (context
.IsEmpty())
475 StringToItems(GetDocument()->GetTopItem(), context
, contextItems
);
477 for ( wxObjectList::compatibility_iterator node
= contextItems
.GetFirst(); node
; node
= node
->GetNext() )
479 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
480 if (otherItem
->IsEnabled())
486 /// Evaluate the requires properties:
487 /// if any of the 'requires' items are disabled,
488 /// then this one is disabled (and inactive).
489 void ctConfigItem::EvaluateDependencies()
491 // For debugging purposes
492 wxString name
= GetName();
494 wxString
requires = GetPropertyString(wxT("requires"));
495 wxString precludes
= GetPropertyString(wxT("precludes"));
496 wxString enabledIf
= GetPropertyString(wxT("enabled-if"));
497 wxString enabledIfNot
= GetPropertyString(wxT("enabled-if-not"));
498 wxString indeterminateIf
= GetPropertyString(wxT("indeterminate-if"));
501 bool enabled
= IsEnabled();
502 bool oldEnabled
= enabled
;
503 bool oldActive
= IsActive();
504 bool explicitlyEnabled
= false;
505 bool explicitlyDisabled
= false;
506 bool inActiveContext
= IsInActiveContext();
508 // Add the parent to the list of dependencies, if the
509 // parent is a check or radio group.
510 ctConfigItem
* parent
= GetParent();
512 (parent
->GetType() == ctTypeCheckGroup
||
513 parent
->GetType() == ctTypeRadioGroup
))
514 items
.Append(parent
);
517 StringToItems(GetDocument()->GetTopItem(), requires, tempItems
);
519 wxObjectList::compatibility_iterator node
;
520 for ( node
= tempItems
.GetFirst(); node
; node
= node
->GetNext() )
522 // Only consider the dependency if both items are in
523 // an active context.
524 // Each is in the active context if:
525 // The context field is empty; or
526 // The context field contains a symbol that is currently enabled.
527 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
528 if (inActiveContext
&& otherItem
->IsInActiveContext())
529 items
.Append(otherItem
);
533 int enabledCount
= 0;
534 for ( node
= items
.GetFirst(); node
; node
= node
->GetNext() )
536 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
538 if (otherItem
->IsEnabled())
543 if (items
.GetCount() > 0 && enabledCount
== 0)
545 // None of the items were enabled
548 explicitlyDisabled
= true;
553 if (!enabledIfNot
.IsEmpty())
555 StringToItems(GetDocument()->GetTopItem(), enabledIfNot
, items
);
556 int disabledCount
= 0;
557 int inContextCount
= 0;
559 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
561 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
563 if (inActiveContext
&& otherItem
->IsInActiveContext())
565 // Make this enabled and inactive, _unless_ it's
566 // already been explicitly disabled in the previous
567 // requires evaluation (it really _has_ to be off)
568 if (!otherItem
->IsEnabled())
575 // Enable if there were no related items that were enabled
576 if (inContextCount
> 0 && (disabledCount
== inContextCount
) && !explicitlyDisabled
)
578 explicitlyEnabled
= true;
585 if (!enabledIf
.IsEmpty())
587 StringToItems(GetDocument()->GetTopItem(), enabledIf
, items
);
588 int enabledCount
= 0;
589 int inContextCount
= 0;
591 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
593 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
594 wxString otherName
= otherItem
->GetName();
596 if (inActiveContext
&& otherItem
->IsInActiveContext())
598 // Make this enabled and inactive, _unless_ it's
599 // already been explicitly disabled in the previous
600 // requires evaluation (it really _has_ to be off)
601 if (otherItem
->IsEnabled())
608 // Enable if there were no related items that were disabled
609 if (inContextCount
> 0 && (enabledCount
> 0) && !explicitlyDisabled
)
611 explicitlyEnabled
= true;
618 if (!precludes
.IsEmpty())
620 StringToItems(GetDocument()->GetTopItem(), precludes
, items
);
621 int enabledCount
= 0;
622 // int disabledCount = 0;
623 int inContextCount
= 0;
625 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
627 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
629 if (inActiveContext
&& otherItem
->IsInActiveContext())
631 // Make this disabled and inactive, _unless_ it's
632 // already been explicitly enabled in the previous
633 // requires evaluation (it really _has_ to be on)
634 // if (!otherItem->IsEnabled())
635 if (otherItem
->IsEnabled())
643 // Disable if there were no related items that were disabled
644 if (inContextCount
> 0 && (enabledCount
> 0) && !explicitlyEnabled
)
645 // if (inContextCount > 0 && (disabledCount > 0) && !explicitlyEnabled)
649 explicitlyDisabled
= true;
653 // Indeterminate overrides the others, and
654 // makes the item active.
656 if (!indeterminateIf
.IsEmpty())
658 StringToItems(GetDocument()->GetTopItem(), indeterminateIf
, items
);
659 int enabledCount
= 0;
660 int inContextCount
= 0;
662 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
664 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
666 if (inActiveContext
&& otherItem
->IsInActiveContext())
668 if (otherItem
->IsEnabled())
675 if (inContextCount
> 0 && enabledCount
> 0)
678 explicitlyEnabled
= false;
679 explicitlyDisabled
= false;
683 // Finally check a sort of dependency: whether our
684 // context is active. If not, make this inactive.
685 if (!IsInActiveContext())
689 // If we didn't explicitly enable or disable it,
690 // then we should make it active.
691 if (!explicitlyEnabled
&& !explicitlyDisabled
)
697 // If going active, set enabled state to the default state
699 oldActive
!= active
&&
700 (GetType() == ctTypeBoolCheck
|| GetType() == ctTypeCheckGroup
) &&
701 m_properties
.FindProperty(wxT("default-state")))
703 bool defaultState
= m_properties
.FindProperty(wxT("default-state"))->GetVariant().GetBool();
704 enabled
= defaultState
;
708 // Deal with setting a radio button
709 if (enabled
&& enabled
!= oldEnabled
&&
710 (GetType() == ctTypeBoolRadio
|| GetType() == ctTypeRadioGroup
))
713 PropagateRadioButton(considered
);
717 /// Get description, which may be dynamically
718 /// generated depending on the property.
719 wxString
ctConfigItem::GetDescription(ctProperty
* property
)
721 if (property
->GetName() == wxT("description"))
723 wxString
value(property
->GetValue());
725 return wxT("Double-click on <B>description</B> to write a brief explanation of the setting.<P>");
729 else if (property
->GetName() == wxT("notes"))
731 wxString
value(property
->GetValue());
733 return wxT("Double-click on <B>notes</B> to write notes about this setting.<P>");
737 return property
->GetDescription();
740 /// Get the title for the property editor
741 wxString
ctConfigItem::GetTitle()
743 wxString
title(GetName());
744 if (GetType() == ctTypeCheckGroup
||
745 GetType() == ctTypeRadioGroup
||
746 GetType() == ctTypeBoolCheck
||
747 GetType() == ctTypeBoolRadio
)
750 title
= title
+ _T(" - enabled");
752 title
= title
+ _T(" - disabled");
757 /// Propagate a change in enabled/disabled status
758 void ctConfigItem::PropagateChange(wxList
& considered
)
760 if (GetType() == ctTypeCheckGroup
||
761 GetType() == ctTypeRadioGroup
||
762 GetType() == ctTypeBoolCheck
||
763 GetType() == ctTypeBoolRadio
)
765 // TODO: what about string, integer? Can they have
768 for ( wxObjectList::compatibility_iterator node
= GetDependents().GetFirst(); node
; node
= node
->GetNext() )
770 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
773 if (!considered
.Member(child
))
775 considered
.Append(child
);
777 child
->EvaluateDependencies();
780 child
->PropagateChange(considered
);
786 /// Process radio button selection
787 void ctConfigItem::PropagateRadioButton(wxList
& considered
)
789 if ((GetType() == ctTypeBoolRadio
|| GetType() == ctTypeRadioGroup
) && IsEnabled())
791 wxString
mutuallyExclusive(GetPropertyString(wxT("exclusivity")));
794 StringToItems(GetDocument()->GetTopItem(), mutuallyExclusive
, list
);
796 for ( wxObjectList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext() )
798 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
799 if (child
->IsEnabled() && child
!= this)
801 child
->Enable(false);
804 if (!considered
.Member(child
))
805 child
->PropagateChange(considered
);