File: | client/ui/ui_nodes.cpp |
Location: | line 268, column 4 |
Description: | Dereference of null pointer (loaded from variable 'resultProperty') |
1 | /** | ||
2 | * @file | ||
3 | */ | ||
4 | |||
5 | /* | ||
6 | Copyright (C) 2002-2011 UFO: Alien Invasion. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or | ||
9 | modify it under the terms of the GNU General Public License | ||
10 | as published by the Free Software Foundation; either version 2 | ||
11 | of the License, or (at your option) any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
16 | |||
17 | See the GNU General Public License for more details. | ||
18 | |||
19 | You should have received a copy of the GNU General Public License | ||
20 | along with this program; if not, write to the Free Software | ||
21 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | ||
22 | |||
23 | */ | ||
24 | |||
25 | #include "ui_main.h" | ||
26 | #include "ui_internal.h" | ||
27 | #include "ui_node.h" | ||
28 | #include "ui_nodes.h" | ||
29 | #include "ui_parse.h" | ||
30 | #include "ui_input.h" | ||
31 | |||
32 | #include "node/ui_node_abstractnode.h" | ||
33 | #include "node/ui_node_abstractscrollbar.h" | ||
34 | #include "node/ui_node_abstractoption.h" | ||
35 | #include "node/ui_node_abstractvalue.h" | ||
36 | #include "node/ui_node_bar.h" | ||
37 | #include "node/ui_node_base.h" | ||
38 | #include "node/ui_node_baseinventory.h" | ||
39 | #include "node/ui_node_battlescape.h" | ||
40 | #include "node/ui_node_button.h" | ||
41 | #include "node/ui_node_checkbox.h" | ||
42 | #include "node/ui_node_controls.h" | ||
43 | #include "node/ui_node_container.h" | ||
44 | #include "node/ui_node_data.h" | ||
45 | #include "node/ui_node_editor.h" | ||
46 | #include "node/ui_node_ekg.h" | ||
47 | #include "node/ui_node_geoscape.h" | ||
48 | #include "node/ui_node_image.h" | ||
49 | #include "node/ui_node_item.h" | ||
50 | #include "node/ui_node_linechart.h" | ||
51 | #include "node/ui_node_material_editor.h" | ||
52 | #include "node/ui_node_messagelist.h" | ||
53 | #include "node/ui_node_model.h" | ||
54 | #include "node/ui_node_option.h" | ||
55 | #include "node/ui_node_optionlist.h" | ||
56 | #include "node/ui_node_optiontree.h" | ||
57 | #include "node/ui_node_panel.h" | ||
58 | #include "node/ui_node_radar.h" | ||
59 | #include "node/ui_node_radiobutton.h" | ||
60 | #include "node/ui_node_rows.h" | ||
61 | #include "node/ui_node_selectbox.h" | ||
62 | #include "node/ui_node_sequence.h" | ||
63 | #include "node/ui_node_string.h" | ||
64 | #include "node/ui_node_special.h" | ||
65 | #include "node/ui_node_spinner.h" | ||
66 | #include "node/ui_node_tab.h" | ||
67 | #include "node/ui_node_tbar.h" | ||
68 | #include "node/ui_node_text.h" | ||
69 | #include "node/ui_node_text2.h" | ||
70 | #include "node/ui_node_textlist.h" | ||
71 | #include "node/ui_node_textentry.h" | ||
72 | #include "node/ui_node_texture.h" | ||
73 | #include "node/ui_node_timer.h" | ||
74 | #include "node/ui_node_keybinding.h" | ||
75 | #include "node/ui_node_todo.h" | ||
76 | #include "node/ui_node_video.h" | ||
77 | #include "node/ui_node_vscrollbar.h" | ||
78 | #include "node/ui_node_zone.h" | ||
79 | |||
80 | typedef void (*registerFunction_t)(uiBehaviour_t *node); | ||
81 | |||
82 | /** | ||
83 | * @brief List of functions to register nodes | ||
84 | * @note Functions must be sorted by node name | ||
85 | */ | ||
86 | static const registerFunction_t registerFunctions[] = { | ||
87 | UI_RegisterNullNode, | ||
88 | UI_RegisterAbstractBaseNode, | ||
89 | UI_RegisterAbstractNode, | ||
90 | UI_RegisterAbstractOptionNode, | ||
91 | UI_RegisterAbstractScrollableNode, | ||
92 | UI_RegisterAbstractScrollbarNode, | ||
93 | UI_RegisterAbstractValueNode, | ||
94 | UI_RegisterBarNode, | ||
95 | UI_RegisterBaseInventoryNode, | ||
96 | UI_RegisterBaseLayoutNode, | ||
97 | UI_RegisterBaseMapNode, | ||
98 | UI_RegisterBattlescapeNode, | ||
99 | UI_RegisterButtonNode, | ||
100 | UI_RegisterCheckBoxNode, | ||
101 | UI_RegisterConFuncNode, | ||
102 | UI_RegisterContainerNode, | ||
103 | UI_RegisterControlsNode, | ||
104 | UI_RegisterCvarFuncNode, | ||
105 | UI_RegisterDataNode, | ||
106 | UI_RegisterEditorNode, | ||
107 | UI_RegisterEKGNode, | ||
108 | UI_RegisterFuncNode, | ||
109 | UI_RegisterGeoscapeNode, | ||
110 | UI_RegisterImageNode, | ||
111 | UI_RegisterItemNode, | ||
112 | UI_RegisterKeyBindingNode, | ||
113 | UI_RegisterLineChartNode, | ||
114 | UI_RegisterMaterialEditorNode, | ||
115 | UI_RegisterMessageListNode, | ||
116 | UI_RegisterModelNode, | ||
117 | UI_RegisterOptionNode, | ||
118 | UI_RegisterOptionListNode, | ||
119 | UI_RegisterOptionTreeNode, | ||
120 | UI_RegisterPanelNode, | ||
121 | UI_RegisterRadarNode, | ||
122 | UI_RegisterRadioButtonNode, | ||
123 | UI_RegisterRowsNode, | ||
124 | UI_RegisterSelectBoxNode, | ||
125 | UI_RegisterSequenceNode, | ||
126 | UI_RegisterSpinnerNode, | ||
127 | UI_RegisterStringNode, | ||
128 | UI_RegisterTabNode, | ||
129 | UI_RegisterTBarNode, | ||
130 | UI_RegisterTextNode, | ||
131 | UI_RegisterText2Node, | ||
132 | UI_RegisterTextEntryNode, | ||
133 | UI_RegisterTextListNode, | ||
134 | UI_RegisterTextureNode, | ||
135 | UI_RegisterTimerNode, | ||
136 | UI_RegisterTodoNode, | ||
137 | UI_RegisterVideoNode, | ||
138 | UI_RegisterVScrollbarNode, | ||
139 | UI_RegisterWindowNode, | ||
140 | UI_RegisterZoneNode | ||
141 | }; | ||
142 | #define NUMBER_OF_BEHAVIOURS(sizeof(registerFunctions) / sizeof(*(registerFunctions))) lengthof(registerFunctions)(sizeof(registerFunctions) / sizeof(*(registerFunctions))) | ||
143 | |||
144 | /** | ||
145 | * @brief List of all node behaviours, indexes by nodetype num. | ||
146 | */ | ||
147 | static uiBehaviour_t nodeBehaviourList[NUMBER_OF_BEHAVIOURS(sizeof(registerFunctions) / sizeof(*(registerFunctions)))]; | ||
148 | |||
149 | /** | ||
150 | * @brief Check the if conditions for a given node | ||
151 | * @sa V_UI_IF | ||
152 | * @returns false if the node is not drawn due to not meet if conditions | ||
153 | */ | ||
154 | bool UI_CheckVisibility (uiNode_t *node) | ||
155 | { | ||
156 | uiCallContext_t context; | ||
157 | if (!node->visibilityCondition) | ||
158 | return true; | ||
159 | context.source = node; | ||
160 | context.useCmdParam = false; | ||
161 | return UI_GetBooleanFromExpression(node->visibilityCondition, &context); | ||
162 | } | ||
163 | |||
164 | /** | ||
165 | * @brief Return a path from a window to a node | ||
166 | * @return A path "windowname.nodename.nodename.givennodename" | ||
167 | * @note Use a static buffer for the result | ||
168 | */ | ||
169 | const char* UI_GetPath (const uiNode_t* node) | ||
170 | { | ||
171 | static char result[512]; | ||
172 | const uiNode_t* nodes[8]; | ||
173 | int i = 0; | ||
174 | |||
175 | while (node) { | ||
176 | assert(i < 8)(__builtin_expect(!(i < 8), 0) ? __assert_rtn(__func__, "src/client/ui/ui_nodes.cpp" , 176, "i < 8") : (void)0); | ||
177 | nodes[i] = node; | ||
178 | node = node->parent; | ||
179 | i++; | ||
180 | } | ||
181 | |||
182 | /** @todo we can use something faster than cat */ | ||
183 | result[0] = '\0'; | ||
184 | while (i) { | ||
185 | i--; | ||
186 | Q_strcat(result, nodes[i]->name, sizeof(result)); | ||
187 | if (i > 0) | ||
188 | Q_strcat(result, ".", sizeof(result)); | ||
189 | } | ||
190 | |||
191 | return result; | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * @brief Read a path and return every we can use (node and property) | ||
196 | * @details The path token must be a window name, and then node child. | ||
197 | * Reserved token 'root' and 'parent' can be used to navigate. | ||
198 | * If relativeNode is set, the path can start with reserved token | ||
199 | * 'this', 'root' and 'parent' (relative to this node). | ||
200 | * The function can return a node property by using a '\@', | ||
201 | * the path 'foo\@pos' will return the window foo and the property 'pos' | ||
202 | * from the 'window' behaviour. | ||
203 | * @param[in] path Path to read. Contain a node location with dot seprator and a facultative property | ||
204 | * @param[in] relativeNode relative node where the path start. It allow to use facultative command to start the path (this, parent, root). | ||
205 | * @param[out] resultNode Node found. Else NULL. | ||
206 | * @param[out] resultProperty Property found. Else NULL. | ||
207 | * TODO Speed up, evilly used function, use strncmp instead of using buffer copy (name[MAX_VAR]) | ||
208 | */ | ||
209 | void UI_ReadNodePath (const char* path, const uiNode_t *relativeNode, uiNode_t **resultNode, const value_t **resultProperty) | ||
210 | { | ||
211 | char name[MAX_VAR64]; | ||
212 | uiNode_t* node = NULL__null; | ||
213 | const char* nextName; | ||
214 | char nextCommand = '^'; | ||
215 | |||
216 | *resultNode = NULL__null; | ||
217 | if (resultProperty) | ||
| |||
218 | *resultProperty = NULL__null; | ||
219 | |||
220 | nextName = path; | ||
221 | while (nextName && nextName[0] != '\0') { | ||
| |||
| |||
222 | const char* begin = nextName; | ||
223 | char command = nextCommand; | ||
224 | nextName = strpbrk(begin, ".@#"); | ||
225 | if (!nextName) { | ||
| |||
| |||
226 | Q_strncpyz(name, begin, sizeof(name))Q_strncpyzDebug( name, begin, sizeof(name), "src/client/ui/ui_nodes.cpp" , 226 ); | ||
227 | nextCommand = '\0'; | ||
228 | } else { | ||
229 | assert(nextName - begin + 1 <= sizeof(name))(__builtin_expect(!(nextName - begin + 1 <= sizeof(name)), 0) ? __assert_rtn(__func__, "src/client/ui/ui_nodes.cpp", 229 , "nextName - begin + 1 <= sizeof(name)") : (void)0); | ||
230 | Q_strncpyz(name, begin, nextName - begin + 1)Q_strncpyzDebug( name, begin, nextName - begin + 1, "src/client/ui/ui_nodes.cpp" , 230 ); | ||
231 | nextCommand = *nextName; | ||
232 | nextName++; | ||
233 | } | ||
234 | |||
235 | switch (command) { | ||
| |||
| |||
236 | case '^': /* first string */ | ||
237 | if (Q_streq(name, "this")(strcmp(name, "this") == 0)) { | ||
| |||
238 | if (relativeNode == NULL__null) | ||
239 | return; | ||
240 | /** @todo find a way to fix the bad cast. only here to remove "discards qualifiers" warning */ | ||
241 | node = *(uiNode_t**) ((void*)&relativeNode); | ||
242 | } else if (Q_streq(name, "parent")(strcmp(name, "parent") == 0)) { | ||
| |||
243 | if (relativeNode == NULL__null) | ||
244 | return; | ||
245 | node = relativeNode->parent; | ||
246 | } else if (Q_streq(name, "root")(strcmp(name, "root") == 0)) { | ||
| |||
247 | if (relativeNode == NULL__null) | ||
248 | return; | ||
249 | node = relativeNode->root; | ||
250 | } else | ||
251 | node = UI_GetWindow(name); | ||
252 | break; | ||
| |||
253 | case '.': /* child node */ | ||
254 | if (Q_streq(name, "parent")(strcmp(name, "parent") == 0)) | ||
255 | node = node->parent; | ||
256 | else if (Q_streq(name, "root")(strcmp(name, "root") == 0)) | ||
257 | node = node->root; | ||
258 | else | ||
259 | node = UI_GetNode(node, name); | ||
260 | break; | ||
261 | case '#': /* window index */ | ||
262 | /** @todo FIXME use a warning instead of an assert */ | ||
263 | assert(UI_Node_IsWindow(node))(__builtin_expect(!(UI_Node_IsWindow(node)), 0) ? __assert_rtn (__func__, "src/client/ui/ui_nodes.cpp", 263, "UI_Node_IsWindow(node)" ) : (void)0); | ||
264 | node = UI_WindowNodeGetIndexedChild(node, name); | ||
265 | break; | ||
266 | case '@': /* property */ | ||
267 | assert(nextCommand == '\0')(__builtin_expect(!(nextCommand == '\0'), 0) ? __assert_rtn(__func__ , "src/client/ui/ui_nodes.cpp", 267, "nextCommand == '\\0'") : (void)0); | ||
268 | *resultProperty = UI_GetPropertyFromBehaviour(node->behaviour, name); | ||
| |||
269 | *resultNode = node; | ||
270 | return; | ||
271 | } | ||
272 | |||
273 | if (!node) | ||
| |||
274 | return; | ||
275 | } | ||
276 | |||
277 | *resultNode = node; | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | /** | ||
282 | * @brief Return a node by a path name (names with dot separation) | ||
283 | * It is a simplification facade over UI_ReadNodePath | ||
284 | * @return The requested node, else NULL if not found | ||
285 | * @code | ||
286 | * // get keylist node from options_keys node from options window | ||
287 | * node = UI_GetNodeByPath("options.options_keys.keylist"); | ||
288 | * @sa UI_ReadNodePath | ||
289 | * @endcode | ||
290 | */ | ||
291 | uiNode_t* UI_GetNodeByPath (const char* path) | ||
292 | { | ||
293 | uiNode_t* node = NULL__null; | ||
294 | const value_t *property; | ||
295 | UI_ReadNodePath(path, NULL__null, &node, &property); | ||
296 | /** @todo FIXME warning if it return a property */ | ||
297 | return node; | ||
298 | } | ||
299 | |||
300 | /** | ||
301 | * @brief Allocate a node into the UI memory (do not call behaviour->new) | ||
302 | * @note It's not a dynamic memory allocation. Please only use it at the loading time | ||
303 | * @todo Assert out when we are not in parsing/loading stage | ||
304 | * @param[in] name Name of the new node, else NULL if we don't want to edit it. | ||
305 | * @param[in] type Name of the node behavior | ||
306 | * @param[in] isDynamic Allocate a node in static or dynamic memory | ||
307 | */ | ||
308 | static uiNode_t* UI_AllocNodeWithoutNew (const char* name, const char* type, bool isDynamic) | ||
309 | { | ||
310 | uiNode_t* node; | ||
311 | uiBehaviour_t *behaviour; | ||
312 | int nodeSize; | ||
313 | |||
314 | behaviour = UI_GetNodeBehaviour(type); | ||
315 | if (behaviour == NULL__null) | ||
316 | Com_Error(ERR_FATAL0, "UI_AllocNodeWithoutNew: Node behaviour '%s' doesn't exist", type); | ||
317 | |||
318 | nodeSize = sizeof(*node) + behaviour->extraDataSize; | ||
319 | |||
320 | if (!isDynamic) { | ||
321 | void *memory = UI_AllocHunkMemory(nodeSize, STRUCT_MEMORY_ALIGN8, true); | ||
322 | if (memory == NULL__null) | ||
323 | Com_Error(ERR_FATAL0, "UI_AllocNodeWithoutNew: No more memory to allocate a new node - increase the cvar ui_hunksize"); | ||
324 | node = static_cast<uiNode_t*>(memory); | ||
325 | ui_global.numNodes++; | ||
326 | } else { | ||
327 | node = static_cast<uiNode_t*>(Mem_PoolAlloc(nodeSize, ui_dynPool, 0)_Mem_Alloc((nodeSize),true,(ui_dynPool),(0),"src/client/ui/ui_nodes.cpp" ,327)); | ||
328 | node->dynamic = true; | ||
329 | } | ||
330 | |||
331 | node->behaviour = behaviour; | ||
332 | #ifdef DEBUG1 | ||
333 | UI_Node_DebugCountWidget(node, 1); | ||
334 | #endif | ||
335 | if (UI_Node_IsAbstract(node)) | ||
336 | Com_Error(ERR_FATAL0, "UI_AllocNodeWithoutNew: Node behavior '%s' is abstract. We can't instantiate it.", type); | ||
337 | |||
338 | if (name != NULL__null) { | ||
339 | Q_strncpyz(node->name, name, sizeof(node->name))Q_strncpyzDebug( node->name, name, sizeof(node->name), "src/client/ui/ui_nodes.cpp" , 339 ); | ||
340 | if (strlen(node->name) != strlen(name)) | ||
341 | Com_Printf("UI_AllocNodeWithoutNew: Node name \"%s\" truncated. New name is \"%s\"\n", name, node->name); | ||
342 | } | ||
343 | |||
344 | /* initialize default properties */ | ||
345 | UI_Node_Loading(node); | ||
346 | |||
347 | return node; | ||
348 | } | ||
349 | |||
350 | /** | ||
351 | * @brief Allocate a node into the UI memory | ||
352 | * @note It's not a dynamic memory allocation. Please only use it at the loading time | ||
353 | * @todo Assert out when we are not in parsing/loading stage | ||
354 | * @param[in] name Name of the new node, else NULL if we don't want to edit it. | ||
355 | * @param[in] type Name of the node behavior | ||
356 | * @param[in] isDynamic Allocate a node in static or dynamic memory | ||
357 | */ | ||
358 | uiNode_t* UI_AllocNode (const char* name, const char* type, bool isDynamic) | ||
359 | { | ||
360 | uiNode_t* node = UI_AllocNodeWithoutNew(name, type, isDynamic); | ||
361 | |||
362 | /* allocate memory */ | ||
363 | if (node->dynamic) | ||
364 | UI_Node_NewNode(node); | ||
365 | |||
366 | return node; | ||
367 | } | ||
368 | |||
369 | /** | ||
370 | * @brief Return the first visible node at a position | ||
371 | * @param[in] node Node where we must search | ||
372 | * @param[in] rx Relative x position to the parent of the node | ||
373 | * @param[in] ry Relative y position to the parent of the node | ||
374 | * @return The first visible node at position, else NULL | ||
375 | */ | ||
376 | static uiNode_t *UI_GetNodeInTreeAtPosition (uiNode_t *node, int rx, int ry) | ||
377 | { | ||
378 | uiNode_t *find; | ||
379 | |||
380 | if (node->invis || UI_Node_IsVirtual(node) || !UI_CheckVisibility(node)) | ||
381 | return NULL__null; | ||
382 | |||
383 | /* relative to the node */ | ||
384 | rx -= node->box.pos[0]; | ||
385 | ry -= node->box.pos[1]; | ||
386 | |||
387 | /* check bounding box */ | ||
388 | if (rx < 0 || ry < 0 || rx >= node->box.size[0] || ry >= node->box.size[1]) | ||
389 | return NULL__null; | ||
390 | |||
391 | /** @todo we should improve the loop (last-to-first) */ | ||
392 | find = NULL__null; | ||
393 | if (node->firstChild) { | ||
394 | uiNode_t *child; | ||
395 | vec2_t clientPosition = {0, 0}; | ||
396 | |||
397 | if (UI_Node_IsScrollableContainer(node)) | ||
398 | UI_Node_GetClientPosition(node, clientPosition); | ||
399 | |||
400 | rx -= clientPosition[0]; | ||
401 | ry -= clientPosition[1]; | ||
402 | |||
403 | for (child = node->firstChild; child; child = child->next) { | ||
404 | uiNode_t *tmp; | ||
405 | tmp = UI_GetNodeInTreeAtPosition(child, rx, ry); | ||
406 | if (tmp) | ||
407 | find = tmp; | ||
408 | } | ||
409 | |||
410 | rx += clientPosition[0]; | ||
411 | ry += clientPosition[1]; | ||
412 | } | ||
413 | if (find) | ||
414 | return find; | ||
415 | |||
416 | /* disable ghost/excluderect in debug mode 2 */ | ||
417 | if (UI_DebugMode() != 2) { | ||
418 | uiExcludeRect_t *excludeRect; | ||
419 | /* is the node tangible */ | ||
420 | if (node->ghost) | ||
421 | return NULL__null; | ||
422 | |||
423 | /* check excluded box */ | ||
424 | for (excludeRect = node->firstExcludeRect; excludeRect != NULL__null; excludeRect = excludeRect->next) { | ||
425 | if (rx >= excludeRect->pos[0] | ||
426 | && rx < excludeRect->pos[0] + excludeRect->size[0] | ||
427 | && ry >= excludeRect->pos[1] | ||
428 | && ry < excludeRect->pos[1] + excludeRect->size[1]) | ||
429 | return NULL__null; | ||
430 | } | ||
431 | } | ||
432 | |||
433 | /* we are over the node */ | ||
434 | return node; | ||
435 | } | ||
436 | |||
437 | /** | ||
438 | * @brief Return the first visible node at a position | ||
439 | */ | ||
440 | uiNode_t *UI_GetNodeAtPosition (int x, int y) | ||
441 | { | ||
442 | int pos; | ||
443 | |||
444 | /* find the first window under the mouse */ | ||
445 | for (pos = ui_global.windowStackPos - 1; pos >= 0; pos--) { | ||
446 | uiNode_t *window = ui_global.windowStack[pos]; | ||
447 | uiNode_t *find; | ||
448 | |||
449 | /* update the layout */ | ||
450 | UI_Validate(window); | ||
451 | |||
452 | find = UI_GetNodeInTreeAtPosition(window, x, y); | ||
453 | if (find) | ||
454 | return find; | ||
455 | |||
456 | /* we must not search anymore */ | ||
457 | if (UI_WindowIsDropDown(window)) | ||
458 | break; | ||
459 | if (UI_WindowIsModal(window)) | ||
460 | break; | ||
461 | if (UI_WindowIsFullScreen(window)) | ||
462 | break; | ||
463 | } | ||
464 | |||
465 | return NULL__null; | ||
466 | } | ||
467 | |||
468 | /** | ||
469 | * @brief Return a node behaviour by name | ||
470 | * @note Use a dichotomic search. nodeBehaviourList must be sorted by name. | ||
471 | * @param[in] name Behaviour name requested | ||
472 | * @return The bahaviour found, else NULL | ||
473 | */ | ||
474 | uiBehaviour_t* UI_GetNodeBehaviour (const char* name) | ||
475 | { | ||
476 | unsigned char min = 0; | ||
477 | unsigned char max = NUMBER_OF_BEHAVIOURS(sizeof(registerFunctions) / sizeof(*(registerFunctions))); | ||
478 | |||
479 | while (min != max) { | ||
480 | const int mid = (min + max) >> 1; | ||
481 | const int diff = strcmp(nodeBehaviourList[mid].name, name); | ||
482 | assert(mid < max)(__builtin_expect(!(mid < max), 0) ? __assert_rtn(__func__ , "src/client/ui/ui_nodes.cpp", 482, "mid < max") : (void) 0); | ||
483 | assert(mid >= min)(__builtin_expect(!(mid >= min), 0) ? __assert_rtn(__func__ , "src/client/ui/ui_nodes.cpp", 483, "mid >= min") : (void )0); | ||
484 | |||
485 | if (diff == 0) | ||
486 | return &nodeBehaviourList[mid]; | ||
487 | |||
488 | if (diff > 0) | ||
489 | max = mid; | ||
490 | else | ||
491 | min = mid + 1; | ||
492 | } | ||
493 | |||
494 | return NULL__null; | ||
495 | } | ||
496 | |||
497 | uiBehaviour_t* UI_GetNodeBehaviourByIndex (int index) | ||
498 | { | ||
499 | return &nodeBehaviourList[index]; | ||
500 | } | ||
501 | |||
502 | int UI_GetNodeBehaviourCount (void) | ||
503 | { | ||
504 | return NUMBER_OF_BEHAVIOURS(sizeof(registerFunctions) / sizeof(*(registerFunctions))); | ||
505 | } | ||
506 | |||
507 | /** | ||
508 | * @brief Remove all child from a node (only remove dynamic memory allocation nodes) | ||
509 | * @param node The node we want to clean | ||
510 | */ | ||
511 | void UI_DeleteAllChild (uiNode_t* node) | ||
512 | { | ||
513 | uiNode_t *child; | ||
514 | child = node->firstChild; | ||
515 | while (child) { | ||
516 | uiNode_t *next = child->next; | ||
517 | UI_DeleteNode(child); | ||
518 | child = next; | ||
519 | } | ||
520 | } | ||
521 | |||
522 | static void UI_BeforeDeletingNode (const uiNode_t* node) | ||
523 | { | ||
524 | if (UI_GetHoveredNode() == node) { | ||
525 | UI_InvalidateMouse(); | ||
526 | } | ||
527 | } | ||
528 | |||
529 | /** | ||
530 | * Delete the node and remove it from his parent | ||
531 | * @param node The node we want to delete | ||
532 | */ | ||
533 | void UI_DeleteNode (uiNode_t* node) | ||
534 | { | ||
535 | uiBehaviour_t *behaviour; | ||
536 | |||
537 | if (!node->dynamic) | ||
538 | return; | ||
539 | |||
540 | UI_BeforeDeletingNode(node); | ||
541 | |||
542 | UI_DeleteAllChild(node); | ||
543 | if (node->firstChild != NULL__null) { | ||
544 | Com_Printf("UI_DeleteNode: Node '%s' contain static nodes. We can't delete it.\n", UI_GetPath(node)); | ||
545 | return; | ||
546 | } | ||
547 | |||
548 | if (node->parent) | ||
549 | UI_RemoveNode(node->parent, node); | ||
550 | |||
551 | /* delete all allocated properties */ | ||
552 | for (behaviour = node->behaviour; behaviour; behaviour = behaviour->super) { | ||
553 | const value_t **property = behaviour->localProperties; | ||
554 | if (property == NULL__null) | ||
555 | continue; | ||
556 | while (*property) { | ||
557 | if (((*property)->type & V_UI_MASK0x8F00) == V_UI_CVAR(0x8000 + 0x0100)) { | ||
558 | if (void*& mem = Com_GetValue<void*>(node, *property)) { | ||
559 | UI_FreeStringProperty(mem); | ||
560 | mem = 0; | ||
561 | } | ||
562 | } | ||
563 | |||
564 | /** @todo We must delete all EA_LISTENER too */ | ||
565 | |||
566 | property++; | ||
567 | } | ||
568 | } | ||
569 | |||
570 | UI_Node_DeleteNode(node); | ||
571 | } | ||
572 | |||
573 | /** | ||
574 | * @brief Clone a node | ||
575 | * @param[in] node Node to clone | ||
576 | * @param[in] recursive True if we also must clone subnodes | ||
577 | * @param[in] newWindow Window where the nodes must be add (this function only link node into window, not window into the new node) | ||
578 | * @param[in] newName New node name, else NULL to use the source name | ||
579 | * @param[in] isDynamic Allocate a node in static or dynamic memory | ||
580 | * @todo exclude rect is not safe cloned. | ||
581 | * @todo actions are not cloned. It is be a problem if we use add/remove listener into a cloned node. | ||
582 | */ | ||
583 | uiNode_t* UI_CloneNode (const uiNode_t* node, uiNode_t *newWindow, bool recursive, const char *newName, bool isDynamic) | ||
584 | { | ||
585 | uiNode_t* newNode = UI_AllocNodeWithoutNew(NULL__null, UI_Node_GetWidgetName(node), isDynamic); | ||
586 | |||
587 | /* clone all data */ | ||
588 | memcpy(newNode, node, UI_Node_GetMemorySize(node)); | ||
589 | newNode->dynamic = isDynamic; | ||
590 | |||
591 | /* custom name */ | ||
592 | if (newName != NULL__null) { | ||
593 | Q_strncpyz(newNode->name, newName, sizeof(newNode->name))Q_strncpyzDebug( newNode->name, newName, sizeof(newNode-> name), "src/client/ui/ui_nodes.cpp", 593 ); | ||
594 | if (strlen(newNode->name) != strlen(newName)) | ||
595 | Com_Printf("UI_CloneNode: Node name \"%s\" truncated. New name is \"%s\"\n", newName, newNode->name); | ||
596 | } | ||
597 | |||
598 | /* clean up node navigation */ | ||
599 | if (node->root == node && newWindow == NULL__null) | ||
600 | newWindow = newNode; | ||
601 | newNode->root = newWindow; | ||
602 | newNode->parent = NULL__null; | ||
603 | newNode->firstChild = NULL__null; | ||
604 | newNode->lastChild = NULL__null; | ||
605 | newNode->next = NULL__null; | ||
606 | newNode->super = node; | ||
607 | |||
608 | /* clone child */ | ||
609 | if (recursive) { | ||
610 | uiNode_t* childNode; | ||
611 | for (childNode = node->firstChild; childNode; childNode = childNode->next) { | ||
612 | uiNode_t* newChildNode = UI_CloneNode(childNode, newWindow, recursive, NULL__null, isDynamic); | ||
613 | UI_AppendNode(newNode, newChildNode); | ||
614 | } | ||
615 | } | ||
616 | |||
617 | /* allocate memories */ | ||
618 | if (newNode->dynamic) | ||
619 | UI_Node_NewNode(newNode); | ||
620 | |||
621 | UI_Node_Clone(node, newNode); | ||
622 | |||
623 | return newNode; | ||
624 | } | ||
625 | |||
626 | void UI_InitNodes (void) | ||
627 | { | ||
628 | int i = 0; | ||
629 | uiBehaviour_t *current = nodeBehaviourList; | ||
630 | |||
631 | /* compute list of node behaviours */ | ||
632 | for (i = 0; i < NUMBER_OF_BEHAVIOURS(sizeof(registerFunctions) / sizeof(*(registerFunctions))); i++) { | ||
633 | OBJZERO(*current)(memset(&((*current)), (0), sizeof((*current)))); | ||
634 | current->registration = true; | ||
635 | registerFunctions[i](current); | ||
636 | current->registration = false; | ||
637 | current++; | ||
638 | } | ||
639 | |||
640 | /* check for safe data: list must be sorted by alphabet */ | ||
641 | current = nodeBehaviourList; | ||
642 | assert(current)(__builtin_expect(!(current), 0) ? __assert_rtn(__func__, "src/client/ui/ui_nodes.cpp" , 642, "current") : (void)0); | ||
643 | for (i = 0; i < NUMBER_OF_BEHAVIOURS(sizeof(registerFunctions) / sizeof(*(registerFunctions))) - 1; i++) { | ||
644 | const uiBehaviour_t *a = current; | ||
645 | const uiBehaviour_t *b = current + 1; | ||
646 | assert(b)(__builtin_expect(!(b), 0) ? __assert_rtn(__func__, "src/client/ui/ui_nodes.cpp" , 646, "b") : (void)0); | ||
647 | if (strcmp(a->name, b->name) >= 0) { | ||
648 | #ifdef DEBUG1 | ||
649 | Com_Error(ERR_FATAL0, "UI_InitNodes: '%s' is before '%s'. Please order node behaviour registrations by name", a->name, b->name); | ||
650 | #else | ||
651 | Com_Error(ERR_FATAL0, "UI_InitNodes: Error: '%s' is before '%s'", a->name, b->name); | ||
652 | #endif | ||
653 | } | ||
654 | current++; | ||
655 | } | ||
656 | |||
657 | /* finalize node behaviour initialization */ | ||
658 | current = nodeBehaviourList; | ||
659 | for (i = 0; i < NUMBER_OF_BEHAVIOURS(sizeof(registerFunctions) / sizeof(*(registerFunctions))); i++) { | ||
660 | UI_InitializeNodeBehaviour(current); | ||
661 | current++; | ||
662 | } | ||
663 | } | ||
664 | |||
665 | void uiBox_t::alignBox (uiBox_t& inner, align_t direction) | ||
666 | { | ||
667 | switch (direction % 3) { | ||
668 | case 0: /* left */ | ||
669 | inner.pos[0] = this->pos[0]; | ||
670 | break; | ||
671 | case 1: /* middle */ | ||
672 | inner.pos[0] = this->pos[0] + (this->size[0] * 0.5) - (inner.size[0] * 0.5); | ||
673 | break; | ||
674 | case 2: /* right */ | ||
675 | inner.pos[0] = this->pos[0] + this->size[0] - inner.size[0]; | ||
676 | break; | ||
677 | } | ||
678 | switch (direction / 3) { | ||
679 | case 0: /* top */ | ||
680 | inner.pos[1] = this->pos[1]; | ||
681 | break; | ||
682 | case 1: /* middle */ | ||
683 | inner.pos[1] = this->pos[1] + (this->size[1] * 0.5) - (inner.size[1] * 0.5); | ||
684 | break; | ||
685 | case 2: /* bottom */ | ||
686 | inner.pos[1] = this->pos[1] + this->size[1] - inner.size[1]; | ||
687 | break; | ||
688 | default: | ||
689 | inner.pos[1] = this->pos[1]; | ||
690 | Com_Error(ERR_FATAL0, "UI_ImageAlignBoxInBox: Align %d not supported\n", direction); | ||
691 | } | ||
692 | } |