1 /////////////////////////////////////////////////////////////////////////////
2 // Name: configitem.cpp
3 // Purpose: wxWidgets Configuration Tool config item class
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
19 #include "wx/tokenzr.h"
20 #include "configitem.h"
21 #include "configtree.h"
22 #include "configtooldoc.h"
23 #include "configtoolview.h"
24 #include "wxconfigtool.h"
25 #include "mainframe.h"
27 IMPLEMENT_CLASS(ctConfigItem
, wxObject
)
29 ctConfigItem::ctConfigItem()
32 m_type
= ctTypeBoolCheck
;
33 m_treeItemId
= wxTreeItemId();
39 ctConfigItem::ctConfigItem(ctConfigItem
* parent
, ctConfigType type
, const wxString
& name
)
43 m_treeItemId
= wxTreeItemId();
49 parent
->AddChild(this);
52 ctConfigItem::~ctConfigItem()
54 ctConfigTreeCtrl
* treeCtrl
= wxGetApp().GetMainFrame()->GetConfigTreeCtrl();
55 if (m_treeItemId
.IsOk() && treeCtrl
)
57 ctTreeItemData
* data
= (ctTreeItemData
*) treeCtrl
->GetItemData(m_treeItemId
);
59 data
->SetConfigItem(NULL
);
62 GetParent()->RemoveChild(this);
65 if (wxGetApp().GetMainFrame()->GetDocument() &&
66 wxGetApp().GetMainFrame()->GetDocument()->GetTopItem() == this)
67 wxGetApp().GetMainFrame()->GetDocument()->SetTopItem(NULL
);
73 /// Can we edit this property?
74 bool ctConfigItem::CanEditProperty(const wxString
& propName
) const
76 ctProperty
* prop
= m_properties
.FindProperty(propName
);
78 return !prop
->GetReadOnly();
83 /// Assignment operator.
84 void ctConfigItem::operator= (const ctConfigItem
& item
)
86 m_properties
= item
.m_properties
;
87 m_modified
= item
.m_modified
;
88 m_defaultProperty
= item
.m_defaultProperty
;
90 m_enabled
= item
.m_enabled
;
91 m_active
= item
.m_active
;
94 /// Sets the name property.
95 void ctConfigItem::SetName(const wxString
& name
)
97 m_properties
.SetProperty(wxT("name"), name
);
101 void ctConfigItem::Clear()
103 wxObjectList::compatibility_iterator node
= m_children
.GetFirst();
106 wxObjectList::compatibility_iterator next
= node
->GetNext();
107 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
109 // This should delete 'node' too, assuming
110 // child's m_parent points to 'this'. If not,
111 // it'll be cleaned up by m_children.Clear().
120 ctConfigItem
* ctConfigItem::GetChild(int n
) const
122 wxASSERT ( n
< GetChildCount() && n
> -1 );
124 if ( n
< GetChildCount() && n
> -1 )
126 ctConfigItem
* child
= wxDynamicCast(m_children
.Item(n
)->GetData(), ctConfigItem
);
133 // Get the child count
134 int ctConfigItem::GetChildCount() const
136 return m_children
.GetCount();
140 void ctConfigItem::AddChild(ctConfigItem
* item
)
142 m_children
.Append(item
);
143 item
->SetParent(this);
146 /// Remove (but don't delete) a child
147 void ctConfigItem::RemoveChild(ctConfigItem
* item
)
149 m_children
.DeleteObject(item
);
150 item
->SetParent(NULL
);
153 /// Initialise standard properties
154 void ctConfigItem::InitProperties()
156 ctProperty
* prop
= m_properties
.FindProperty(wxT("name"));
159 prop
= new ctProperty
;
160 m_properties
.AddProperty(prop
);
162 prop
->SetDescription(_("<B>Name</B><P> The name of the configuration setting."));
163 prop
->SetReadOnly(true);
165 m_properties
.AddProperty(
167 wxT("<B>Description</B><P> The setting description."),
168 wxVariant(wxT(""), wxT("description")),
171 m_properties
.AddProperty(
173 wxT("<B>Default-state</B><P> The default state."),
174 wxVariant(true, wxT("default-state")),
177 if (GetType() == ctTypeString
)
179 m_properties
.AddProperty(
181 wxT("<B>Default-value</B><P> The default value."),
182 wxVariant(true, wxT("default-value")),
185 else if (GetType() == ctTypeInteger
)
187 m_properties
.AddProperty(
189 wxT("<B>Default-value</B><P> The default value."),
190 wxVariant((long) 0, wxT("default-value")),
194 m_properties
.AddProperty(
196 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."),
197 wxVariant(wxT(""), wxT("requires")),
198 wxT("configitems")));
200 m_properties
.AddProperty(
202 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."),
203 wxVariant(wxT(""), wxT("precludes")),
204 wxT("configitems")));
206 m_properties
.AddProperty(
208 wxT("<B>Enabled-if</B><P> When any of these settings are 1, this setting <I>must</I> be 1."),
209 wxVariant(wxT(""), wxT("enabled-if")),
210 wxT("configitems")));
212 m_properties
.AddProperty(
214 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."),
215 wxVariant(wxT(""), wxT("enabled-if-not")),
216 wxT("configitems")));
218 m_properties
.AddProperty(
220 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."),
221 wxVariant(wxT(""), wxT("indeterminate-if")),
222 wxT("configitems")));
224 m_properties
.AddProperty(
226 wxT("<B>Exclusivity</B><P> The settings that are mutually exclusive with this one."),
227 wxVariant(wxT(""), wxT("exclusivity")),
228 wxT("configitems")));
230 m_properties
.AddProperty(
232 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."),
233 wxVariant(wxT(""), wxT("context")),
234 wxT("configitems")));
236 m_properties
.AddProperty(
238 wxT("<B>Configure-command</B><P> Configure command to generate if this is on."),
239 wxVariant(wxT(""), wxT("configure-command")),
242 m_properties
.AddProperty(
244 wxT("<B>Help-topic</B><P> The help topic in the wxWidgets manual for this component or setting."),
245 wxVariant(wxT(""), wxT("help-topic")),
248 m_properties
.AddProperty(
250 wxT("<B>Notes</B><P> User notes."),
251 wxVariant(wxT(""), wxT("notes")),
254 m_defaultProperty
= wxT("description");
257 /// Do additional actions to apply the property to the internal
259 void ctConfigItem::ApplyProperty(ctProperty
* prop
, const wxVariant
& WXUNUSED(oldValue
))
261 ctConfigToolDoc
* doc
= GetDocument();
262 bool oldModified
= doc
->IsModified();
265 wxString name
= prop
->GetName();
266 if (name
== wxT("requires") ||
267 name
== wxT("precludes") ||
268 name
== wxT("enabled-if") ||
269 name
== wxT("enabled-if-not") ||
270 name
== wxT("indeterminate-if") ||
271 name
== wxT("context"))
273 doc
->RefreshDependencies();
275 if (doc
&& doc
->GetFirstView() && oldModified
!= doc
->IsModified())
276 ((ctConfigToolView
*)doc
->GetFirstView())->OnChangeFilename();
279 /// Get the associated document (currently, assumes
280 /// there's only ever one document active)
281 ctConfigToolDoc
* ctConfigItem::GetDocument()
283 ctConfigToolDoc
* doc
= wxGetApp().GetMainFrame()->GetDocument();
287 /// Convert string containing config item names to
288 /// an array of config item names
289 void ctConfigItem::StringToArray(const wxString
& items
, wxArrayString
& itemsArray
)
291 wxStringTokenizer
tokenizer(items
, wxT(","));
292 while (tokenizer
.HasMoreTokens())
294 wxString token
= tokenizer
.GetNextToken();
295 itemsArray
.Add(token
);
299 /// Convert array containing config item names to
301 void ctConfigItem::ArrayToString(const wxArrayString
& itemsArray
, wxString
& items
)
303 items
= wxEmptyString
;
305 for (i
= 0; i
< itemsArray
.GetCount(); i
++)
307 items
+= itemsArray
[i
];
308 if (i
< (itemsArray
.GetCount() - 1))
313 /// Populate a list of items found in the string.
314 void ctConfigItem::StringToItems(ctConfigItem
* topItem
, const wxString
& items
, wxList
& list
)
316 wxArrayString strArray
;
317 StringToArray(items
, strArray
);
319 for (i
= 0; i
< strArray
.GetCount(); i
++)
321 wxString
str(strArray
[i
]);
322 ctConfigItem
* item
= topItem
->FindItem(str
);
328 /// Find an item in this hierarchy
329 ctConfigItem
* ctConfigItem::FindItem(const wxString
& name
)
331 if (GetName() == name
)
334 for ( wxObjectList::compatibility_iterator node
= GetChildren().GetFirst(); node
; node
= node
->GetNext() )
336 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
337 ctConfigItem
* found
= child
->FindItem(name
);
344 /// Find the next sibling
345 ctConfigItem
* ctConfigItem::FindNextSibling()
349 wxObjectList::compatibility_iterator node
= GetParent()->GetChildren().Member(this);
350 if (node
&& node
->GetNext())
352 return (ctConfigItem
*) node
->GetNext()->GetData();
357 /// Find the previous sibling
358 ctConfigItem
* ctConfigItem::FindPreviousSibling()
362 wxObjectList::compatibility_iterator node
= GetParent()->GetChildren().Member(this);
363 if (node
&& node
->GetPrevious())
365 return (ctConfigItem
*) node
->GetPrevious()->GetData();
371 void ctConfigItem::Sync()
375 ctConfigToolView
* view
= (ctConfigToolView
*) GetDocument()->GetFirstView();
378 view
->SyncItem(wxGetApp().GetMainFrame()->GetConfigTreeCtrl(), this);
383 /// Create a clone of this and children
384 ctConfigItem
* ctConfigItem::DeepClone()
386 ctConfigItem
* newItem
= Clone();
388 for ( wxObjectList::compatibility_iterator node
= GetChildren().GetFirst(); node
; node
= node
->GetNext() )
390 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
391 ctConfigItem
* newChild
= child
->DeepClone();
392 newItem
->AddChild(newChild
);
397 /// Detach: remove from parent, and remove tree items
398 void ctConfigItem::Detach()
401 GetParent()->RemoveChild(this);
403 GetDocument()->SetTopItem(NULL
);
406 wxTreeItemId treeItem
= GetTreeItemId();
410 // Will delete the branch, but not the config items.
411 wxGetApp().GetMainFrame()->GetConfigTreeCtrl()->Delete(treeItem
);
414 /// Hide from tree: make sure tree deletions won't delete
416 void ctConfigItem::DetachFromTree()
418 wxTreeItemId item
= GetTreeItemId();
420 ctTreeItemData
* data
= (ctTreeItemData
*) wxGetApp().GetMainFrame()->GetConfigTreeCtrl()->GetItemData(item
);
421 data
->SetConfigItem(NULL
);
422 m_treeItemId
= wxTreeItemId();
424 for ( wxObjectList::compatibility_iterator node
= GetChildren().GetFirst(); node
; node
= node
->GetNext() )
426 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
427 child
->DetachFromTree();
431 /// Attach: insert after the given position
432 void ctConfigItem::Attach(ctConfigItem
* parent
, ctConfigItem
* insertBefore
)
439 wxObjectList::compatibility_iterator node
= parent
->GetChildren().Member(insertBefore
);
441 parent
->GetChildren().Insert(node
, this);
443 parent
->GetChildren().Append(this);
446 parent
->GetChildren().Append(this);
450 GetDocument()->SetTopItem(this);
454 /// Can have children?
455 bool ctConfigItem::CanHaveChildren() const
457 return (GetType() == ctTypeGroup
||
458 GetType() == ctTypeCheckGroup
||
459 GetType() == ctTypeRadioGroup
);
462 // An item is in the active context if:
463 // The context field is empty; or
464 // The context field contains a symbol that is currently enabled.
465 bool ctConfigItem::IsInActiveContext()
467 wxString context
= GetPropertyString(wxT("context"));
468 if (context
.IsEmpty())
472 StringToItems(GetDocument()->GetTopItem(), context
, contextItems
);
474 for ( wxObjectList::compatibility_iterator node
= contextItems
.GetFirst(); node
; node
= node
->GetNext() )
476 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
477 if (otherItem
->IsEnabled())
483 /// Evaluate the requires properties:
484 /// if any of the 'requires' items are disabled,
485 /// then this one is disabled (and inactive).
486 void ctConfigItem::EvaluateDependencies()
488 // For debugging purposes
489 wxString name
= GetName();
491 wxString
requires = GetPropertyString(wxT("requires"));
492 wxString precludes
= GetPropertyString(wxT("precludes"));
493 wxString enabledIf
= GetPropertyString(wxT("enabled-if"));
494 wxString enabledIfNot
= GetPropertyString(wxT("enabled-if-not"));
495 wxString indeterminateIf
= GetPropertyString(wxT("indeterminate-if"));
498 bool enabled
= IsEnabled();
499 bool oldEnabled
= enabled
;
500 bool oldActive
= IsActive();
501 bool explicitlyEnabled
= false;
502 bool explicitlyDisabled
= false;
503 bool inActiveContext
= IsInActiveContext();
505 // Add the parent to the list of dependencies, if the
506 // parent is a check or radio group.
507 ctConfigItem
* parent
= GetParent();
509 (parent
->GetType() == ctTypeCheckGroup
||
510 parent
->GetType() == ctTypeRadioGroup
))
511 items
.Append(parent
);
514 StringToItems(GetDocument()->GetTopItem(), requires, tempItems
);
516 wxObjectList::compatibility_iterator node
;
517 for ( node
= tempItems
.GetFirst(); node
; node
= node
->GetNext() )
519 // Only consider the dependency if both items are in
520 // an active context.
521 // Each is in the active context if:
522 // The context field is empty; or
523 // The context field contains a symbol that is currently enabled.
524 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
525 if (inActiveContext
&& otherItem
->IsInActiveContext())
526 items
.Append(otherItem
);
530 int enabledCount
= 0;
531 for ( node
= items
.GetFirst(); node
; node
= node
->GetNext() )
533 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
535 if (otherItem
->IsEnabled())
540 if (items
.GetCount() > 0 && enabledCount
== 0)
542 // None of the items were enabled
545 explicitlyDisabled
= true;
550 if (!enabledIfNot
.IsEmpty())
552 StringToItems(GetDocument()->GetTopItem(), enabledIfNot
, items
);
553 int disabledCount
= 0;
554 int inContextCount
= 0;
556 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
558 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
560 if (inActiveContext
&& otherItem
->IsInActiveContext())
562 // Make this enabled and inactive, _unless_ it's
563 // already been explicitly disabled in the previous
564 // requires evaluation (it really _has_ to be off)
565 if (!otherItem
->IsEnabled())
572 // Enable if there were no related items that were enabled
573 if (inContextCount
> 0 && (disabledCount
== inContextCount
) && !explicitlyDisabled
)
575 explicitlyEnabled
= true;
582 if (!enabledIf
.IsEmpty())
584 StringToItems(GetDocument()->GetTopItem(), enabledIf
, items
);
585 int enabledCount
= 0;
586 int inContextCount
= 0;
588 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
590 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
591 wxString otherName
= otherItem
->GetName();
593 if (inActiveContext
&& otherItem
->IsInActiveContext())
595 // Make this enabled and inactive, _unless_ it's
596 // already been explicitly disabled in the previous
597 // requires evaluation (it really _has_ to be off)
598 if (otherItem
->IsEnabled())
605 // Enable if there were no related items that were disabled
606 if (inContextCount
> 0 && (enabledCount
> 0) && !explicitlyDisabled
)
608 explicitlyEnabled
= true;
615 if (!precludes
.IsEmpty())
617 StringToItems(GetDocument()->GetTopItem(), precludes
, items
);
618 int enabledCount
= 0;
619 // int disabledCount = 0;
620 int inContextCount
= 0;
622 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
624 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
626 if (inActiveContext
&& otherItem
->IsInActiveContext())
628 // Make this disabled and inactive, _unless_ it's
629 // already been explicitly enabled in the previous
630 // requires evaluation (it really _has_ to be on)
631 // if (!otherItem->IsEnabled())
632 if (otherItem
->IsEnabled())
640 // Disable if there were no related items that were disabled
641 if (inContextCount
> 0 && (enabledCount
> 0) && !explicitlyEnabled
)
642 // if (inContextCount > 0 && (disabledCount > 0) && !explicitlyEnabled)
646 explicitlyDisabled
= true;
650 // Indeterminate overrides the others, and
651 // makes the item active.
653 if (!indeterminateIf
.IsEmpty())
655 StringToItems(GetDocument()->GetTopItem(), indeterminateIf
, items
);
656 int enabledCount
= 0;
657 int inContextCount
= 0;
659 for ( wxObjectList::compatibility_iterator node
= items
.GetFirst(); node
; node
= node
->GetNext() )
661 ctConfigItem
* otherItem
= (ctConfigItem
*) node
->GetData();
663 if (inActiveContext
&& otherItem
->IsInActiveContext())
665 if (otherItem
->IsEnabled())
672 if (inContextCount
> 0 && enabledCount
> 0)
675 explicitlyEnabled
= false;
676 explicitlyDisabled
= false;
680 // Finally check a sort of dependency: whether our
681 // context is active. If not, make this inactive.
682 if (!IsInActiveContext())
686 // If we didn't explicitly enable or disable it,
687 // then we should make it active.
688 if (!explicitlyEnabled
&& !explicitlyDisabled
)
694 // If going active, set enabled state to the default state
696 oldActive
!= active
&&
697 (GetType() == ctTypeBoolCheck
|| GetType() == ctTypeCheckGroup
) &&
698 m_properties
.FindProperty(wxT("default-state")))
700 bool defaultState
= m_properties
.FindProperty(wxT("default-state"))->GetVariant().GetBool();
701 enabled
= defaultState
;
705 // Deal with setting a radio button
706 if (enabled
&& enabled
!= oldEnabled
&&
707 (GetType() == ctTypeBoolRadio
|| GetType() == ctTypeRadioGroup
))
710 PropagateRadioButton(considered
);
714 /// Get description, which may be dynamically
715 /// generated depending on the property.
716 wxString
ctConfigItem::GetDescription(ctProperty
* property
)
718 if (property
->GetName() == wxT("description"))
720 wxString
value(property
->GetValue());
722 return wxT("Double-click on <B>description</B> to write a brief explanation of the setting.<P>");
726 else if (property
->GetName() == wxT("notes"))
728 wxString
value(property
->GetValue());
730 return wxT("Double-click on <B>notes</B> to write notes about this setting.<P>");
734 return property
->GetDescription();
737 /// Get the title for the property editor
738 wxString
ctConfigItem::GetTitle()
740 wxString
title(GetName());
741 if (GetType() == ctTypeCheckGroup
||
742 GetType() == ctTypeRadioGroup
||
743 GetType() == ctTypeBoolCheck
||
744 GetType() == ctTypeBoolRadio
)
747 title
= title
+ _T(" - enabled");
749 title
= title
+ _T(" - disabled");
754 /// Propagate a change in enabled/disabled status
755 void ctConfigItem::PropagateChange(wxList
& considered
)
757 if (GetType() == ctTypeCheckGroup
||
758 GetType() == ctTypeRadioGroup
||
759 GetType() == ctTypeBoolCheck
||
760 GetType() == ctTypeBoolRadio
)
762 // TODO: what about string, integer? Can they have
765 for ( wxObjectList::compatibility_iterator node
= GetDependents().GetFirst(); node
; node
= node
->GetNext() )
767 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
770 if (!considered
.Member(child
))
772 considered
.Append(child
);
774 child
->EvaluateDependencies();
777 child
->PropagateChange(considered
);
783 /// Process radio button selection
784 void ctConfigItem::PropagateRadioButton(wxList
& considered
)
786 if ((GetType() == ctTypeBoolRadio
|| GetType() == ctTypeRadioGroup
) && IsEnabled())
788 wxString
mutuallyExclusive(GetPropertyString(wxT("exclusivity")));
791 StringToItems(GetDocument()->GetTopItem(), mutuallyExclusive
, list
);
793 for ( wxObjectList::compatibility_iterator node
= list
.GetFirst(); node
; node
= node
->GetNext() )
795 ctConfigItem
* child
= (ctConfigItem
*) node
->GetData();
796 if (child
->IsEnabled() && child
!= this)
798 child
->Enable(false);
801 if (!considered
.Member(child
))
802 child
->PropagateChange(considered
);