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 // ui_cursor.c
22 //
23 
24 #include "ui_local.h"
25 
26 static cVar_t		*ui_cursorX;
27 static cVar_t		*ui_cursorY;
28 
29 /*
30 =======================================================================
31 
32 	MENU MOUSE CURSOR
33 
34 =======================================================================
35 */
36 
37 /*
38 =============
39 UI_DrawMouseCursor
40 =============
41 */
UI_DrawMouseCursor(void)42 void UI_DrawMouseCursor (void)
43 {
44 	struct shader_s *cursor;
45 
46 	if (uiState.cursorOverItem)
47 		cursor = uiMedia.cursorHoverShader;
48 	else
49 		cursor = uiMedia.cursorShader;
50 
51 	cgi.R_DrawPic (cursor, 0, uiState.cursorX + 1, uiState.cursorY + 1,
52 		uiState.cursorW * clamp (UIFT_SCALE, 0.5f, 1),
53 		uiState.cursorH * clamp (UIFT_SCALE, 0.5f, 1),
54 		0, 0, 1, 1, Q_colorWhite);
55 }
56 
57 
58 /*
59 =============
60 UI_FindMouseItem
61 
62 Finds the item that the mouse is over for the specified framework
63 =============
64 */
UI_FindMouseItem(uiFrameWork_t * fw)65 static void UI_FindMouseItem (uiFrameWork_t *fw)
66 {
67 	int		i;
68 
69 	// Search for the current item that the mouse is over
70 	uiState.mouseItem = NULL;
71 	for (i=0 ; i<fw->numItems ; i++) {
72 		// Skip NOSELECT items
73 		if (((uiCommon_t *) fw->items[i])->flags & UIF_NOSELECT)
74 			continue;
75 
76 		// Check if the mouse is colliding
77 		if (uiState.cursorX > ((uiCommon_t *) fw->items[i])->botRight[0]
78 		|| uiState.cursorY > ((uiCommon_t *) fw->items[i])->botRight[1]
79 		|| uiState.cursorX < ((uiCommon_t *) fw->items[i])->topLeft[0]
80 		|| uiState.cursorY < ((uiCommon_t *) fw->items[i])->topLeft[1])
81 			continue;
82 
83 		uiState.cursorItem = fw->items[i];
84 		uiState.mouseItem = fw->items[i];
85 
86 		if (fw->cursor == i)
87 			break;
88 		uiState.newCursorItem = qTrue;
89 		fw->cursor = i;
90 		break;
91 	}
92 
93 	// Rollover cursor image
94 	uiState.cursorOverItem = (uiState.mouseItem != NULL);
95 }
96 
97 
98 /*
99 =============
100 UI_UpdateMousePos
101 =============
102 */
UI_UpdateMousePos(void)103 void UI_UpdateMousePos (void)
104 {
105 	if (!uiState.activeUI || !uiState.activeUI->numItems || uiState.cursorLock)
106 		return;
107 
108 	uiState.newCursorItem = qFalse;
109 	UI_FindMouseItem (uiState.activeUI);
110 }
111 
112 
113 /*
114 =============
115 UI_MoveMouse
116 =============
117 */
UI_MoveMouse(float x,float y)118 void UI_MoveMouse (float x, float y)
119 {
120 	if (uiState.cursorLock)
121 		return;
122 
123 	// Filter
124 	if (ui_filtermouse->intVal) {
125 		uiState.cursorX = (uiState.cursorX * 2 + (x * ui_sensitivity->floatVal)) * 0.5f;
126 		uiState.cursorY = (uiState.cursorY * 2 + (y * ui_sensitivity->floatVal)) * 0.5f;
127 	}
128 	else {
129 		uiState.cursorX += x * ui_sensitivity->floatVal;
130 		uiState.cursorY += y * ui_sensitivity->floatVal;
131 	}
132 
133 	// Clamp
134 	uiState.cursorX = clamp (uiState.cursorX, 2, cg.refConfig.vidWidth - 2);
135 	uiState.cursorY = clamp (uiState.cursorY, 2, cg.refConfig.vidHeight - 2);
136 
137 	if (x || y)
138 		UI_UpdateMousePos ();
139 }
140 
141 
142 /*
143 =============
144 UI_SetupBounds
145 =============
146 */
Action_Setup(uiAction_t * s)147 static void Action_Setup (uiAction_t *s)
148 {
149 	if (!s->generic.name)
150 		return;
151 
152 	s->generic.topLeft[0] = s->generic.x + s->generic.parent->x;
153 	s->generic.topLeft[1] = s->generic.y + s->generic.parent->y;
154 
155 	s->generic.botRight[1] = s->generic.topLeft[1] + UISIZE_TYPE (s->generic.flags);
156 
157 	if (s->generic.flags & UIF_CENTERED)
158 		s->generic.topLeft[0] -= ((Q_ColorCharCount (s->generic.name, (int)strlen (s->generic.name)) * UISIZE_TYPE (s->generic.flags))*0.5);
159 	else if (!(s->generic.flags & UIF_LEFT_JUSTIFY))
160 		s->generic.topLeft[0] += LCOLUMN_OFFSET - ((Q_ColorCharCount (s->generic.name, (int)strlen (s->generic.name))) * UISIZE_TYPE (s->generic.flags));
161 
162 	s->generic.botRight[0] = s->generic.topLeft[0] + (Q_ColorCharCount (s->generic.name, (int)strlen (s->generic.name)) * UISIZE_TYPE (s->generic.flags));
163 }
164 
Field_Setup(uiField_t * s)165 static void Field_Setup (uiField_t *s)
166 {
167 	s->generic.topLeft[0] = s->generic.x + s->generic.parent->x;
168 
169 	if (s->generic.name)
170 		s->generic.topLeft[0] -= (Q_ColorCharCount (s->generic.name, (int)strlen (s->generic.name)) + 1) * UISIZE_TYPE (s->generic.flags);
171 
172 	s->generic.topLeft[1] = s->generic.y + s->generic.parent->y - (UISIZE_TYPE (s->generic.flags) * 0.5);
173 
174 	s->generic.botRight[0] = s->generic.x + s->generic.parent->x + ((s->visibleLength + 2) * UISIZE_TYPE (s->generic.flags));
175 	s->generic.botRight[1] = s->generic.topLeft[1] + UISIZE_TYPE (s->generic.flags)*2;
176 
177 	if (s->generic.flags & UIF_CENTERED) {
178 		s->generic.topLeft[0] -= ((s->visibleLength + 2) * UISIZE_TYPE (s->generic.flags))*0.5;
179 		s->generic.botRight[0] -= ((s->visibleLength + 2) * UISIZE_TYPE (s->generic.flags))*0.5;
180 	}
181 }
182 
MenuImage_Setup(uiImage_t * s)183 static void MenuImage_Setup (uiImage_t *s)
184 {
185 	int		width, height;
186 
187 	if (s->width || s->height) {
188 		width = s->width;
189 		height = s->height;
190 	}
191 	else {
192 		cgi.R_GetImageSize (s->shader, &width, &height);
193 		s->width = width;
194 		s->height = height;
195 	}
196 
197 	width *= UISCALE_TYPE (s->generic.flags);
198 	height *= UISCALE_TYPE (s->generic.flags);
199 
200 	s->generic.topLeft[0] = s->generic.x + s->generic.parent->x;
201 
202 	if (s->generic.flags & UIF_CENTERED)
203 		s->generic.topLeft[0] -= width * 0.5;
204 	else if (s->generic.flags & UIF_LEFT_JUSTIFY)
205 		s->generic.topLeft[0] += LCOLUMN_OFFSET;
206 
207 	s->generic.topLeft[1] = s->generic.y + s->generic.parent->y;
208 	s->generic.botRight[0] = s->generic.topLeft[0] + width;
209 	s->generic.botRight[1] = s->generic.topLeft[1] + height;
210 }
211 
212 // FIXME need calculations to detect when on the left/right side of the slider
213 // so left == less and right == more
214 // if the mouse cursor position is less than the cursor position and is within the bounding box, left
215 // if the mouse cursor position is greater than the cursor position and is within the bounding box, right
Slider_Setup(uiSlider_t * s)216 static void Slider_Setup (uiSlider_t *s)
217 {
218 	float		xsize, ysize;
219 	float		offset;
220 
221 	if (s->generic.name)
222 		offset = Q_ColorCharCount (s->generic.name, (int)strlen (s->generic.name)) * UISIZE_TYPE (s->generic.flags);
223 	else
224 		offset = 0;
225 
226 	s->generic.topLeft[0] = s->generic.x + s->generic.parent->x - offset - UISIZE_TYPE (s->generic.flags);
227 	s->generic.topLeft[1] = s->generic.y + s->generic.parent->y;
228 
229 	xsize = (UISIZE_TYPE (s->generic.flags) * 6) + offset + ((SLIDER_RANGE-1) * UISIZE_TYPE (s->generic.flags));
230 	ysize = UISIZE_TYPE (s->generic.flags);
231 
232 	s->generic.botRight[0] = s->generic.topLeft[0] + xsize;
233 	s->generic.botRight[1] = s->generic.topLeft[1] + ysize;
234 }
235 
SpinControl_Setup(uiList_t * s)236 static void SpinControl_Setup (uiList_t *s)
237 {
238 	float		xsize = 0, ysize = 0;
239 
240 	ysize = UISIZE_TYPE (s->generic.flags);
241 	if (s->generic.name) {
242 		s->generic.topLeft[0] = s->generic.x + s->generic.parent->x - (Q_ColorCharCount (s->generic.name, (int)strlen (s->generic.name)) * UISIZE_TYPE (s->generic.flags)) - UISIZE_TYPE (s->generic.flags);
243 		s->generic.topLeft[1] = s->generic.y + s->generic.parent->y;
244 
245 		xsize = (Q_ColorCharCount (s->generic.name, (int)strlen (s->generic.name)) * UISIZE_TYPE (s->generic.flags)) + UISIZE_TYPE (s->generic.flags)*3;
246 	}
247 
248 	if (s->itemNames && s->itemNames[s->curValue]) {
249 		if (s->itemNames[s->curValue] && (!strchr(s->itemNames[s->curValue], '\n'))) {
250 			if (!s->generic.name)
251 				s->generic.topLeft[0] += UISIZE_TYPE (s->generic.flags)*20;
252 		}
253 		else
254 			ysize += UISIZE_TYPE (s->generic.flags);
255 
256 		if (s->itemNames[s->curValue])
257 			xsize += (Q_ColorCharCount (s->itemNames[s->curValue], (int)strlen (s->itemNames[s->curValue])) * UISIZE_TYPE (s->generic.flags));
258 	}
259 
260 	s->generic.botRight[0] = s->generic.topLeft[0] + xsize;
261 	s->generic.botRight[1] = s->generic.topLeft[1] + ysize;
262 }
263 
UI_SetupBounds(uiFrameWork_t * menu)264 void UI_SetupBounds (uiFrameWork_t *menu)
265 {
266 	uiCommon_t	*item;
267 	int			i;
268 
269 	// Generate collision boxes for items
270 	for (i=0 ; i<menu->numItems ; i++) {
271 		item = (uiCommon_t *)menu->items[i];
272 
273 		if (item->flags & UIF_NOSELECT) {
274 			item->topLeft[0] = item->topLeft[1] = 0;
275 			item->botRight[0] = item->botRight[1] = 0;
276 			continue;
277 		}
278 
279 		switch (item->type) {
280 		case UITYPE_ACTION:
281 			Action_Setup ((uiAction_t *) menu->items[i]);
282 			break;
283 
284 		case UITYPE_FIELD:
285 			Field_Setup ((uiField_t *) menu->items[i]);
286 			break;
287 
288 		case UITYPE_IMAGE:
289 			MenuImage_Setup ((uiImage_t *) menu->items[i]);
290 			break;
291 
292 		case UITYPE_SLIDER:
293 			Slider_Setup ((uiSlider_t *) menu->items[i]);
294 			break;
295 
296 		case UITYPE_SPINCONTROL:
297 			SpinControl_Setup ((uiList_t *) menu->items[i]);
298 			break;
299 		}
300 	}
301 }
302 
303 /*
304 =======================================================================
305 
306 	INITIALIZATION/SHUTDOWN
307 
308 =======================================================================
309 */
310 
311 /*
312 =============
313 UI_CursorInit
314 =============
315 */
UI_CursorInit(void)316 void UI_CursorInit (void)
317 {
318 	uiState.cursorOverItem = qFalse;
319 
320 	// Cursor position
321 	ui_cursorX	= cgi.Cvar_Register ("ui_cursorX",	"-1",	CVAR_READONLY);
322 	ui_cursorY	= cgi.Cvar_Register ("ui_cursorY",	"-1",	CVAR_READONLY);
323 	if (ui_cursorX->floatVal == -1 && ui_cursorY->floatVal == -1) {
324 		uiState.cursorX = cg.refConfig.vidWidth * 0.5f;
325 		uiState.cursorY = cg.refConfig.vidHeight * 0.5f;
326 	}
327 	else {
328 		uiState.cursorX = ui_cursorX->floatVal;
329 		uiState.cursorY = ui_cursorY->floatVal;
330 	}
331 }
332 
333 
334 /*
335 =============
336 UI_CursorShutdown
337 =============
338 */
UI_CursorShutdown(void)339 void UI_CursorShutdown (void)
340 {
341 	// Store the cursor position
342 	cgi.Cvar_VariableSetValue (ui_cursorX, uiState.cursorX, qTrue);
343 	cgi.Cvar_VariableSetValue (ui_cursorY, uiState.cursorY, qTrue);
344 }
345