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