added comment support to COM_ReadAndTokenizeLine
[divverent/darkplaces.git] / ui.c
1
2 #include "quakedef.h"
3
4 cvar_t ui_showname = {0, "ui_showname", "0"};
5
6 #define ITEM_CLICKABLE 1
7 #define ITEM_DRAWABLE 2
8
9 #define UIKEY_LEFT 1
10 #define UIKEY_RIGHT 2
11 #define UIKEY_UP 3
12 #define UIKEY_DOWN 4
13 #define UIKEY_ENTER 5
14
15 #define UI_MOUSEBUTTONS 3
16
17 static int ui_alive, ui_active;
18 static float ui_mouse_x, ui_mouse_y;
19 static int ui_mousebutton[UI_MOUSEBUTTONS], ui_mouseclick;
20 static int ui_keyui, ui_keyitem;
21 static ui_item_t *ui_keyrealitem;
22
23 static ui_t *ui_list[MAX_UI_COUNT];
24
25 static void ui_start(void)
26 {       
27         ui_mouse_x = vid.conwidth * 0.5;
28         ui_mouse_y = vid.conheight * 0.5;
29         ui_alive = true;
30 }
31
32 static void ui_shutdown(void)
33 {
34         ui_alive = false;
35 }
36
37 static void ui_newmap(void)
38 {
39 }
40
41 static mempool_t *uimempool;
42
43 void ui_init(void)
44 {
45         uimempool = Mem_AllocPool("UI");
46
47         Cvar_RegisterVariable(&ui_showname);
48
49         R_RegisterModule("UI", ui_start, ui_shutdown, ui_newmap);
50 }
51
52 void ui_mouseupdate(float x, float y)
53 {
54         if (ui_alive)
55         {
56                 ui_mouse_x = bound(0, x, vid.conwidth);
57                 ui_mouse_y = bound(0, y, vid.conheight);
58         }
59 }
60
61 void ui_mouseupdaterelative(float x, float y)
62 {
63         if (ui_alive)
64         {
65                 ui_mouse_x += x;
66                 ui_mouse_y += y;
67                 ui_mouse_x = bound(0, ui_mouse_x, vid.conwidth);
68                 ui_mouse_y = bound(0, ui_mouse_y, vid.conheight);
69         }
70 }
71
72 ui_t *ui_create(void)
73 {
74         ui_t *ui;
75         ui = Mem_Alloc(uimempool, sizeof(*ui));
76         if (ui == NULL)
77                 Sys_Error("ui_create: unable to allocate memory for new ui\n");
78         memset(ui, 0, sizeof(*ui));
79         return ui;
80 }
81
82 void ui_free(ui_t *ui)
83 {
84         if (ui)
85                 Mem_Free(ui);
86 }
87
88 void ui_clear(ui_t *ui)
89 {
90         ui->item_count = 0;
91 }
92
93 void ui_item
94 (
95         ui_t *ui, char *basename, int number,
96         float x, float y, char *picname, char *string,
97         float left, float top, float width, float height,
98         void(*leftkey)(void *nativedata1, void *nativedata2, float data1, float data2),
99         void(*rightkey)(void *nativedata1, void *nativedata2, float data1, float data2),
100         void(*enterkey)(void *nativedata1, void *nativedata2, float data1, float data2),
101         void(*mouseclick)(void *nativedata1, void *nativedata2, float data1, float data2, float xfrac, float yfrac),
102         void *nativedata1, void *nativedata2, float data1, float data2
103 )
104 {
105         int i;
106         ui_item_t *it;
107         char itemname[32];
108         snprintf(itemname, sizeof(itemname), "%s%04d", basename, number);
109         for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
110                 if (it->name == NULL || !strncmp(itemname, it->name, 32))
111                         break;
112         if (i == ui->item_count)
113         {
114                 if (i == MAX_UI_ITEMS)
115                 {
116                         Con_Printf("ui_item: ran out of UI item slots\n");
117                         return;
118                 }
119                 ui->item_count++;
120         }
121         memset(it, 0, sizeof(ui_item_t));
122         strlcpy (it->name, itemname, sizeof (it->name));
123         it->flags = 0;
124         if (picname || string)
125         {
126                 it->flags |= ITEM_DRAWABLE;
127                 it->draw_picname = picname;
128                 it->draw_string = string;
129                 it->draw_x = x;
130                 it->draw_y = y;
131         }
132         if (leftkey || rightkey || enterkey || mouseclick)
133         {
134                 it->flags |= ITEM_CLICKABLE;
135                 it->click_x = x + left;
136                 it->click_y = y + top;
137                 it->click_x2 = it->click_x + width;
138                 it->click_y2 = it->click_y + height;
139                 it->leftkey = leftkey;
140                 it->rightkey = rightkey;
141                 it->enterkey = enterkey;
142                 it->mouseclick = mouseclick;
143                 if (it->mouseclick == NULL)
144                         it->mouseclick = (void *)it->enterkey;
145                 if (it->leftkey == NULL)
146                         it->leftkey = it->enterkey;
147                 if (it->rightkey == NULL)
148                         it->rightkey = it->enterkey;
149                 it->nativedata1 = nativedata1;
150                 it->nativedata2 = nativedata2;
151         }
152 }
153
154 void ui_item_remove(ui_t *ui, char *basename, int number)
155 {
156         int i;
157         ui_item_t *it;
158         char itemname[32];
159         snprintf(itemname, sizeof(itemname), "%s%04d", basename, number);
160         for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
161                 if (it->name && !strncmp(itemname, it->name, 32))
162                         break;
163         if (i < ui->item_count)
164                 it->name[0] = 0;
165 }
166
167 ui_item_t *ui_hititem(float x, float y)
168 {
169         int i, j;
170         ui_item_t *it;
171         ui_t *ui;
172         for (j = 0;j < MAX_UI_COUNT;j++)
173                 if ((ui = ui_list[j]))
174                         for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
175                                 if (it->name[0] && (it->flags & ITEM_CLICKABLE))
176                                         if (x >= it->click_x && y >= it->click_y && x < it->click_x2 && y < it->click_y2)
177                                                 return it;
178         return NULL;
179 }
180
181 int ui_uiactive(ui_t *ui)
182 {
183         int i;
184         for (i = 0;i < MAX_UI_COUNT;i++)
185                 if (ui_list[i] == ui)
186                         return true;
187         return false;
188 }
189
190 void ui_activate(ui_t *ui, int yes)
191 {
192         int i;
193         if (yes)
194         {
195                 if (ui_uiactive(ui))
196                         return;
197
198                 for (i = 0;i < MAX_UI_COUNT;i++)
199                 {
200                         if (ui_list[i] == NULL)
201                         {
202                                 ui_list[i] = ui;
203                                 return;
204                         }
205                 }
206
207                 Con_Printf("ui_activate: ran out of active ui list items\n");
208         }
209         else
210         {
211                 for (i = 0;i < MAX_UI_COUNT;i++)
212                 {
213                         if (ui_list[i] == ui)
214                         {
215                                 ui_list[i] = NULL;
216                                 return;
217                         }
218                 }
219         }
220 }
221
222 int ui_isactive(void)
223 {
224         int j;
225         ui_t *ui;
226         if (ui_alive)
227         {
228                 for (j = 0;j < MAX_UI_COUNT;j++)
229                         if ((ui = ui_list[j]))
230                                 if (ui->item_count)
231                                         return true;
232         }
233         return false;
234 }
235
236 #define UI_QUEUE_SIZE 256
237 static qbyte ui_keyqueue[UI_QUEUE_SIZE];
238 static int ui_keyqueuepos = 0;
239
240 void ui_leftkeyupdate(int pressed)
241 {
242         static int key = false;
243         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
244                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_LEFT;
245         key = pressed;
246 }
247
248 void ui_rightkeyupdate(int pressed)
249 {
250         static int key = false;
251         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
252                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_RIGHT;
253         key = pressed;
254 }
255
256 void ui_upkeyupdate(int pressed)
257 {
258         static int key = false;
259         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
260                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_UP;
261         key = pressed;
262 }
263
264 void ui_downkeyupdate(int pressed)
265 {
266         static int key = false;
267         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
268                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_DOWN;
269         key = pressed;
270 }
271
272 void ui_mousebuttonupdate(int button, int pressed)
273 {
274         if (button < 0 || button >= UI_MOUSEBUTTONS)
275                 return;
276         if (button == 0 && ui_mousebutton[button] && !pressed)
277                 ui_mouseclick = true;
278         ui_mousebutton[button] = pressed;
279 }
280
281 void ui_update(void)
282 {
283         ui_item_t *startitem, *it;
284         if (ui_alive)
285         {
286                 ui_mouse_x = bound(0, ui_mouse_x, vid.conwidth);
287                 ui_mouse_y = bound(0, ui_mouse_y, vid.conheight);
288
289                 if ((ui_active = ui_isactive()))
290                 {
291                         // validate currently selected item
292                         if(ui_list[ui_keyui] == NULL)
293                         {
294                                 while (ui_list[ui_keyui] == NULL)
295                                         ui_keyui = (ui_keyui + 1) % MAX_UI_COUNT;
296                                 ui_keyitem = 0;
297                         }
298                         ui_keyitem = bound(0, ui_keyitem, ui_list[ui_keyui]->item_count - 1);
299                         startitem = ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
300                         if ((ui_keyrealitem->flags & ITEM_CLICKABLE) == 0)
301                         {
302                                 do
303                                 {
304                                         // FIXME: cycle through UIs as well as items in a UI
305                                         ui_keyitem = (ui_keyitem - 1) % ui_list[ui_keyui]->item_count - 1;
306                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
307                                 }
308                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
309                         }
310
311                         if (ui_keyqueuepos)
312                         {
313                                 int i;
314                                 for (i = 0;i < ui_keyqueuepos;i++)
315                                 {
316                                         startitem = ui_keyrealitem;
317                                         switch(ui_keyqueue[i])
318                                         {
319                                         case UIKEY_UP:
320                                                 do
321                                                 {
322                                                         ui_keyitem--;
323                                                         if (ui_keyitem < 0)
324                                                         {
325                                                                 do
326                                                                         ui_keyui = (ui_keyui - 1) % MAX_UI_COUNT;
327                                                                 while(ui_list[ui_keyui] == NULL);
328                                                                 ui_keyitem = ui_list[ui_keyui]->item_count - 1;
329                                                         }
330                                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
331                                                 }
332                                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
333                                                 break;
334                                         case UIKEY_DOWN:
335                                                 do
336                                                 {
337                                                         ui_keyitem++;
338                                                         if (ui_keyitem >= ui_list[ui_keyui]->item_count)
339                                                         {
340                                                                 do
341                                                                         ui_keyui = (ui_keyui + 1) % MAX_UI_COUNT;
342                                                                 while(ui_list[ui_keyui] == NULL);
343                                                                 ui_keyitem = 0;
344                                                         }
345                                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
346                                                 }
347                                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
348                                                 break;
349                                         case UIKEY_LEFT:
350                                                 if (ui_keyrealitem->leftkey)
351                                                         ui_keyrealitem->leftkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
352                                                 break;
353                                         case UIKEY_RIGHT:
354                                                 if (ui_keyrealitem->rightkey)
355                                                         ui_keyrealitem->rightkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
356                                                 break;
357                                         case UIKEY_ENTER:
358                                                 if (ui_keyrealitem->enterkey)
359                                                         ui_keyrealitem->enterkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
360                                                 break;
361                                         }
362                                 }
363                         }
364                         ui_keyqueuepos = 0;
365
366                         if (ui_mouseclick && (it = ui_hititem(ui_mouse_x, ui_mouse_y)) && it->mouseclick)
367                                 it->mouseclick(it->nativedata1, it->nativedata2, it->data1, it->data2, ui_mouse_x - it->click_x, ui_mouse_y - it->click_y);
368         }
369         }
370         ui_mouseclick = false;
371 }
372
373 void ui_draw(void)
374 {
375         int i, j;
376         ui_item_t *it;
377         ui_t *ui;
378         if (ui_alive && ui_active)
379         {
380                 for (j = 0;j < MAX_UI_COUNT;j++)
381                         if ((ui = ui_list[j]))
382                                 if (ui->item_count)
383                                         for (i = 0, it = ui->items;i < ui->item_count;i++, it++)
384                                                 if (it->flags & ITEM_DRAWABLE)
385                                                 {
386                                                         if (it->draw_picname)
387                                                                 DrawQ_Pic(it->draw_x, it->draw_y, it->draw_picname, 0, 0, 1, 1, 1, 1, 0);
388                                                         if (it->draw_string)
389                                                                 DrawQ_String(it->draw_x, it->draw_y, it->draw_string, 0, 8, 8, 1, 1, 1, 1, 0);
390                                                 }
391
392                 if ((it = ui_hititem(ui_mouse_x, ui_mouse_y)))
393                 {
394                         if (it->draw_picname)
395                                 DrawQ_Pic(it->draw_x, it->draw_y, it->draw_picname, 0, 0, 1, 1, 1, 1, DRAWFLAG_ADDITIVE);
396                         if (it->draw_string)
397                                 DrawQ_String(it->draw_x, it->draw_y, it->draw_string, 0, 8, 8, 1, 1, 1, 1, DRAWFLAG_ADDITIVE);
398                         if (ui_showname.integer)
399                                 DrawQ_String(ui_mouse_x, ui_mouse_y + 16, it->name, 0, 8, 8, 1, 1, 1, 1, 0);
400         }
401
402                 it = ui_keyrealitem;
403                 if (it->draw_picname)
404                         DrawQ_Pic(it->draw_x, it->draw_y, it->draw_picname, 0, 0, 1, 1, 1, 1, DRAWFLAG_ADDITIVE);
405                 if (it->draw_string)
406                         DrawQ_String(it->draw_x, it->draw_y, it->draw_string, 0, 8, 8, 1, 1, 1, 1, DRAWFLAG_ADDITIVE);
407
408                 DrawQ_Pic(ui_mouse_x, ui_mouse_y, "ui/mousepointer.tga", 0, 0, 1, 1, 1, 1, 0);
409         }
410 }
411