1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
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 2
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.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 
20 //
21 // gui_events.c
22 //
23 
24 #include "gui_local.h"
25 
26 /*
27 =============================================================================
28 
29 	NAMED EVENTS
30 
31 =============================================================================
32 */
33 
34 /*
35 ==================
36 GUI_QueueWindowNamedEvent
37 
38 Queues a named event to trigger on a specified window.
39 ==================
40 */
GUI_QueueWindowNamedEvent(gui_t * gui,char * name,qBool warn)41 static void GUI_QueueWindowNamedEvent (gui_t *gui, char *name, qBool warn)
42 {
43 	event_t	*event;
44 	int		i;
45 
46 	assert (name && name[0]);
47 	if (!name || !name[0])
48 		return;
49 
50 	// Only send if the window is visible
51 	if (!FRVALUE (gui, FR_VISIBLE)
52 	|| FRVALUE (gui, FR_NO_EVENTS))
53 		return;
54 
55 	// Find it in the event list
56 	for (i=0, event=&gui->eventList[0] ; i<gui->numEvents ; event++, i++) {
57 		if (event->type != WEV_NAMED)
58 			continue;
59 		if (Q_stricmp (name, event->named))
60 			continue;
61 		break;
62 	}
63 
64 	// Not found
65 	if (i == gui->numEvents) {
66 		if (warn)
67 			Com_Printf (PRNT_WARNING, "WARNING: Named event '%s' not found in '%s'\n", name, gui->name);
68 		return;
69 	}
70 
71 	// No room!
72 	if (gui->numQueued+1 >= MAX_GUI_EVENTS)
73 		return;
74 
75 	// Make sure it's not already queued
76 	for (i=0 ; i<gui->numQueued ; i++) {
77 		if (gui->queueList[i] == event)
78 			return;
79 	}
80 
81 	// Append
82 	gui->queueList[gui->numQueued++] = event;
83 }
84 
85 
86 /*
87 ==================
88 GUI_NamedGlobalEvent
89 
90 Affects all opened GUIs.
91 ==================
92 */
GUI_NamedGlobalEvent(char * name)93 void GUI_NamedGlobalEvent (char *name)
94 {
95 	gui_t	*gui;
96 	int		i;
97 
98 	// Recurse through each open GUI and apply
99 	for (i=0, gui=cl_guiState.openLayers[0] ; i<cl_guiState.numLayers ; gui++, i++)
100 		GUI_NamedGUIEvent (gui, name);
101 }
102 
103 
104 /*
105 ==================
106 GUI_NamedGUIEvent
107 
108 Affects only the specified GUI.
109 ==================
110 */
GUI_NamedGUIEvent(gui_t * gui,char * name)111 void GUI_NamedGUIEvent (gui_t *gui, char *name)
112 {
113 	gui_t	*child;
114 	int		i;
115 
116 	// Queue any matching events
117 	GUI_QueueWindowNamedEvent (gui, name, qFalse);
118 
119 	// Recurse down the children
120 	for (i=0, child=gui->childList ; i<gui->numChildren ; child++, i++)
121 		GUI_NamedGUIEvent (child, name);
122 }
123 
124 /*
125 =============================================================================
126 
127 	ACTION PROCESSING
128 
129 =============================================================================
130 */
131 
132 /*
133 ==================
134 GUI_SetAction
135 ==================
136 */
GUI_SetAction(gui_t * gui,eva_set_t * setAction)137 static void GUI_SetAction (gui_t *gui, eva_set_t *setAction)
138 {
139 	gui_t	*queueWindow;
140 	int		queueEvent;
141 	int		i;
142 
143 	if (!setAction->destWindowPtr)
144 		return;
145 
146 	queueEvent = WEV_NONE;
147 
148 	switch (setAction->destType) {
149 	case EVA_SETDEST_FLOAT|EVA_SETDEST_STORAGE:
150 		switch (setAction->destRegister) {
151 		case FR_VISIBLE:
152 			if (FRVALUE (setAction->destWindowPtr, setAction->destRegister) != setAction->srcStorage[0]) {
153 				gui->shared->cursor.mouseMoved = qTrue;
154 				queueWindow = setAction->destWindowPtr;
155 				if (FRVALUE (setAction->destWindowPtr, setAction->destRegister))
156 					queueEvent = WEV_INIT;
157 				else
158 					queueEvent = WEV_SHUTDOWN;
159 			}
160 			break;
161 		}
162 
163 		switch (setAction->srcType) {
164 		case EVA_SETSRC_STORAGE:
165 			setAction->destWindowPtr->d.floatRegisters[setAction->destRegister].storage = setAction->srcStorage[0];
166 			setAction->destWindowPtr->d.floatRegisters[setAction->destRegister].var = &setAction->destWindowPtr->d.floatRegisters[setAction->destRegister].storage;
167 			break;
168 		case EVA_SETSRC_DEF:
169 			break;
170 		case EVA_SETSRC_GUIVAR:
171 			break;
172 		}
173 		break;
174 
175 	case EVA_SETDEST_VEC|EVA_SETDEST_STORAGE:
176 		for (i=0 ; i<setAction->destNumVecs ; i++)
177 			setAction->destWindowPtr->d.vecRegisters[setAction->destRegister].storage[i] = setAction->srcStorage[i];
178 
179 		setAction->destWindowPtr->d.vecRegisters[setAction->destRegister].var = setAction->destWindowPtr->d.vecRegisters[setAction->destRegister].storage;
180 		break;
181 
182 	case EVA_SETDEST_FLOAT|EVA_SETDEST_DEF:
183 		break;
184 
185 	case EVA_SETDEST_VEC|EVA_SETDEST_DEF:
186 		break;
187 
188 	default:
189 		assert (0);
190 		break;
191 	}
192 
193 	// Trigger events
194 	if (queueEvent != WEV_NONE)
195 		GUI_QueueTrigger (queueWindow, queueEvent);
196 }
197 
198 
199 /*
200 ==================
201 GUI_RunEvent
202 
203 Processes event actions.
204 ==================
205 */
GUI_RunEvent(gui_t * gui,event_t * event)206 static void GUI_RunEvent (gui_t *gui, event_t *event)
207 {
208 	evAction_t	*action;
209 	int			i;
210 
211 	for (i=0, action=event->actionList ; i<event->numActions ; action++, i++) {
212 		switch (action->type) {
213 		case EVA_CLOSE:
214 			gui->shared->queueClose = qTrue;
215 			break;
216 
217 		case EVA_COMMAND:
218 			assert (action->command && action->command[0]);
219 			Cbuf_AddText (action->command);
220 			break;
221 
222 		case EVA_IF:
223 			break;
224 
225 		case EVA_LOCAL_SOUND:
226 			Snd_StartLocalSound (action->localSound->sound, action->localSound->volume);
227 			break;
228 
229 		case EVA_NAMED_EVENT:
230 			GUI_QueueWindowNamedEvent (action->named->destWindowPtr, action->named->eventName, qTrue);
231 			break;
232 
233 		case EVA_RESET_TIME:
234 			gui->openTime = Sys_UMilliseconds () - action->resetTime;
235 			gui->time = gui->lastTime = action->resetTime;
236 			break;
237 
238 		case EVA_SET:
239 			GUI_SetAction (gui, action->set);
240 			break;
241 
242 		case EVA_STOP_TRANSITIONS:
243 			break;
244 		case EVA_TRANSITION:
245 			break;
246 		}
247 	}
248 }
249 
250 /*
251 =============================================================================
252 
253 	DEFAULT EVENTS
254 
255 =============================================================================
256 */
257 
258 /*
259 ==================
260 GUI_CheckDefDefault
261 ==================
262 */
GUI_CheckDefDefault(gui_t * gui,evType_t type)263 static void GUI_CheckDefDefault (gui_t *gui, evType_t type)
264 {
265 	switch (type) {
266 	case WEV_ACTION:
267 		if (gui->d.checkDef->liveUpdate) {
268 			if (!Q_stricmp (gui->d.checkDef->cvar->string, gui->d.checkDef->values[0]))
269 				Cvar_VariableSet (gui->d.checkDef->cvar, gui->d.checkDef->values[1], qFalse);
270 			else
271 				Cvar_VariableSet (gui->d.checkDef->cvar, gui->d.checkDef->values[0], qFalse);
272 		}
273 		else {
274 			assert (0);
275 		}
276 		break;
277 	}
278 }
279 
280 /*
281 =============================================================================
282 
283 	EVENT QUEUE
284 
285 	Events can be saved here as "triggers", so that on the next render call
286 	they will trigger the appropriate event.
287 =============================================================================
288 */
289 
290 /*
291 ==================
292 GUI_TriggerEvents
293 
294 Triggers any queued events, along with the per-frame and time-based events.
295 ==================
296 */
GUI_TriggerEvents(gui_t * gui)297 void GUI_TriggerEvents (gui_t *gui)
298 {
299 	gui_t	*child;
300 	event_t	*event;
301 	int		i;
302 
303 	if (!FRVALUE (gui, FR_VISIBLE)
304 	|| FRVALUE (gui, FR_NO_EVENTS)) {
305 		// Clear default events
306 		gui->numDefaultQueued = 0;
307 
308 		// Clear queued triggers
309 		gui->numQueued = 0;
310 		return;
311 	}
312 
313 	// Update time
314 	gui->time = Sys_UMilliseconds () - gui->openTime;
315 
316 	// FIXME: something needs to be done about uint32 wrapping!!
317 	/*if (gui->lastTime > gui->time) {
318 		gui->lastTime = gui->time;
319 	}*/
320 
321 	// Process events
322 	for (i=0, event=gui->eventList ; i<gui->numEvents ; event++, i++) {
323 		// This is triggered every frame
324 		if (event->type == WEV_FRAME) {
325 			GUI_RunEvent (gui, event);
326 			continue;
327 		}
328 
329 		// Time-based triggers
330 		if (event->type == WEV_TIME) {
331 			if (event->onTime >= gui->lastTime && event->onTime < gui->time)
332 				GUI_RunEvent (gui, event);
333 			continue;
334 		}
335 		break;
336 	}
337 
338 	// Fire queued triggers
339 	for (i=0 ; i<gui->numQueued ; i++)
340 		GUI_RunEvent (gui, gui->queueList[i]);
341 	gui->numQueued = 0;
342 
343 	// Fire default events
344 	for (i=0 ; i<gui->numDefaultQueued ; i++) {
345 		switch (gui->type) {
346 		case WTP_CHECKBOX:
347 			GUI_CheckDefDefault (gui, gui->defaultQueueList[i]);
348 			break;
349 		}
350 	}
351 	gui->numDefaultQueued = 0;
352 
353 	// Store the current time for the next trip
354 	gui->lastTime = gui->time;
355 
356 	// Recurse down the children
357 	for (i=0, child=gui->childList ; i<gui->numChildren ; child++, i++)
358 		GUI_TriggerEvents (child);
359 }
360 
361 
362 /*
363 ==================
364 GUI_QueueTrigger
365 ==================
366 */
GUI_QueueTrigger(gui_t * gui,evType_t type)367 void GUI_QueueTrigger (gui_t *gui, evType_t type)
368 {
369 	gui_t	*child;
370 	event_t	*event;
371 	int		i;
372 
373 	if (!gui)
374 		return;
375 
376 	// Mouse enter/exit is sent every frame, regardless of the state actually changing
377 	// or not. This is purely to make the exit event as reliable as possible.
378 	switch (type) {
379 	case WEV_ACTION:
380 		break;
381 
382 	case WEV_ESCAPE:
383 		break;
384 
385 	case WEV_INIT:
386 		if (gui->inited)
387 			return;
388 		gui->inited = qTrue;
389 		break;
390 
391 	case WEV_SHUTDOWN:
392 		if (!gui->inited)
393 			return;
394 		gui->inited = qFalse;
395 
396 		GUI_QueueTrigger (gui, WEV_MOUSE_EXIT);
397 		for (i=0, child=gui->childList ; i<gui->numChildren ; child++, i++)
398 			GUI_QueueTrigger (child, WEV_SHUTDOWN);
399 		break;
400 
401 	case WEV_MOUSE_ENTER:
402 		if (gui->mouseEntered && !gui->mouseExited)
403 			return;
404 		gui->mouseEntered = qTrue;
405 		gui->mouseExited = qFalse;
406 		break;
407 
408 	case WEV_MOUSE_EXIT:
409 		if (!gui->mouseEntered && gui->mouseExited)
410 			return;
411 		gui->mouseEntered = qFalse;
412 		gui->mouseExited = qTrue;
413 		break;
414 
415 	case WEV_NAMED:
416 		// These are queued up elsewhere!
417 		assert (0);
418 		return;
419 
420 	case WEV_FRAME:
421 	case WEV_TIME:
422 		// These should *NEVER* be queued!!
423 		// They're forcibly ran in GUI_TriggerEvents at the beginning of every frame anyways!
424 		assert (0);
425 		return;
426 
427 	default:
428 		// Should never happen!
429 		assert (0);
430 		return;
431 	}
432 
433 	// Add to default action list (if not found already)
434 	for (i=0 ; i<gui->numDefaultQueued ; i++) {
435 		if (gui->defaultQueueList[i] == type)
436 			break;
437 	}
438 	if (i == gui->numDefaultQueued) {
439 		if (gui->numDefaultQueued < MAX_GUI_EVENTS)
440 			gui->defaultQueueList[gui->numDefaultQueued++] = type;
441 	}
442 
443 	// Find it in the event list
444 	for (i=0, event=&gui->eventList[0] ; i<gui->numEvents ; event++, i++) {
445 		if (event->type == type)
446 			break;
447 	}
448 	if (i == gui->numEvents)
449 		return;	// Not found
450 
451 	// If already in the list, ignore
452 	for (i=0 ; i<gui->numQueued ; i++) {
453 		if (gui->queueList[i] == event)
454 			return;
455 	}
456 
457 	// Append
458 	if (gui->numQueued < MAX_GUI_EVENTS)
459 		gui->queueList[gui->numQueued++] = event;
460 
461 	// Update
462 	gui->shared->cursor.mouseMoved = qTrue;
463 
464 	// Post-process sub-triggers
465 	switch (type) {
466 	case WEV_INIT:
467 		for (i=0, child=gui->childList ; i<gui->numChildren ; child++, i++)
468 			GUI_QueueTrigger (child, WEV_INIT);
469 		GUI_QueueTrigger (gui, WEV_MOUSE_ENTER);
470 		break;
471 	}
472 }
473