1 /**
2  * @file
3  */
4 
5 /*
6 Copyright (C) 2002-2013 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 "../../cl_shared.h"
26 #include "../ui_nodes.h"
27 #include "../ui_parse.h"
28 #include "../ui_actions.h"
29 #include "../ui_behaviour.h"
30 #include "ui_node_window.h"
31 #include "ui_node_special.h"
32 #include "ui_node_abstractnode.h"
33 
34 /**
35  * @brief Call after the script initialized the node
36  * @todo special cases should be managed as a common property event of the parent node
37  */
onLoaded(uiNode_t * node)38 void uiFuncNode::onLoaded (uiNode_t* node)
39 {
40 	const value_t* prop = UI_GetPropertyFromBehaviour(node->parent->behaviour, node->name);
41 	if (prop && prop->type == V_UI_ACTION) {
42 		/** @todo move this code into the parser (cause property should not create a func node) */
43 		uiAction_t*& value = Com_GetValue<uiAction_t*>(node->parent, prop);
44 		if (value != nullptr && node->super == nullptr) {
45 			Com_Printf("UI_FuncNodeLoaded: '%s' already defined. Previous function ignored (\"%s\")\n", prop->string, UI_GetPath(node));
46 		}
47 		value = node->onClick;
48 	}
49 }
50 
UI_RegisterFuncNode(uiBehaviour_t * behaviour)51 void UI_RegisterFuncNode (uiBehaviour_t* behaviour)
52 {
53 	behaviour->name = "func";
54 	behaviour->isVirtual = true;
55 	behaviour->isFunction = true;
56 	behaviour->manager = UINodePtr(new uiFuncNode());
57 }
58 
UI_RegisterNullNode(uiBehaviour_t * behaviour)59 void UI_RegisterNullNode (uiBehaviour_t* behaviour)
60 {
61 	behaviour->name = "";
62 	behaviour->isVirtual = true;
63 }
64 
65 /**
66  * @brief Callback to execute a confunc
67  */
UI_ConfuncCommand_f(void)68 static void UI_ConfuncCommand_f (void)
69 {
70 	uiNode_t* node = static_cast<uiNode_t*>(Cmd_Userdata());
71 	assert(node);
72 	assert(UI_NodeInstanceOf(node, "confunc"));
73 	UI_ExecuteConFuncActions(node, node->onClick);
74 }
75 
76 /**
77  * @brief Checks whether the given node is a virtual confunc that can be overridden from inheriting nodes.
78  * @param node The node to check (must be a confunc node).
79  * @return @c true if the given node is a dummy node, @c false otherwise.
80  */
UI_ConFuncIsVirtual(const uiNode_t * const node)81 static bool UI_ConFuncIsVirtual (const uiNode_t* const node)
82 {
83 	/* magic way to know if it is a dummy node (used for inherited confunc) */
84 	const uiNode_t* dummy = static_cast<const uiNode_t*>(Cmd_GetUserdata(node->name));
85 	assert(node);
86 	assert(UI_NodeInstanceOf(node, "confunc"));
87 	return (dummy != nullptr && dummy->parent == nullptr);
88 }
89 
90 /**
91  * @brief Call after the script initialized the node
92  */
onLoaded(uiNode_t * node)93 void uiConFuncNode::onLoaded (uiNode_t* node)
94 {
95 	/* register confunc non inherited */
96 	if (node->super == nullptr) {
97 		/* don't add a callback twice */
98 		if (!Cmd_Exists(node->name)) {
99 			Cmd_AddCommand(node->name, UI_ConfuncCommand_f, "Confunc callback");
100 			Cmd_AddUserdata(node->name, node);
101 		} else {
102 			Com_Printf("UI_ParseNodeBody: Command name for confunc '%s' already registered\n", UI_GetPath(node));
103 		}
104 	} else {
105 		/* convert a confunc to an "inherited" confunc if it is possible */
106 		if (Cmd_Exists(node->name)) {
107 			if (UI_ConFuncIsVirtual(node))
108 				return;
109 		}
110 
111 		uiNode_t* dummy = UI_AllocNode(node->name, "confunc", false);
112 		Cmd_AddCommand(node->name, UI_ConfuncCommand_f, "Inherited confunc callback");
113 		Cmd_AddUserdata(dummy->name, dummy);
114 	}
115 }
116 
clone(const uiNode_t * source,uiNode_t * clone)117 void uiConFuncNode::clone (const uiNode_t* source, uiNode_t* clone)
118 {
119 	onLoaded(clone);
120 }
121 
122 /**
123  * @brief Callback every time the parent window is opened (pushed into the active window stack)
124  */
onWindowOpened(uiNode_t * node,linkedList_t * params)125 void uiConFuncNode::onWindowOpened (uiNode_t* node, linkedList_t* params)
126 {
127 	if (UI_ConFuncIsVirtual(node)) {
128 		const value_t* property = UI_GetPropertyFromBehaviour(node->behaviour, "onClick");
129 		uiNode_t* userData = static_cast<uiNode_t*>(Cmd_GetUserdata(node->name));
130 		UI_AddListener(userData, property, node);
131 	}
132 }
133 
134 /**
135  * @brief Callback every time the parent window is closed (pop from the active window stack)
136  */
onWindowClosed(uiNode_t * node)137 void uiConFuncNode::onWindowClosed (uiNode_t* node)
138 {
139 	if (UI_ConFuncIsVirtual(node)) {
140 		const value_t* property = UI_GetPropertyFromBehaviour(node->behaviour, "onClick");
141 		uiNode_t* userData = static_cast<uiNode_t*>(Cmd_GetUserdata(node->name));
142 		UI_RemoveListener(userData, property, node);
143 	}
144 }
145 
UI_RegisterConFuncNode(uiBehaviour_t * behaviour)146 void UI_RegisterConFuncNode (uiBehaviour_t* behaviour)
147 {
148 	behaviour->name = "confunc";
149 	behaviour->isVirtual = true;
150 	behaviour->isFunction = true;
151 	behaviour->manager = UINodePtr(new uiConFuncNode());
152 }
153 
UI_CvarListenerNodeCallback(const char * cvarName,const char * oldValue,const char * newValue,void * data)154 static void UI_CvarListenerNodeCallback (const char* cvarName, const char* oldValue, const char* newValue, void* data)
155 {
156 	linkedList_t* list = static_cast<linkedList_t*>(data);
157 	linkedList_t* params = nullptr;
158 	LIST_AddString(&params, oldValue);
159 	LIST_AddString(&params, newValue);
160 	while (list) {
161 		uiNode_t* node = static_cast<uiNode_t*>(list->data);
162 		UI_ExecuteEventActionsEx(node, node->onClick, params);
163 		list = list->next;
164 	}
165 }
166 
167 /**
168  * @brief Callback every time the parent window is opened (pushed into the active window stack)
169  */
UI_CvarListenerNodeBind(uiNode_t * node)170 static void UI_CvarListenerNodeBind (uiNode_t* node)
171 {
172 	cvarChangeListener_t* l;
173 	l = Cvar_RegisterChangeListener(node->name, UI_CvarListenerNodeCallback);
174 	if (l == nullptr) {
175 		Com_Printf("Node %s is not binded: cvar %s not found\n", UI_GetPath(node), node->name);
176 		return;
177 	}
178 
179 	if (LIST_GetPointer(static_cast<linkedList_t*>(l->data), node) == nullptr) {
180 		LIST_AddPointer(reinterpret_cast<linkedList_t**>(&l->data), node);
181 	}
182 }
183 
184 /**
185  * @brief Callback every time the parent window is closed (pop from the active window stack)
186  */
onWindowClosed(uiNode_t * node)187 void uiCvarNode::onWindowClosed (uiNode_t* node)
188 {
189 	cvar_t* var;
190 
191 	var = Cvar_FindVar(node->name);
192 	if (var == nullptr)
193 		return;
194 
195 	cvarChangeListener_t* l = var->changeListener;
196 	while (l) {
197 		if (l->exec == UI_CvarListenerNodeCallback) {
198 			LIST_Remove(reinterpret_cast<linkedList_t**>(&l->data), node);
199 			if (LIST_IsEmpty(static_cast<linkedList_t*>(l->data))) {
200 				Cvar_UnRegisterChangeListener(node->name, UI_CvarListenerNodeCallback);
201 			}
202 			break;
203 		}
204 		l = l->next;
205 	}
206 }
207 
onWindowOpened(uiNode_t * node,linkedList_t * params)208 void uiCvarNode::onWindowOpened (uiNode_t* node, linkedList_t* params)
209 {
210 	UI_CvarListenerNodeBind(node);
211 }
212 
deleteNode(uiNode_t * node)213 void uiCvarNode::deleteNode (uiNode_t* node)
214 {
215 	onWindowClosed(node);
216 }
217 
clone(const uiNode_t * source,uiNode_t * clone)218 void uiCvarNode::clone (const uiNode_t* source, uiNode_t* clone)
219 {
220 	UI_CvarListenerNodeBind(clone);
221 }
222 
UI_CvarListenerNodeForceBind(uiNode_t * node,const uiCallContext_t * context)223 static void UI_CvarListenerNodeForceBind (uiNode_t* node, const uiCallContext_t* context)
224 {
225 	UI_CvarListenerNodeBind(node);
226 }
227 
UI_RegisterCvarFuncNode(uiBehaviour_t * behaviour)228 void UI_RegisterCvarFuncNode (uiBehaviour_t* behaviour)
229 {
230 	behaviour->name = "cvarlistener";
231 	behaviour->isVirtual = true;
232 	behaviour->isFunction = true;
233 	behaviour->manager = UINodePtr(new uiCvarNode());
234 	/* Force to bind the node to the cvar */
235 	UI_RegisterNodeMethod(behaviour, "forceBind", UI_CvarListenerNodeForceBind);
236 }
237