1 //  SuperTuxKart - a fun racing game with go-kart
2 //  Copyright (C) 2010-2015 Marianne Gagnon
3 //
4 //  This program is free software; you can redistribute it and/or
5 //  modify it under the terms of the GNU General Public License
6 //  as published by the Free Software Foundation; either version 3
7 //  of the License, or (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; if not, write to the Free Software
16 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 
18 #include "guiengine/abstract_top_level_container.hpp"
19 #include "guiengine/engine.hpp"
20 #include "guiengine/scalable_font.hpp"
21 #include "guiengine/widget.hpp"
22 #include "graphics/irr_driver.hpp"
23 #include "io/file_manager.hpp"
24 #include "utils/ptr_vector.hpp"
25 
26 #include <iostream>
27 
28 #include <IGUIElement.h>
29 
30 using namespace GUIEngine;
31 
32 using namespace irr;
33 using namespace core;
34 using namespace gui;
35 using namespace io;
36 using namespace scene;
37 using namespace video;
38 
39 
AbstractTopLevelContainer()40 AbstractTopLevelContainer::AbstractTopLevelContainer()
41 {
42     m_first_widget = NULL;
43     m_last_widget = NULL;
44 }
45 
46 // ----------------------------------------------------------------------------
47 
48 /** This function adds a list of widgets recursively, effectively creating the hierarchy
49  *  of widgets.
50  *  \param widgets The vector of widgets to add
51  *  \param parent The parent widget of the vector of widgets */
addWidgetsRecursively(PtrVector<Widget> & widgets,Widget * parent)52 void AbstractTopLevelContainer::addWidgetsRecursively(
53                                                     PtrVector<Widget>& widgets,
54                                                     Widget* parent)
55 {
56     const unsigned short widgets_amount = widgets.size();
57 
58     // ------- add widgets
59     for (int n=0; n<widgets_amount; n++)
60     {
61         if (widgets[n].getType() == WTYPE_DIV)
62         {
63             widgets[n].add(); // Will do nothing, but will maybe reserve an ID
64             addWidgetsRecursively(widgets[n].m_children, &widgets[n]);
65         }
66         else
67         {
68             // warn if widget has no dimensions (except for ribbons and icons,
69             // where it is normal since it adjusts to its contents)
70             if ((widgets[n].m_w < 1 || widgets[n].m_h < 1) &&
71                 widgets[n].getType() != WTYPE_RIBBON &&
72                 widgets[n].getType() != WTYPE_ICON_BUTTON &&
73                 widgets[n].getType() != WTYPE_SPACER)
74             {
75                 Log::warn("AbstractTopLevelContainer::addWidgetsRecursively",
76                     "Widget %s of type %d has no dimensions",
77                     widgets[n].m_properties[PROP_ID].c_str(), widgets[n].getType());
78             }
79 
80             if (widgets[n].m_x == -1 || widgets[n].m_y == -1)
81             {
82                 Log::warn("AbstractTopLevelContainer::addWidgetsRecursively",
83                     "Widget %s of type %d has no position",
84                     widgets[n].m_properties[PROP_ID].c_str(), widgets[n].getType());
85             }
86 
87             widgets[n].add();
88         }
89 
90     } // for n in all widgets
91 
92 }   // addWidgetsRecursively
93 
94 // ----------------------------------------------------------------------------
95 
96 /** This function checks recursively if a widget is a child of a vector of widgets
97  *  \param within The vector of widgets that may contain the widget
98  *  \param widget The widget that needs to be checked if it is in within
99  *  \return True if the widget is in within, false otherwise */
isMyChildHelperFunc(const PtrVector<Widget> * within,const Widget * widget)100 bool isMyChildHelperFunc(const PtrVector<Widget>* within, const Widget* widget)
101 {
102     if (within->size() == 0) return false;
103 
104     if (within->contains(widget))
105     {
106         return true;
107     }
108 
109     const int count = within->size();
110     for (int n=0; n<count; n++)
111     {
112         if (isMyChildHelperFunc(&within->get(n)->getChildren(), widget))
113         {
114             return true;
115         }
116     }
117 
118     return false;
119 }
120 
121 // ----------------------------------------------------------------------------
122 
123 /** This function checks if a widget is a child of the container.
124  *  \param widget The widget that needs to be checked if it is a child
125  *  \return True if the widget is a child, false otherwise */
isMyChild(Widget * widget) const126 bool AbstractTopLevelContainer::isMyChild(Widget* widget) const
127 {
128     return isMyChildHelperFunc(&m_widgets, widget);
129 }
130 
131 // ----------------------------------------------------------------------------
132 
133 /** This function returns a widget by name if that widget is found.
134  *  \param name The name of the widget to find
135  *  \return The result of the search, or NULL if the object is not found */
getWidget(const char * name)136 Widget* AbstractTopLevelContainer::getWidget(const char* name)
137 {
138     return getWidget(name, &m_widgets);
139 }   // getWidget
140 
141 // -----------------------------------------------------------------------------
142 
143 /** This function returns a widget by irrlicht ID if that widget is found.
144  *  \param id The irrlicht ID of the widget to find
145  *  \return The result of the search, or NULL if the object is not found */
getWidget(const int id)146 Widget* AbstractTopLevelContainer::getWidget(const int id)
147 {
148     return getWidget(id, &m_widgets);
149 }   // getWidget
150 
151 // -----------------------------------------------------------------------------
152 
153 /** This function returns a widget by name if that widget is found in within_vector.
154  *  \param name The name of the widget to find
155  *  \param within_vector The vector of widgets to look in
156  *  \return The result of the search, or NULL if the object is not found */
getWidget(const char * name,PtrVector<Widget> * within_vector)157 Widget* AbstractTopLevelContainer::getWidget(const char* name,
158                                              PtrVector<Widget>* within_vector)
159 {
160     const unsigned short widgets_amount = within_vector->size();
161 
162     for(int n=0; n<widgets_amount; n++)
163     {
164         Widget& widget = (*within_vector)[n];
165 
166         if (widget.m_properties[PROP_ID] == name) return &widget;
167 
168         if (widget.searchInsideMe() && widget.m_children.size() > 0)
169         {
170             Widget* el = getWidget(name, &(widget.m_children));
171             if(el != NULL) return el;
172         }
173     } // for n in all widgets
174 
175     return NULL;
176 }   // getWidget
177 
178 // -----------------------------------------------------------------------------
179 
180 /** This function returns a widget by irrlicht ID if that widget is found.
181  *  \param id The irrlicht ID of the widget to find
182  *  \param within_vector The vector to look into
183  *  \return The result of the search, or NULL if the object is not found */
getWidget(const int id,PtrVector<Widget> * within_vector)184 Widget* AbstractTopLevelContainer::getWidget(const int id,
185                                              PtrVector<Widget>* within_vector)
186 {
187     const unsigned short widgets_amount = within_vector->size();
188 
189     for (int n=0; n<widgets_amount; n++)
190     {
191         Widget& widget = (*within_vector)[n];
192 
193         if (widget.m_element != NULL &&
194             widget.getIrrlichtElement()->getID() == id) return &widget;
195 
196         if (widget.searchInsideMe() && widget.getChildren().size() > 0)
197         {
198             //Log::info("AbstractTopLevelContainer", "widget = <%s> widget.m_children.size() = ",
199             //    widget.m_properties[PROP_ID].c_str(), widget.m_children.size());
200             Widget* el = getWidget(id, &(widget.m_children));
201             if(el != NULL) return el;
202         }
203     } // for n in all widgets
204 
205     return NULL;
206 }   // getWidget
207 
208 // -----------------------------------------------------------------------------
209 
210 /** This function returns the first widget found in within_vector.
211  *  \param within_vector The vector to look into
212  *  \return The result of the search, or NULL if the object is not found */
getFirstWidget(PtrVector<Widget> * within_vector)213 Widget* AbstractTopLevelContainer::getFirstWidget(
214                                               PtrVector<Widget>* within_vector)
215 {
216     if (m_first_widget != NULL) return m_first_widget;
217     if (within_vector == NULL) within_vector = &m_widgets;
218 
219     for (unsigned int i = 0; i < within_vector->size(); i++)
220     {
221         if (!within_vector->get(i)->m_focusable) continue;
222 
223         // if container, also checks children
224         // (FIXME: don't hardcode which types to avoid descending into)
225         if (within_vector->get(i)->m_children.size() > 0 &&
226             within_vector->get(i)->getType() != WTYPE_RIBBON &&
227             within_vector->get(i)->getType() != WTYPE_SPINNER)
228         {
229             Widget* w = getFirstWidget(&within_vector->get(i)->m_children);
230             if (w != NULL) return w;
231         }
232 
233         Widget* item = within_vector->get(i);
234         if (item->getIrrlichtElement() == NULL ||
235             item->getIrrlichtElement()->getTabOrder() == -1 ||
236              /* non-tabbing items are given such IDs */
237             item->getIrrlichtElement()->getTabOrder() >= 1000 ||
238             !item->m_focusable)
239         {
240             continue;
241         }
242 
243         return item;
244     } // for i in all widgets of within_vector
245     return NULL;
246 }   // getFirstWidget
247 
248 // -----------------------------------------------------------------------------
249 
250 /** This function returns the last widget found in within_vector.
251  *  \param within_vector The vector to look into
252  *  \return The result of the search, or NULL if the object is not found */
getLastWidget(PtrVector<Widget> * within_vector)253 Widget* AbstractTopLevelContainer::getLastWidget(
254                                               PtrVector<Widget>* within_vector)
255 {
256     if (m_last_widget != NULL) return m_last_widget;
257     if (within_vector == NULL) within_vector = &m_widgets;
258 
259     for (int i = within_vector->size()-1; i >= 0; i--)
260     {
261         if (!within_vector->get(i)->m_focusable) continue;
262 
263         // if container, also checks children
264         if (within_vector->get(i)->getChildren().size() > 0 &&
265             within_vector->get(i)->getType() != WTYPE_RIBBON &&
266             within_vector->get(i)->getType() != WTYPE_SPINNER)
267         {
268             Widget* w = getLastWidget(&within_vector->get(i)->m_children);
269             if (w != NULL) return w;
270         }
271 
272         Widget* item = within_vector->get(i);
273         IGUIElement* elem = item->getIrrlichtElement();
274 
275         if (elem == NULL ||
276             elem->getTabOrder() == -1 ||
277             !Widget::isFocusableId(elem->getTabOrder()) ||
278             !item->m_focusable)
279         {
280             continue;
281         }
282 
283         return item;
284     }  // for i in all widgets of within_vector
285     return NULL;
286 }   // getLastWidget
287 
288 // ----------------------------------------------------------------------------
289 
290 /** This function is called when screen is removed. This means all irrlicht
291  *   widgets this object has pointers to are now gone. All references are set
292  *   to NULL to avoid problems.
293  *   \param within_vector The vector of widgets to clear
294  */
elementsWereDeleted(PtrVector<Widget> * within_vector)295 void AbstractTopLevelContainer::elementsWereDeleted(PtrVector<Widget>* within_vector)
296 {
297     if (within_vector == NULL) within_vector = &m_widgets;
298     const unsigned short widgets_amount = within_vector->size();
299 
300     for (int n=0; n<widgets_amount; n++)
301     {
302         Widget& widget = (*within_vector)[n];
303 
304         widget.elementRemoved();
305 
306         if (widget.m_children.size() > 0)
307         {
308             elementsWereDeleted( &(widget.m_children) );
309         }
310     }
311 }   // elementsWereDeleted
312