added CVAR_SAVE and CVAR_NOTIFY flags to cvar_t structure (at the beginning), updated...
[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 qpic_t *ui_mousepointer;
26
27 void ui_start(void)
28 {
29         ui_mousepointer = Draw_CachePic("ui/mousepointer.lmp");
30         ui_mouse_x = vid.width * 0.5;
31         ui_mouse_y = vid.height * 0.5;
32         ui_alive = true;
33 }
34
35 void ui_shutdown(void)
36 {
37         ui_mousepointer = NULL;
38         ui_alive = false;
39 }
40
41 void ui_newmap(void)
42 {
43 }
44
45 void ui_init(void)
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.width);
56                 ui_mouse_y = bound(0, y, vid.height);
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.width);
67                 ui_mouse_y = bound(0, ui_mouse_y, vid.height);
68         }
69 }
70
71 ui_t *ui_create(void)
72 {
73         ui_t *ui;
74         ui = qmalloc(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                 qfree(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, qpic_t *pic,
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         strncpy(it->name, itemname, 32);
121         it->flags = 0;
122         if (leftkey || rightkey || enterkey || mouseclick)
123                 it->flags |= ITEM_CLICKABLE;
124         if (pic)
125                 it->flags |= ITEM_DRAWABLE;
126         it->draw_x = x;
127         it->draw_y = y;
128         it->click_x = x + left;
129         it->click_y = y + top;
130         it->click_x2 = it->click_x + width;
131         it->click_y2 = it->click_y + height;
132         it->leftkey = leftkey;
133         it->rightkey = rightkey;
134         it->enterkey = enterkey;
135         it->mouseclick = mouseclick;
136         if (it->mouseclick == NULL)
137                 it->mouseclick = (void *)it->enterkey;
138         if (it->leftkey == NULL)
139                 it->leftkey = it->enterkey;
140         if (it->rightkey == NULL)
141                 it->rightkey = it->enterkey;
142         it->nativedata1 = nativedata1;
143         it->nativedata2 = nativedata2;
144 }
145
146 void ui_item_remove(ui_t *ui, char *basename, int number)
147 {
148         int i;
149         ui_item_t *it;
150         char itemname[32];
151         snprintf(itemname, sizeof(itemname), "%s%04d", basename, number);
152         for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
153                 if (it->name && !strncmp(itemname, it->name, 32))
154                         break;
155         if (i < ui->item_count)
156                 it->name[0] = 0;
157 }
158
159 ui_item_t *ui_hititem(float x, float y)
160 {
161         int i, j;
162         ui_item_t *it;
163         ui_t *ui;
164         for (j = 0;j < MAX_UI_COUNT;j++)
165                 if ((ui = ui_list[j]))
166                         for (it = ui->items, i = 0;i < ui->item_count;it++, i++)
167                                 if (it->name[0] && (it->flags & ITEM_CLICKABLE))
168                                         if (x >= it->click_x && y >= it->click_y && x < it->click_x2 && y < it->click_y2)
169                                                 return it;
170         return NULL;
171 }
172
173 int ui_uiactive(ui_t *ui)
174 {
175         int i;
176         for (i = 0;i < MAX_UI_COUNT;i++)
177                 if (ui_list[i] == ui)
178                         return true;
179         return false;
180 }
181
182 void ui_activate(ui_t *ui, int yes)
183 {
184         int i;
185         if (yes)
186         {
187                 if (ui_uiactive(ui))
188                         return;
189
190                 for (i = 0;i < MAX_UI_COUNT;i++)
191                 {
192                         if (ui_list[i] == NULL)
193                         {
194                                 ui_list[i] = ui;
195                                 return;
196                         }
197                 }
198
199                 Con_Printf("ui_activate: ran out of active ui list items\n");
200         }
201         else
202         {
203                 for (i = 0;i < MAX_UI_COUNT;i++)
204                 {
205                         if (ui_list[i] == ui)
206                         {
207                                 ui_list[i] = NULL;
208                                 return;
209                         }
210                 }
211         }
212 }
213
214 int ui_isactive(void)
215 {
216         int j;
217         ui_t *ui;
218         if (ui_alive)
219         {
220                 for (j = 0;j < MAX_UI_COUNT;j++)
221                         if ((ui = ui_list[j]))
222                                 if (ui->item_count)
223                                         return true;
224         }
225         return false;
226 }
227
228 #define UI_QUEUE_SIZE 256
229 static byte ui_keyqueue[UI_QUEUE_SIZE];
230 static int ui_keyqueuepos = 0;
231
232 void ui_leftkeyupdate(int pressed)
233 {
234         static int key = false;
235         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
236                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_LEFT;
237         key = pressed;
238 }
239
240 void ui_rightkeyupdate(int pressed)
241 {
242         static int key = false;
243         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
244                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_RIGHT;
245         key = pressed;
246 }
247
248 void ui_upkeyupdate(int pressed)
249 {
250         static int key = false;
251         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
252                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_UP;
253         key = pressed;
254 }
255
256 void ui_downkeyupdate(int pressed)
257 {
258         static int key = false;
259         if (pressed && !key && ui_keyqueuepos < UI_QUEUE_SIZE)
260                 ui_keyqueue[ui_keyqueuepos++] = UIKEY_DOWN;
261         key = pressed;
262 }
263
264 void ui_mousebuttonupdate(int button, int pressed)
265 {
266         if (button < 0 || button >= UI_MOUSEBUTTONS)
267                 return;
268         if (button == 0 && ui_mousebutton[button] && !pressed)
269                 ui_mouseclick = true;
270         ui_mousebutton[button] = pressed;
271 }
272
273 void ui_update(void)
274 {
275         ui_item_t *startitem, *it;
276         if (ui_alive)
277         {
278                 ui_mouse_x = bound(0, ui_mouse_x, vid.width);
279                 ui_mouse_y = bound(0, ui_mouse_y, vid.height);
280
281                 if ((ui_active = ui_isactive()))
282                 {
283                         // validate currently selected item
284                         if(ui_list[ui_keyui] == NULL)
285                         {
286                                 while (ui_list[ui_keyui] == NULL)
287                                         ui_keyui = (ui_keyui + 1) % MAX_UI_COUNT;
288                                 ui_keyitem = 0;
289                         }
290                         ui_keyitem = bound(0, ui_keyitem, ui_list[ui_keyui]->item_count - 1);
291                         startitem = ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
292                         if ((ui_keyrealitem->flags & ITEM_CLICKABLE) == 0)
293                         {
294                                 do
295                                 {
296                                         // FIXME: cycle through UIs as well as items in a UI
297                                         ui_keyitem = (ui_keyitem - 1) % ui_list[ui_keyui]->item_count - 1;
298                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
299                                 }
300                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
301                         }
302
303                         if (ui_keyqueuepos)
304                         {
305                                 int i;
306                                 for (i = 0;i < ui_keyqueuepos;i++)
307                                 {
308                                         startitem = ui_keyrealitem;
309                                         switch(ui_keyqueue[i])
310                                         {
311                                         case UIKEY_UP:
312                                                 do
313                                                 {
314                                                         ui_keyitem--;
315                                                         if (ui_keyitem < 0)
316                                                         {
317                                                                 do
318                                                                         ui_keyui = (ui_keyui - 1) % MAX_UI_COUNT;
319                                                                 while(ui_list[ui_keyui] == NULL);
320                                                                 ui_keyitem = ui_list[ui_keyui]->item_count - 1;
321                                                         }
322                                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
323                                                 }
324                                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
325                                                 break;
326                                         case UIKEY_DOWN:
327                                                 do
328                                                 {
329                                                         ui_keyitem++;
330                                                         if (ui_keyitem >= ui_list[ui_keyui]->item_count)
331                                                         {
332                                                                 do
333                                                                         ui_keyui = (ui_keyui + 1) % MAX_UI_COUNT;
334                                                                 while(ui_list[ui_keyui] == NULL);
335                                                                 ui_keyitem = 0;
336                                                         }
337                                                         ui_keyrealitem = &ui_list[ui_keyui]->items[ui_keyitem];
338                                                 }
339                                                 while (ui_keyrealitem != startitem && (ui_keyrealitem->flags & ITEM_CLICKABLE) == 0);
340                                                 break;
341                                         case UIKEY_LEFT:
342                                                 if (ui_keyrealitem->leftkey)
343                                                         ui_keyrealitem->leftkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
344                                                 break;
345                                         case UIKEY_RIGHT:
346                                                 if (ui_keyrealitem->rightkey)
347                                                         ui_keyrealitem->rightkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
348                                                 break;
349                                         case UIKEY_ENTER:
350                                                 if (ui_keyrealitem->enterkey)
351                                                         ui_keyrealitem->enterkey(ui_keyrealitem->nativedata1, ui_keyrealitem->nativedata2, ui_keyrealitem->data1, ui_keyrealitem->data2);
352                                                 break;
353                                         }
354                                 }
355                         }
356                         ui_keyqueuepos = 0;
357
358                         if (ui_mouseclick && (it = ui_hititem(ui_mouse_x, ui_mouse_y)) && it->mouseclick)
359                                 it->mouseclick(it->nativedata1, it->nativedata2, it->data1, it->data2, ui_mouse_x - it->click_x, ui_mouse_y - it->click_y);
360         }
361         }
362         ui_mouseclick = false;
363 }
364
365 void ui_draw(void)
366 {
367         int i, j;
368         ui_item_t *it;
369         ui_t *ui;
370         if (ui_alive && ui_active)
371         {
372                 for (j = 0;j < MAX_UI_COUNT;j++)
373                         if ((ui = ui_list[j]))
374                                 if (ui->item_count)
375                                         for (i = 0, it = ui->items;i < ui->item_count;i++, it++)
376                                                 if (it->flags & ITEM_DRAWABLE)
377                                                         Draw_Pic(it->draw_x, it->draw_y, it->draw_pic);
378
379                 if ((it = ui_hititem(ui_mouse_x, ui_mouse_y)))
380                 {
381                         Draw_AdditivePic(it->draw_x, it->draw_y, it->draw_pic);
382                         if (ui_showname.value)
383                                 Draw_String(ui_mouse_x, ui_mouse_y + 16, it->name, 9999);
384         }
385
386                 it = ui_keyrealitem;
387                 Draw_AdditivePic(it->draw_x, it->draw_y, it->draw_pic);
388
389                 Draw_Pic(ui_mouse_x, ui_mouse_y, ui_mousepointer);
390         }
391 }