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