1 /**
2  * @file
3  * @brief C interface to allow to access to cpp node code.
4  */
5 
6 /*
7 Copyright (C) 2002-2013 UFO: Alien Invasion.
8 
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13 
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 
18 See the GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23 
24 */
25 
26 #include <typeinfo>
27 #include "ui_main.h"
28 #include "ui_behaviour.h"
29 #include "ui_nodes.h"
30 #include "ui_node.h"
31 #include "node/ui_node_abstractnode.h"
32 #include "node/ui_node_abstractoption.h"
33 #include "node/ui_node_battlescape.h"
34 #include "node/ui_node_panel.h"
35 #include "ui_parse.h"
36 #include "ui_components.h"
37 #include "ui_internal.h"
38 
UI_Node_IsVirtual(uiNode_t const * node)39 bool UI_Node_IsVirtual (uiNode_t const* node)
40 {
41 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
42 	return b == nullptr;
43 }
44 
UI_Node_IsDrawable(uiNode_t const * node)45 bool UI_Node_IsDrawable (uiNode_t const* node)
46 {
47 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
48 	return b != nullptr;
49 }
50 
UI_Node_IsOptionContainer(uiNode_t const * node)51 bool UI_Node_IsOptionContainer (uiNode_t const* node)
52 {
53 	uiAbstractOptionNode* b = dynamic_cast<uiAbstractOptionNode*>(node->behaviour->manager.get());
54 	return b != nullptr;
55 }
56 
UI_Node_IsWindow(uiNode_t const * node)57 bool UI_Node_IsWindow (uiNode_t const* node)
58 {
59 	uiWindowNode* b = dynamic_cast<uiWindowNode*>(node->behaviour->manager.get());
60 	return b != nullptr;
61 }
62 
UI_Node_IsBattleScape(uiNode_t const * node)63 bool UI_Node_IsBattleScape (uiNode_t const* node)
64 {
65 	uiBattleScapeNode *b = dynamic_cast<uiBattleScapeNode*>(node->behaviour->manager.get());
66 	return b != nullptr;
67 }
68 
UI_Node_IsAbstract(uiNode_t const * node)69 bool UI_Node_IsAbstract (uiNode_t const* node)
70 {
71 	return node->behaviour->isAbstract;
72 }
73 
UI_Node_IsDrawItselfChild(uiNode_t const * node)74 bool UI_Node_IsDrawItselfChild (uiNode_t const* node)
75 {
76 	return node->behaviour->drawItselfChild;
77 }
78 
79 /**
80  * @todo Use typeid when it is possible
81  */
UI_Node_IsFunction(uiNode_t const * node)82 bool UI_Node_IsFunction (uiNode_t const* node)
83 {
84 	return node->behaviour->isFunction;
85 }
86 
87 /**
88  * @todo Use typeid when it is possible
89  */
UI_Node_IsScrollableContainer(uiNode_t const * node)90 bool UI_Node_IsScrollableContainer (uiNode_t const* node)
91 {
92 	uiAbstractScrollableNode* b = dynamic_cast<uiAbstractScrollableNode*>(node->behaviour->manager.get());
93 	return b != nullptr;
94 }
95 
UI_Node_GetWidgetName(uiNode_t const * node)96 const char* UI_Node_GetWidgetName (uiNode_t const* node)
97 {
98 	return node->behaviour->name;
99 }
100 
UI_Node_GetMemorySize(uiNode_t const * node)101 intptr_t UI_Node_GetMemorySize (uiNode_t const* node)
102 {
103 	return sizeof(*node) + node->behaviour->extraDataSize;
104 }
105 
UI_Node_Draw(uiNode_t * node)106 void UI_Node_Draw (uiNode_t* node)
107 {
108 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
109 	b->draw(node);
110 }
111 
UI_Node_DrawTooltip(const uiNode_t * node,int x,int y)112 void UI_Node_DrawTooltip (const uiNode_t* node, int x, int y)
113 {
114 	const uiLocatedNode* b = dynamic_cast<const uiLocatedNode*>(node->behaviour->manager.get());
115 	b->drawTooltip(node, x, y);
116 }
117 
UI_Node_DrawOverWindow(uiNode_t * node)118 void UI_Node_DrawOverWindow (uiNode_t* node)
119 {
120 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
121 	b->drawOverWindow(node);
122 }
123 
124 /* mouse events */
UI_Node_LeftClick(uiNode_t * node,int x,int y)125 void UI_Node_LeftClick (uiNode_t* node, int x, int y)
126 {
127 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
128 	b->onLeftClick(node, x, y);
129 }
130 
UI_Node_RightClick(uiNode_t * node,int x,int y)131 void UI_Node_RightClick (uiNode_t* node, int x, int y)
132 {
133 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
134 	b->onRightClick(node, x, y);
135 }
136 
UI_Node_MiddleClick(uiNode_t * node,int x,int y)137 void UI_Node_MiddleClick (uiNode_t* node, int x, int y)
138 {
139 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
140 	b->onMiddleClick(node, x, y);
141 }
142 
UI_Node_Scroll(uiNode_t * node,int deltaX,int deltaY)143 bool UI_Node_Scroll (uiNode_t* node, int deltaX, int deltaY)
144 {
145 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
146 	return b->onScroll(node, deltaX, deltaY);
147 }
148 
UI_Node_MouseMove(uiNode_t * node,int x,int y)149 void UI_Node_MouseMove (uiNode_t* node, int x, int y)
150 {
151 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
152 	b->onMouseMove(node, x, y);
153 }
154 
UI_Node_MouseDown(uiNode_t * node,int x,int y,int button)155 void UI_Node_MouseDown (uiNode_t* node, int x, int y, int button)
156 {
157 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
158 	b->onMouseDown(node, x, y, button);
159 }
160 
UI_Node_MouseUp(uiNode_t * node,int x,int y,int button)161 void UI_Node_MouseUp (uiNode_t* node, int x, int y, int button)
162 {
163 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
164 	b->onMouseUp(node, x, y, button);
165 }
166 
UI_Node_MouseEnter(uiNode_t * node)167 void UI_Node_MouseEnter (uiNode_t* node)
168 {
169 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
170 	b->onMouseEnter(node);
171 }
172 
UI_Node_MouseLeave(uiNode_t * node)173 void UI_Node_MouseLeave (uiNode_t* node)
174 {
175 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
176 	b->onMouseLeave(node);
177 }
178 
UI_Node_MouseLongPress(uiNode_t * node,int x,int y,int button)179 bool UI_Node_MouseLongPress (uiNode_t* node, int x, int y, int button)
180 {
181 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
182 	return b->onMouseLongPress(node, x, y, button);
183 }
184 
UI_Node_StartDragging(uiNode_t * node,int startX,int startY,int currentX,int currentY,int button)185 bool UI_Node_StartDragging (uiNode_t* node, int startX, int startY, int currentX, int currentY, int button)
186 {
187 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
188 	return b->onStartDragging(node, startX, startY, currentX, currentY, button);
189 }
190 
UI_Node_CapturedMouseMove(uiNode_t * node,int x,int y)191 void UI_Node_CapturedMouseMove (uiNode_t* node, int x, int y)
192 {
193 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
194 	b->onCapturedMouseMove(node, x, y);
195 }
196 
UI_Node_CapturedMouseLost(uiNode_t * node)197 void UI_Node_CapturedMouseLost (uiNode_t* node)
198 {
199 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
200 	b->onCapturedMouseLost(node);
201 }
202 
203 /* system allocation */
204 
UI_Node_Loading(uiNode_t * node)205 void UI_Node_Loading (uiNode_t* node)
206 {
207 	uiNode* b = node->behaviour->manager.get();
208 	b->onLoading(node);
209 }
210 
UI_Node_Loaded(uiNode_t * node)211 void UI_Node_Loaded (uiNode_t* node)
212 {
213 	uiNode* b = node->behaviour->manager.get();
214 	b->onLoaded(node);
215 }
216 
UI_Node_Clone(uiNode_t const * source,uiNode_t * clone)217 void UI_Node_Clone (uiNode_t const* source, uiNode_t* clone)
218 {
219 	uiNode* b = source->behaviour->manager.get();
220 	b->clone(source, clone);
221 }
222 
UI_Node_NewNode(uiNode_t * node)223 void UI_Node_NewNode (uiNode_t* node)
224 {
225 	uiNode* b = node->behaviour->manager.get();
226 	b->newNode(node);
227 }
228 
UI_Node_DeleteNode(uiNode_t * node)229 void UI_Node_DeleteNode (uiNode_t* node)
230 {
231 	uiNode* b = node->behaviour->manager.get();
232 	b->deleteNode(node);
233 }
234 
235 /* system callback */
236 
UI_Node_WindowOpened(uiNode_t * node,linkedList_t * params)237 void UI_Node_WindowOpened (uiNode_t* node, linkedList_t* params)
238 {
239 	uiNode* b = node->behaviour->manager.get();
240 	b->onWindowOpened(node, params);
241 }
242 
UI_Node_WindowClosed(uiNode_t * node)243 void UI_Node_WindowClosed (uiNode_t* node)
244 {
245 	uiNode* b = node->behaviour->manager.get();
246 	b->onWindowClosed(node);
247 }
248 
UI_Node_WindowActivate(uiNode_t * node)249 void UI_Node_WindowActivate (uiNode_t* node)
250 {
251 	uiNode* b = node->behaviour->manager.get();
252 	b->onWindowActivate(node);
253 }
254 
UI_Node_DoLayout(uiNode_t * node)255 void UI_Node_DoLayout (uiNode_t* node)
256 {
257 	if (UI_Node_IsDrawable(node)) {
258 		uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
259 		b->doLayout(node);
260 	}
261 }
262 
UI_Node_Activate(uiNode_t * node)263 void UI_Node_Activate (uiNode_t* node)
264 {
265 	uiNode* b = node->behaviour->manager.get();
266 	b->onActivate(node);
267 }
268 
UI_Node_PropertyChanged(uiNode_t * node,const value_t * property)269 void UI_Node_PropertyChanged (uiNode_t* node, const value_t* property)
270 {
271 	uiNode* b = node->behaviour->manager.get();
272 	b->onPropertyChanged(node, property);
273 }
274 
UI_Node_SizeChanged(uiNode_t * node)275 void UI_Node_SizeChanged (uiNode_t* node)
276 {
277 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
278 	b->onSizeChanged(node);
279 }
280 
UI_Node_GetClientPosition(uiNode_t const * node,vec2_t position)281 void UI_Node_GetClientPosition (uiNode_t const* node, vec2_t position)
282 {
283 	uiAbstractScrollableNode* b = dynamic_cast<uiAbstractScrollableNode*>(node->behaviour->manager.get());
284 	b->getClientPosition(node, position);
285 }
286 
287 /* drag and drop callback */
288 
UI_Node_DndEnter(uiNode_t * node)289 bool UI_Node_DndEnter (uiNode_t* node)
290 {
291 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
292 	return b->onDndEnter(node);
293 }
294 
UI_Node_DndMove(uiNode_t * node,int x,int y)295 bool UI_Node_DndMove (uiNode_t* node, int x, int y)
296 {
297 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
298 	return b->onDndMove(node, x, y);
299 }
300 
UI_Node_DndLeave(uiNode_t * node)301 void UI_Node_DndLeave (uiNode_t* node)
302 {
303 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
304 	b->onDndLeave(node);
305 }
306 
UI_Node_DndDrop(uiNode_t * node,int x,int y)307 bool UI_Node_DndDrop (uiNode_t* node, int x, int y)
308 {
309 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
310 	return b->onDndDrop(node, x, y);
311 }
312 
UI_Node_DndFinished(uiNode_t * node,bool isDroped)313 bool UI_Node_DndFinished (uiNode_t* node, bool isDroped)
314 {
315 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
316 	return b->onDndFinished(node, isDroped);
317 }
318 
319 /* focus and keyboard events */
320 
UI_Node_FocusGained(uiNode_t * node)321 void UI_Node_FocusGained (uiNode_t* node)
322 {
323 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
324 	b->onFocusGained(node);
325 }
326 
UI_Node_FocusLost(uiNode_t * node)327 void UI_Node_FocusLost (uiNode_t* node)
328 {
329 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
330 	b->onFocusLost(node);
331 }
332 
UI_Node_KeyPressed(uiNode_t * node,unsigned int key,unsigned short unicode)333 bool UI_Node_KeyPressed (uiNode_t* node, unsigned int key, unsigned short unicode)
334 {
335 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
336 	return b->onKeyPressed(node, key, unicode);
337 }
338 
UI_Node_KeyReleased(uiNode_t * node,unsigned int key,unsigned short unicode)339 bool UI_Node_KeyReleased (uiNode_t* node, unsigned int key, unsigned short unicode)
340 {
341 	uiLocatedNode* b = dynamic_cast<uiLocatedNode*>(node->behaviour->manager.get());
342 	return b->onKeyReleased(node, key, unicode);
343 }
344 
345 /* cell size */
346 
UI_Node_GetCellWidth(uiNode_t * node)347 int UI_Node_GetCellWidth (uiNode_t* node)
348 {
349 	uiAbstractScrollableNode* b = dynamic_cast<uiAbstractScrollableNode*>(node->behaviour->manager.get());
350 	return b->getCellWidth(node);
351 }
352 
UI_Node_GetCellHeight(uiNode_t * node)353 int UI_Node_GetCellHeight (uiNode_t* node)
354 {
355 	uiAbstractScrollableNode* b = dynamic_cast<uiAbstractScrollableNode*>(node->behaviour->manager.get());
356 	return b->getCellHeight(node);
357 }
358 
359 #ifdef DEBUG
360 
UI_Node_DebugCountWidget(uiNode_t * node,int count)361 void UI_Node_DebugCountWidget (uiNode_t* node, int count)
362 {
363 	node->behaviour->count += count;
364 }
365 
366 #endif
367 
368 /**
369  * @brief Check the node inheritance
370  * @param[in] node Requested node
371  * @param[in] behaviourName Behaviour name we check
372  * @return True if the node inherits from the behaviour
373  */
UI_NodeInstanceOf(const uiNode_t * node,const char * behaviourName)374 bool UI_NodeInstanceOf (const uiNode_t* node, const char* behaviourName)
375 {
376 	const uiBehaviour_t* behaviour;
377 	for (behaviour = node->behaviour; behaviour; behaviour = behaviour->super) {
378 		if (Q_streq(behaviour->name, behaviourName))
379 			return true;
380 	}
381 	return false;
382 }
383 
384 /**
385  * @brief Check the node inheritance
386  * @param[in] node Requested node
387  * @param[in] behaviour Behaviour we check
388  * @return True if the node inherits from the behaviour
389  */
UI_NodeInstanceOfPointer(const uiNode_t * node,const uiBehaviour_t * behaviour)390 bool UI_NodeInstanceOfPointer (const uiNode_t* node, const uiBehaviour_t* behaviour)
391 {
392 	const uiBehaviour_t* b;
393 	for (b = node->behaviour; b; b = b->super) {
394 		if (b == behaviour)
395 			return true;
396 	}
397 	return false;
398 }
399 
400 /**
401  * @brief return a relative position of a point into a node.
402  * @param [in] node Requested node
403  * @param [out] pos Result position
404  * @param [in] direction
405  * @note For example we can request the right-bottom corner with LAYOUTALIGN_BOTTOMRIGHT
406  */
UI_NodeGetPoint(const uiNode_t * node,vec2_t pos,int direction)407 void UI_NodeGetPoint (const uiNode_t* node, vec2_t pos, int direction)
408 {
409 	switch (UI_GET_HORIZONTAL_ALIGN(direction)) {
410 	case LAYOUTALIGN_H_LEFT:
411 		pos[0] = 0;
412 		break;
413 	case LAYOUTALIGN_H_MIDDLE:
414 		pos[0] = (int)(node->box.size[0] / 2);
415 		break;
416 	case LAYOUTALIGN_H_RIGHT:
417 		pos[0] = node->box.size[0];
418 		break;
419 	default:
420 		Com_Printf("UI_NodeGetPoint: Align '%d' (0x%X) is not a common alignment", direction, direction);
421 		pos[0] = 0;
422 		pos[1] = 0;
423 		return;
424 	}
425 
426 	switch (UI_GET_VERTICAL_ALIGN(direction)) {
427 	case LAYOUTALIGN_V_TOP:
428 		pos[1] = 0;
429 		break;
430 	case LAYOUTALIGN_V_MIDDLE:
431 		pos[1] = (int)(node->box.size[1] / 2);
432 		break;
433 	case LAYOUTALIGN_V_BOTTOM:
434 		pos[1] = node->box.size[1];
435 		break;
436 	default:
437 		Com_Printf("UI_NodeGetPoint: Align '%d' (0x%X) is not a common alignment", direction, direction);
438 		pos[0] = 0;
439 		pos[1] = 0;
440 		return;
441 	}
442 }
443 
444 /**
445  * @brief Returns the absolute position of a node.
446  * @param[in] node Context node
447  * @param[out] pos Absolute position
448  */
UI_GetNodeAbsPos(const uiNode_t * node,vec2_t pos)449 void UI_GetNodeAbsPos (const uiNode_t* node, vec2_t pos)
450 {
451 	assert(node);
452 	assert(pos);
453 
454 	/* if we request the position of a non drawable node, there is a problem */
455 	if (node->behaviour->isVirtual)
456 		Com_Error(ERR_FATAL, "UI_GetNodeAbsPos: Node '%s' doesn't have a position", node->name);
457 
458 	Vector2Set(pos, 0, 0);
459 	while (node) {
460 #ifdef DEBUG
461 		if (node->box.pos[0] != (int)node->box.pos[0] || node->box.pos[1] != (int)node->box.pos[1])
462 			Com_Error(ERR_FATAL, "UI_GetNodeAbsPos: Node '%s' position %f,%f is not integer", UI_GetPath(node), node->box.pos[0], node->box.pos[1]);
463 #endif
464 		pos[0] += node->box.pos[0];
465 		pos[1] += node->box.pos[1];
466 		node = node->parent;
467 	}
468 }
469 
470 /**
471  * @brief Returns the absolute position of a node in the screen.
472  * Screen position is not used for the node rendering cause we use OpenGL
473  * translations. But this function is need for some R_functions methodes.
474  * @param[in] node Context node
475  * @param[out] pos Absolute position into the screen
476  */
UI_GetNodeScreenPos(const uiNode_t * node,vec2_t pos)477 void UI_GetNodeScreenPos (const uiNode_t* node, vec2_t pos)
478 {
479 	assert(node);
480 	assert(pos);
481 
482 	/* if we request the position of a non drawable node, there is a problem */
483 	if (node->behaviour->isVirtual)
484 		Com_Error(ERR_FATAL, "UI_GetNodeAbsPos: Node '%s' doesn't have a position", node->name);
485 
486 	Vector2Set(pos, 0, 0);
487 	while (node) {
488 #ifdef DEBUG
489 		if (node->box.pos[0] != (int)node->box.pos[0] || node->box.pos[1] != (int)node->box.pos[1])
490 			Com_Error(ERR_FATAL, "UI_GetNodeAbsPos: Node '%s' position %f,%f is not integer", UI_GetPath(node), node->box.pos[0], node->box.pos[1]);
491 #endif
492 		pos[0] += node->box.pos[0];
493 		pos[1] += node->box.pos[1];
494 		node = node->parent;
495 
496 		if (node && UI_Node_IsScrollableContainer(node)) {
497 			vec2_t clientPosition = {0, 0};
498 			UI_Node_GetClientPosition(node, clientPosition);
499 			pos[0] += clientPosition[0];
500 			pos[1] += clientPosition[1];
501 		}
502 	}
503 }
504 
505 /**
506  * @brief Update a relative point to an absolute one
507  * @param[in] node The requested node
508  * @param[in,out] pos A point to transform
509  */
UI_NodeRelativeToAbsolutePoint(const uiNode_t * node,vec2_t pos)510 void UI_NodeRelativeToAbsolutePoint (const uiNode_t* node, vec2_t pos)
511 {
512 	assert(node);
513 	assert(pos);
514 	while (node) {
515 		pos[0] += node->box.pos[0];
516 		pos[1] += node->box.pos[1];
517 		node = node->parent;
518 	}
519 }
520 
521 /**
522  * @brief Update an absolute position to a relative one
523  * @param[in] node Context node
524  * @param[in,out] x an absolute x position
525  * @param[in,out] y an absolute y position
526  */
UI_NodeAbsoluteToRelativePos(const uiNode_t * node,int * x,int * y)527 void UI_NodeAbsoluteToRelativePos (const uiNode_t* node, int* x, int* y)
528 {
529 	assert(node != nullptr);
530 	/* if we request the position of an undrawable node, there is a problem */
531 	assert(node->behaviour->isVirtual == false);
532 	assert(x != nullptr);
533 	assert(y != nullptr);
534 
535 	/* if we request the position of an undrawable node, there is a problem */
536 	if (node->behaviour->isVirtual)
537 		Com_Error(ERR_DROP, "UI_NodeAbsoluteToRelativePos: Node '%s' doesn't have a position", node->name);
538 
539 	while (node) {
540 		*x -= node->box.pos[0];
541 		*y -= node->box.pos[1];
542 
543 		if (UI_Node_IsScrollableContainer(node)) {
544 			vec2_t clientPosition = {0, 0};
545 			UI_Node_GetClientPosition(node, clientPosition);
546 			*x -= clientPosition[0];
547 			*y -= clientPosition[1];
548 		}
549 
550 		node = node->parent;
551 	}
552 }
553 
554 /**
555  * @brief Hides a given node
556  * @note Sanity check whether node is null included
557  */
UI_HideNode(uiNode_t * node)558 void UI_HideNode (uiNode_t* node)
559 {
560 	if (node)
561 		node->invis = true;
562 	else
563 		Com_Printf("UI_HideNode: No node given\n");
564 }
565 
566 /**
567  * @brief Unhides a given node
568  * @note Sanity check whether node is null included
569  */
UI_UnHideNode(uiNode_t * node)570 void UI_UnHideNode (uiNode_t* node)
571 {
572 	if (node)
573 		node->invis = false;
574 	else
575 		Com_Printf("UI_UnHideNode: No node given\n");
576 }
577 
578 /**
579  * @brief Update the node size and fire the size callback
580  */
UI_NodeSetSize(uiNode_t * node,vec2_t size)581 void UI_NodeSetSize (uiNode_t* node, vec2_t size)
582 {
583 	if (Vector2Equal(node->box.size, size))
584 		return;
585 	node->box.size[0] = size[0];
586 	node->box.size[1] = size[1];
587 	UI_Node_SizeChanged(node);
588 }
589 
590 /**
591  * @brief Search a child node by given name
592  * @note Only search with one depth
593  */
UI_GetNode(const uiNode_t * const node,const char * name)594 uiNode_t* UI_GetNode (const uiNode_t* const node, const char* name)
595 {
596 	uiNode_t* current = nullptr;
597 
598 	if (!node)
599 		return nullptr;
600 
601 	for (current = node->firstChild; current; current = current->next)
602 		if (Q_streq(name, current->name))
603 			break;
604 
605 	return current;
606 }
607 
608 /**
609  * @brief Insert a node next another one into a node. If prevNode is nullptr add the node on the head of the window
610  * @param[in] node Node where inserting a node
611  * @param[in] prevNode previous node, will became before the newNode; else nullptr if newNode will become the first child of the node
612  * @param[in] newNode node we insert
613  */
UI_InsertNode(uiNode_t * const node,uiNode_t * prevNode,uiNode_t * newNode)614 void UI_InsertNode (uiNode_t* const node, uiNode_t* prevNode, uiNode_t* newNode)
615 {
616 	if (newNode->root == nullptr)
617 		newNode->root = node->root;
618 
619 	assert(node);
620 	assert(newNode);
621 	/* insert only a single element */
622 	assert(!newNode->next);
623 	/* everything come from the same window (force the dev to update himself this links) */
624 	assert(!prevNode || (prevNode->root == newNode->root));
625 
626 	uiNode_t** const anchor = prevNode ? &prevNode->next : &node->firstChild;
627 	newNode->next   = *anchor;
628 	*anchor         = newNode;
629 	newNode->parent = node;
630 
631 	if (!newNode->next)
632 		node->lastChild = newNode;
633 
634 	if (newNode->root && newNode->indexed)
635 		UI_WindowNodeAddIndexedNode(newNode->root, newNode);
636 
637 	UI_Invalidate(node);
638 }
639 
640 /**
641  * @brief Remove a node from a parent node
642  * @return The removed node, else nullptr
643  * @param[in] node Node where is the child
644  * @param[in] child Node we want to remove
645  */
UI_RemoveNode(uiNode_t * const node,uiNode_t * child)646 uiNode_t* UI_RemoveNode (uiNode_t* const node, uiNode_t* child)
647 {
648 	assert(node);
649 	assert(child);
650 	assert(child->parent == node);
651 	assert(node->firstChild);
652 
653 	/** remove the 'child' node */
654 	for (uiNode_t** anchor = &node->firstChild;; anchor = &(*anchor)->next) {
655 		if (!*anchor)
656 			return 0;
657 
658 		if (*anchor == child) {
659 			*anchor = child->next;
660 			break;
661 		}
662 	}
663 	UI_Invalidate(node);
664 
665 	/** update cache */
666 	if (node->lastChild == child) {
667 		node->lastChild = node->firstChild;
668 		while (node->lastChild && node->lastChild->next)
669 			node->lastChild = node->lastChild->next;
670 	}
671 	if (child->root && child->indexed)
672 		UI_WindowNodeRemoveIndexedNode(child->root, child);
673 
674 	child->next = nullptr;
675 	return child;
676 }
677 
UI_UpdateRoot(uiNode_t * node,uiNode_t * newRoot)678 void UI_UpdateRoot (uiNode_t* node, uiNode_t* newRoot)
679 {
680 	node->root = newRoot;
681 	node = node->firstChild;
682 	while (node) {
683 		UI_UpdateRoot(node, newRoot);
684 		node = node->next;
685 	}
686 }
687 
688 /**
689  * @brief add a node at the end of the node child
690  */
UI_AppendNode(uiNode_t * const node,uiNode_t * newNode)691 void UI_AppendNode (uiNode_t* const node, uiNode_t* newNode)
692 {
693 	UI_InsertNode(node, node->lastChild, newNode);
694 }
695 
UI_NodeSetPropertyFromRAW(uiNode_t * node,const value_t * property,const void * rawValue,int rawType)696 void UI_NodeSetPropertyFromRAW (uiNode_t* node, const value_t* property, const void* rawValue, int rawType)
697 {
698 	if (property->type != rawType) {
699 		Com_Printf("UI_NodeSetPropertyFromRAW: type %i expected, but @%s type %i found. Property setter to '%s@%s' skipped\n", rawType, property->string, property->type, UI_GetPath(node), property->string);
700 		return;
701 	}
702 
703 	if ((property->type & V_UI_MASK) == V_NOT_UI)
704 		Com_SetValue(node, rawValue, property->type, property->ofs, property->size);
705 	else if ((property->type & V_UI_MASK) == V_UI_CVAR) {
706 		UI_FreeStringProperty(Com_GetValue<void*>(node, property));
707 		switch (property->type & V_BASETYPEMASK) {
708 		case V_FLOAT: *Com_GetValue<float*     >(node, property) = *static_cast<float const*>(rawValue); break;
709 		case V_INT:   *Com_GetValue<int*       >(node, property) = *static_cast<int   const*>(rawValue); break;
710 		default:       Com_GetValue<byte const*>(node, property) =  static_cast<byte  const*>(rawValue); break;
711 		}
712 	} else if (property->type == V_UI_ACTION) {
713 		Com_GetValue<uiAction_t const*>(node, property) = static_cast<uiAction_t const*>(rawValue);
714 	} else if (property->type == V_UI_SPRITEREF) {
715 		Com_GetValue<uiSprite_t const*>(node, property) = static_cast<uiSprite_t const*>(rawValue);
716 	} else {
717 		Com_Error(ERR_FATAL, "UI_NodeSetPropertyFromRAW: Property type '%d' unsupported", property->type);
718 	}
719 	UI_Node_PropertyChanged(node, property);
720 }
721 
722 /**
723  * @brief Set node property
724  */
UI_NodeSetProperty(uiNode_t * node,const value_t * property,const char * value)725 bool UI_NodeSetProperty (uiNode_t* node, const value_t* property, const char* value)
726 {
727 	const int specialType = property->type & V_UI_MASK;
728 	int result;
729 	size_t bytes;
730 
731 	switch (specialType) {
732 	case V_NOT_UI:	/* common type */
733 		result = Com_ParseValue(node, value, property->type, property->ofs, property->size, &bytes);
734 		if (result != RESULT_OK) {
735 			Com_Printf("UI_NodeSetProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
736 			return false;
737 		}
738 		UI_Node_PropertyChanged(node, property);
739 		return true;
740 
741 	case V_UI_CVAR:	/* cvar */
742 		switch ((int)property->type) {
743 		case V_UI_CVAR:
744 			if (Q_strstart(value, "*cvar:")) {
745 				char*& b = Com_GetValue<char*>(node, property);
746 				UI_FreeStringProperty(b);
747 				b = Mem_PoolStrDup(value, ui_dynStringPool, 0);
748 				UI_Node_PropertyChanged(node, property);
749 				return true;
750 			}
751 			break;
752 		case V_CVAR_OR_FLOAT:
753 			{
754 				float f;
755 
756 				if (Q_strstart(value, "*cvar:")) {
757 					char*& b = Com_GetValue<char*>(node, property);
758 					UI_FreeStringProperty(b);
759 					b = Mem_PoolStrDup(value, ui_dynStringPool, 0);
760 					UI_Node_PropertyChanged(node, property);
761 					return true;
762 				}
763 
764 				result = Com_ParseValue(&f, value, V_FLOAT, 0, sizeof(f), &bytes);
765 				if (result != RESULT_OK) {
766 					Com_Printf("UI_NodeSetProperty: Invalid value for property '%s': %s\n", property->string, Com_GetLastParseError());
767 					return false;
768 				}
769 
770 				void* const b = Com_GetValue<void*>(node, property);
771 				if (char const* const cvar = Q_strstart((char const*)b, "*cvar:"))
772 					Cvar_SetValue(cvar, f);
773 				else
774 					*(float*) b = f;
775 				UI_Node_PropertyChanged(node, property);
776 				return true;
777 			}
778 		case V_CVAR_OR_LONGSTRING:
779 		case V_CVAR_OR_STRING:
780 			{
781 				char*& b = Com_GetValue<char*>(node, property);
782 				UI_FreeStringProperty(b);
783 				b = Mem_PoolStrDup(value, ui_dynStringPool, 0);
784 				UI_Node_PropertyChanged(node, property);
785 				return true;
786 			}
787 		}
788 		break;
789 
790 	case V_UI:
791 		switch ((int)property->type) {
792 		case V_UI_SPRITEREF:
793 			{
794 				uiSprite_t* sprite = UI_GetSpriteByName(value);
795 				Com_GetValue<uiSprite_t const*>(node, property) = sprite;
796 				return true;
797 			}
798 		}
799 		break;
800 	}
801 
802 	Com_Printf("UI_NodeSetProperty: Unimplemented type for property '%s@%s'\n", UI_GetPath(node), property->string);
803 	return false;
804 }
805 
806 /**
807  * @brief Return a string from a node property
808  * @param[in] node Requested node
809  * @param[in] property Requested property
810  * @return Return a string value of a property, else nullptr, if the type is not supported
811  */
UI_GetStringFromNodeProperty(const uiNode_t * node,const value_t * property)812 const char* UI_GetStringFromNodeProperty (const uiNode_t* node, const value_t* property)
813 {
814 	const int baseType = property->type & V_UI_MASK;
815 	assert(node);
816 	assert(property);
817 
818 	switch (baseType) {
819 	case V_NOT_UI: /* common type */
820 		return Com_ValueToStr(node, property->type, property->ofs);
821 	case V_UI_CVAR:
822 		switch ((int)property->type) {
823 		case V_CVAR_OR_FLOAT:
824 			{
825 				const float f = UI_GetReferenceFloat(node, Com_GetValue<void*>(node, property));
826 				const int i = f;
827 				if (f == i)
828 					return va("%i", i);
829 				else
830 					return va("%f", f);
831 			}
832 		case V_CVAR_OR_LONGSTRING:
833 		case V_CVAR_OR_STRING:
834 		case V_UI_CVAR:
835 			return UI_GetReferenceString(node, Com_GetValue<char*>(node, property));
836 		}
837 		break;
838 	default:
839 		break;
840 	}
841 
842 	Com_Printf("UI_GetStringFromNodeProperty: Unsupported string getter for property type 0x%X (%s@%s)\n", property->type, UI_GetPath(node), property->string);
843 	return nullptr;
844 }
845 
846 /**
847  * @brief Return a float from a node property
848  * @param[in] node Requested node
849  * @param[in] property Requested property
850  * @return Return the float value of a property, else 0, if the type is not supported
851  * @note If the type is not supported, a waring is reported to the console
852  */
UI_GetFloatFromNodeProperty(const uiNode_t * node,const value_t * property)853 float UI_GetFloatFromNodeProperty (const uiNode_t* node, const value_t* property)
854 {
855 	assert(node);
856 
857 	if (property->type == V_FLOAT) {
858 		return Com_GetValue<float>(node, property);
859 	} else if ((property->type & V_UI_MASK) == V_UI_CVAR) {
860 		void* const b = Com_GetValue<void*>(node, property);
861 		if (char const* const cvarName = Q_strstart((char const*)b, "*cvar:")) {
862 			const cvar_t* cvar = Cvar_Get(cvarName, "", 0, "UI script cvar property");
863 			return cvar->value;
864 		} else if (property->type == V_CVAR_OR_FLOAT) {
865 			return *(const float*) b;
866 		} else if (property->type == V_CVAR_OR_STRING) {
867 			return atof((const char*)b);
868 		}
869 	} else if (property->type == V_INT) {
870 		return Com_GetValue<int>(node, property);
871 	} else if (property->type == V_BOOL) {
872 		return Com_GetValue<bool>(node, property);
873 	} else {
874 #ifdef DEBUG
875 		Com_Printf("UI_GetFloatFromNodeProperty: Unimplemented float getter for property '%s@%s'. If it should return a float, request it.\n", UI_GetPath(node), property->string);
876 #else
877 		Com_Printf("UI_GetFloatFromNodeProperty: Property '%s@%s' can't return a float\n", UI_GetPath(node), property->string);
878 #endif
879 	}
880 
881 	return 0;
882 }
883 
884 /**
885  * @brief Invalidate a node and all his parent to request a layout update
886  */
UI_Invalidate(uiNode_t * node)887 void UI_Invalidate (uiNode_t* node)
888 {
889 	while (node) {
890 		if (node->invalidated)
891 			return;
892 		node->invalidated = true;
893 		node = node->parent;
894 	}
895 }
896 
897 /**
898  * @brief Validate a node tree
899  */
UI_Validate(uiNode_t * node)900 void UI_Validate (uiNode_t* node)
901 {
902 	if (node->invalidated)
903 		UI_Node_DoLayout(node);
904 }
905