1 /***********************************************************************
2     created:    30/10/2011
3     author:     Martin Preisler
4 
5     purpose:    Implements the NamedElement class
6 *************************************************************************/
7 /***************************************************************************
8  *   Copyright (C) 2004 - 2011 Paul D Turner & The CEGUI Development Team
9  *
10  *   Permission is hereby granted, free of charge, to any person obtaining
11  *   a copy of this software and associated documentation files (the
12  *   "Software"), to deal in the Software without restriction, including
13  *   without limitation the rights to use, copy, modify, merge, publish,
14  *   distribute, sublicense, and/or sell copies of the Software, and to
15  *   permit persons to whom the Software is furnished to do so, subject to
16  *   the following conditions:
17  *
18  *   The above copyright notice and this permission notice shall be
19  *   included in all copies or substantial portions of the Software.
20  *
21  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23  *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24  *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
25  *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26  *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27  *   OTHER DEALINGS IN THE SOFTWARE.
28  ***************************************************************************/
29 #include <queue>
30 
31 #ifdef HAVE_CONFIG_H
32 #   include "config.h"
33 #endif
34 
35 #include "CEGUI/NamedElement.h"
36 #include "CEGUI/Logger.h"
37 #include "CEGUI/Exceptions.h"
38 
39 // Start of CEGUI namespace section
40 namespace CEGUI
41 {
42 
43 const String NamedElement::EventNamespace("NamedElement");
44 
45 const String NamedElement::EventNameChanged("NameChanged");
46 
47 //----------------------------------------------------------------------------//
NamedElement(const String & name)48 NamedElement::NamedElement(const String& name):
49     d_name(name)
50 {
51     addNamedElementProperties();
52 }
53 
54 //----------------------------------------------------------------------------//
~NamedElement()55 NamedElement::~NamedElement()
56 {}
57 
58 //----------------------------------------------------------------------------//
setName(const String & name)59 void NamedElement::setName(const String& name)
60 {
61     if (d_name == name)
62         return;
63 
64     if (getParentElement())
65     {
66         NamedElement* parent = dynamic_cast<NamedElement*>(getParentElement());
67 
68         if (parent && parent->isChild(name))
69         {
70             CEGUI_THROW(AlreadyExistsException("Failed to rename "
71                 "NamedElement at: " + getNamePath() + " as: " + name + ". A Window "
72                 "with that name is already attached as a sibling."));
73         }
74     }
75 
76     // log this under informative level
77     Logger::getSingleton().logEvent("Renamed element at: " + getNamePath() +
78                                     " as: " + name, Informative);
79 
80     d_name = name;
81 
82     NamedElementEventArgs args(this);
83     onNameChanged(args);
84 }
85 
86 //----------------------------------------------------------------------------//
getNamePath() const87 String NamedElement::getNamePath() const
88 {
89     String path("");
90 
91     Element* parent_element = getParentElement();
92     NamedElement* parent_named_element = dynamic_cast<NamedElement*>(parent_element);
93 
94     if (parent_element)
95     {
96         if (parent_named_element)
97             path = parent_named_element->getNamePath() + '/';
98         else
99             path = "<not a named element>/";
100     }
101 
102     path += getName();
103 
104     return path;
105 }
106 
107 //----------------------------------------------------------------------------//
isChild(const String & name_path) const108 bool NamedElement::isChild(const String& name_path) const
109 {
110     return getChildByNamePath_impl(name_path) != 0;
111 }
112 
113 //----------------------------------------------------------------------------//
isChildRecursive(const String & name) const114 bool NamedElement::isChildRecursive(const String& name) const
115 {
116     return getChildByNameRecursive_impl(name) != 0;
117 }
118 
119 //----------------------------------------------------------------------------//
isAncestor(const String & name) const120 bool NamedElement::isAncestor(const String& name) const
121 {
122     Element const* current = this;
123 
124     while (true)
125     {
126         const Element* parent = current->getParentElement();
127 
128         if (!parent)
129             return false;
130 
131         const NamedElement* named_parent = dynamic_cast<const NamedElement*>(parent);
132 
133         if (named_parent && named_parent->getName() == name)
134             return true;
135 
136         current = parent;
137     }
138 }
139 
140 //----------------------------------------------------------------------------//
getChildElement(const String & name_path) const141 NamedElement* NamedElement::getChildElement(const String& name_path) const
142 {
143     NamedElement* e = getChildByNamePath_impl(name_path);
144 
145     if (e)
146         return e;
147 
148     CEGUI_THROW(UnknownObjectException("The Element object "
149         "referenced by '" + name_path + "' is not attached to Element at '"
150         + getNamePath() + "'."));
151 }
152 
153 //----------------------------------------------------------------------------//
getChildElementRecursive(const String & name_path) const154 NamedElement* NamedElement::getChildElementRecursive(const String& name_path) const
155 {
156     return getChildByNameRecursive_impl(name_path);
157 }
158 
159 //----------------------------------------------------------------------------//
removeChild(const String & name_path)160 void NamedElement::removeChild(const String& name_path)
161 {
162     NamedElement* e = getChildByNamePath_impl(name_path);
163 
164     if (e)
165         removeChild(e);
166     else
167         CEGUI_THROW(UnknownObjectException("The Element object "
168             "referenced by '" + name_path + "' is not attached to Element at '"
169             + getNamePath() + "'."));
170 }
171 
172 //----------------------------------------------------------------------------//
addChild_impl(Element * element)173 void NamedElement::addChild_impl(Element* element)
174 {
175     NamedElement* named_element = dynamic_cast<NamedElement*>(element);
176 
177     if (named_element)
178     {
179         const NamedElement* const existing = getChildByNamePath_impl(named_element->getName());
180 
181         if (existing && named_element != existing)
182             CEGUI_THROW(AlreadyExistsException("Failed to add "
183                 "Element named: " + named_element->getName() + " to element at: " +
184                 getNamePath() + " since an Element with that name is already "
185                 "attached."));
186     }
187 
188     Element::addChild_impl(element);
189 }
190 
191 //----------------------------------------------------------------------------//
getChildByNamePath_impl(const String & name_path) const192 NamedElement* NamedElement::getChildByNamePath_impl(const String& name_path) const
193 {
194     const size_t sep = name_path.find_first_of('/');
195     const String base_child(name_path.substr(0, sep));
196 
197     const size_t child_count = d_children.size();
198 
199     for (size_t i = 0; i < child_count; ++i)
200     {
201         Element* child = getChildElementAtIdx(i);
202         NamedElement* named_child = dynamic_cast<NamedElement*>(child);
203 
204         if (!named_child)
205             continue;
206 
207         if (named_child->getName() == base_child)
208         {
209             if (sep != String::npos && sep < name_path.length() - 1)
210                 return named_child->getChildByNamePath_impl(name_path.substr(sep + 1));
211             else
212                 return named_child;
213         }
214     }
215 
216     return 0;
217 }
218 
219 //----------------------------------------------------------------------------//
getChildByNameRecursive_impl(const String & name) const220 NamedElement* NamedElement::getChildByNameRecursive_impl(const String& name) const
221 {
222     const size_t child_count = d_children.size();
223 
224     std::queue<Element*> ElementsToSearch;
225 
226     for (size_t i = 0; i < child_count; ++i) // load all children into the queue
227     {
228         Element* child = getChildElementAtIdx(i);
229         ElementsToSearch.push(child);
230     }
231 
232     while (!ElementsToSearch.empty()) // breadth-first search for the child to find
233     {
234         Element* child = ElementsToSearch.front();
235         ElementsToSearch.pop();
236 
237         NamedElement* named_child = dynamic_cast<NamedElement*>(child);
238         if (named_child)
239         {
240             if (named_child->getName() == name)
241             {
242                 return named_child;
243             }
244         }
245 
246         const size_t element_child_count = child->getChildCount();
247         for(size_t i = 0; i < element_child_count; ++i)
248         {
249             ElementsToSearch.push(child->getChildElementAtIdx(i));
250         }
251     }
252 
253     return 0;
254 }
255 
256 //----------------------------------------------------------------------------//
addNamedElementProperties()257 void NamedElement::addNamedElementProperties()
258 {
259     const String propertyOrigin("NamedElement");
260 
261     // "Name" is already stored in <Window> element
262     CEGUI_DEFINE_PROPERTY_NO_XML(NamedElement, String,
263         "Name", "Property to get/set the name of the Element. Make sure it's unique in its parent if any.",
264         &NamedElement::setName, &NamedElement::getName, ""
265     );
266 
267     CEGUI_DEFINE_PROPERTY_NO_XML(NamedElement, String,
268         "NamePath", "Property to get the absolute name path of this Element.",
269         0, &NamedElement::getNamePath, ""
270     );
271 }
272 
273 //----------------------------------------------------------------------------//
onNameChanged(NamedElementEventArgs & e)274 void NamedElement::onNameChanged(NamedElementEventArgs& e)
275 {
276     fireEvent(EventNameChanged, e, EventNamespace);
277 }
278 
279 } // End of  CEGUI namespace section
280