* use alignment for clipped/wrapped text
[open-ps2-loader.git] / src / menusys.c
bloba448cf85d675175ba87d5213ad7fd8cff2e17b33
1 /*
2 Copyright 2009, Ifcaro & volca
3 Licenced under Academic Free License version 3.0
4 Review OpenUsbLd README & LICENSE files for further details.
5 */
7 #include "include/menusys.h"
8 #include "include/iosupport.h"
9 #include "include/usbld.h"
10 #include "include/renderman.h"
11 #include "include/fntsys.h"
12 #include "include/lang.h"
13 #include "include/themes.h"
14 #include "include/pad.h"
15 #include "include/gui.h"
16 #include "include/system.h"
17 #include "include/ioman.h"
18 #include "assert.h"
20 #define MENU_SETTINGS 0
21 #define MENU_GFX_SETTINGS 1
22 #define MENU_IP_CONFIG 2
23 #define MENU_SAVE_CHANGES 3
24 #define MENU_START_HDL 4
25 #define MENU_ABOUT 5
26 #define MENU_EXIT 6
27 #define MENU_POWER_OFF 7
29 // global menu variables
30 static menu_list_t* menu;
31 static menu_list_t* selected_item;
33 static int itemIdConfig;
34 static config_set_t* itemConfig;
36 // "main menu submenu"
37 static submenu_list_t* mainMenu;
38 // active item in the main menu
39 static submenu_list_t* mainMenuCurrent;
41 // io call to handle the loading of config
42 #define IO_MENU_LOAD_CONFIG 6
44 static s32 menuSemaId;
45 static ee_sema_t menuSema;
47 typedef struct {
48 int itemId;
49 item_list_t* list;
50 } load_config_request_t;
52 // Io handled action...
53 static void menuLoadConfig(void* data) {
54 load_config_request_t* req = data;
56 WaitSema(menuSemaId);
57 if (!itemConfig && (req->itemId == itemIdConfig))
58 itemConfig = req->list->itemGetConfig(itemIdConfig);
59 SignalSema(menuSemaId);
61 free(req);
64 static void menuRequestConfig() {
65 WaitSema(menuSemaId);
66 if (itemIdConfig != selected_item->item->current->item.id) {
67 if (itemConfig) {
68 configFree(itemConfig);
69 itemConfig = NULL;
72 item_list_t* list = selected_item->item->userdata;
73 if (guiInactiveFrames >= list->delay) {
74 itemIdConfig = selected_item->item->current->item.id;
76 load_config_request_t* req = malloc(sizeof(load_config_request_t));
77 req->itemId = itemIdConfig;
78 req->list = list;
79 ioPutRequest(IO_MENU_LOAD_CONFIG, req);
82 SignalSema(menuSemaId);
85 static void menuInitMainMenu() {
86 if (mainMenu)
87 submenuDestroy(&mainMenu);
89 // initialize the menu
90 #ifndef __CHILDPROOF
91 submenuAppendItem(&mainMenu, -1, NULL, MENU_SETTINGS, _STR_SETTINGS);
92 submenuAppendItem(&mainMenu, -1, NULL, MENU_GFX_SETTINGS, _STR_GFX_SETTINGS);
93 submenuAppendItem(&mainMenu, -1, NULL, MENU_IP_CONFIG, _STR_IPCONFIG);
94 submenuAppendItem(&mainMenu, -1, NULL, MENU_SAVE_CHANGES, _STR_SAVE_CHANGES);
95 if (gHDDStartMode) // enabled at all?
96 submenuAppendItem(&mainMenu, -1, NULL, MENU_START_HDL, _STR_STARTHDL);
97 #endif
98 submenuAppendItem(&mainMenu, -1, NULL, MENU_ABOUT, _STR_ABOUT);
99 submenuAppendItem(&mainMenu, -1, NULL, MENU_EXIT, _STR_EXIT);
100 submenuAppendItem(&mainMenu, -1, NULL, MENU_POWER_OFF, _STR_POWEROFF);
102 mainMenuCurrent = mainMenu;
105 // -------------------------------------------------------------------------------------------
106 // ---------------------------------------- Menu manipulation --------------------------------
107 // -------------------------------------------------------------------------------------------
108 void menuInit() {
109 menu = NULL;
110 selected_item = NULL;
111 itemIdConfig = -1;
112 itemConfig = NULL;
113 mainMenu = NULL;
114 mainMenuCurrent = NULL;
115 menuInitMainMenu();
117 menuSema.init_count = 1;
118 menuSema.max_count = 1;
119 menuSema.option = 0;
120 menuSemaId = CreateSema(&menuSema);
122 ioRegisterHandler(IO_MENU_LOAD_CONFIG, &menuLoadConfig);
125 void menuEnd() {
126 // destroy menu
127 menu_list_t *cur = menu;
129 while (cur) {
130 menu_list_t *td = cur;
131 cur = cur->next;
133 if (&td->item)
134 submenuDestroy(&td->item->submenu);
136 menuRemoveHints(td->item);
138 free(td);
141 submenuDestroy(&mainMenu);
143 if (itemConfig) {
144 configFree(itemConfig);
145 itemConfig = NULL;
148 DeleteSema(menuSemaId);
151 static menu_list_t* AllocMenuItem(menu_item_t* item) {
152 menu_list_t* it;
154 it = malloc(sizeof(menu_list_t));
156 it->prev = NULL;
157 it->next = NULL;
158 it->item = item;
160 return it;
163 void menuAppendItem(menu_item_t* item) {
164 assert(item);
166 if (menu == NULL) {
167 menu = AllocMenuItem(item);
168 return;
171 menu_list_t *cur = menu;
173 // traverse till the end
174 while (cur->next)
175 cur = cur->next;
177 // create new item
178 menu_list_t *newitem = AllocMenuItem(item);
180 // link
181 cur->next = newitem;
182 newitem->prev = cur;
185 void submenuRebuildCache(submenu_list_t* submenu) {
186 while (submenu) {
187 if (submenu->item.cache_id)
188 free(submenu->item.cache_id);
189 if(submenu->item.cache_uid)
190 free(submenu->item.cache_uid);
192 int size = gTheme->gameCacheCount * sizeof(int);
193 submenu->item.cache_id = malloc(size);
194 memset(submenu->item.cache_id, -1, size);
195 submenu->item.cache_uid = malloc(size);
196 memset(submenu->item.cache_uid, -1, size);
198 submenu = submenu->next;
202 static submenu_list_t* submenuAllocItem(int icon_id, char *text, int id, int text_id) {
203 submenu_list_t* it = (submenu_list_t*) malloc(sizeof(submenu_list_t));
205 it->prev = NULL;
206 it->next = NULL;
207 it->item.icon_id = icon_id;
208 it->item.text = text;
209 it->item.text_id = text_id;
210 it->item.id = id;
211 it->item.cache_id = NULL;
212 it->item.cache_uid = NULL;
213 submenuRebuildCache(it);
215 return it;
218 submenu_list_t* submenuAppendItem(submenu_list_t** submenu, int icon_id, char *text, int id, int text_id) {
219 if (*submenu == NULL) {
220 *submenu = submenuAllocItem(icon_id, text, id, text_id);
221 return *submenu;
224 submenu_list_t *cur = *submenu;
226 // traverse till the end
227 while (cur->next)
228 cur = cur->next;
230 // create new item
231 submenu_list_t *newitem = submenuAllocItem(icon_id, text, id, text_id);
233 // link
234 cur->next = newitem;
235 newitem->prev = cur;
237 return newitem;
240 static void submenuDestroyItem(submenu_list_t* submenu) {
241 free(submenu->item.cache_id);
242 free(submenu->item.cache_uid);
244 free(submenu);
247 void submenuRemoveItem(submenu_list_t** submenu, int id) {
248 submenu_list_t* cur = *submenu;
249 submenu_list_t* prev = NULL;
251 while (cur) {
252 if (cur->item.id == id) {
253 submenu_list_t* next = cur->next;
255 if (prev)
256 prev->next = cur->next;
258 if (*submenu == cur)
259 *submenu = next;
261 submenuDestroyItem(cur);
263 cur = next;
264 } else {
265 prev = cur;
266 cur = cur->next;
271 void submenuDestroy(submenu_list_t** submenu) {
272 // destroy sub menu
273 submenu_list_t *cur = *submenu;
275 while (cur) {
276 submenu_list_t *td = cur;
277 cur = cur->next;
279 submenuDestroyItem(td);
282 *submenu = NULL;
285 void menuAddHint(menu_item_t *menu, int text_id, int icon_id) {
286 // allocate a new hint item
287 menu_hint_item_t* hint = malloc(sizeof(menu_hint_item_t));
289 hint->text_id = text_id;
290 hint->icon_id = icon_id;
291 hint->next = NULL;
293 if (menu->hints) {
294 menu_hint_item_t* top = menu->hints;
296 // rewind to end
297 for (; top->next; top = top->next);
299 top->next = hint;
300 } else {
301 menu->hints = hint;
305 void menuRemoveHints(menu_item_t *menu) {
306 while (menu->hints) {
307 menu_hint_item_t* hint = menu->hints;
308 menu->hints = hint->next;
309 free(hint);
313 char *menuItemGetText(menu_item_t* it) {
314 if (it->text_id >= 0)
315 return _l(it->text_id);
316 else
317 return it->text;
320 char *submenuItemGetText(submenu_item_t* it) {
321 if (it->text_id >= 0)
322 return _l(it->text_id);
323 else
324 return it->text;
327 static void swap(submenu_list_t* a, submenu_list_t* b) {
328 submenu_list_t *pa, *nb;
329 pa = a->prev;
330 nb = b->next;
332 a->next = nb;
333 b->prev = pa;
334 b->next = a;
335 a->prev = b;
337 if (pa)
338 pa->next = b;
340 if (nb)
341 nb->prev = a;
344 // Sorts the given submenu by comparing the on-screen titles
345 void submenuSort(submenu_list_t** submenu) {
346 // a simple bubblesort
347 // *submenu = mergeSort(*submenu);
348 submenu_list_t *head = *submenu;
349 int sorted = 0;
351 if ((submenu == NULL) || (*submenu == NULL) || ((*submenu)->next == NULL))
352 return;
354 while (!sorted) {
355 sorted = 1;
357 submenu_list_t *tip = head;
359 while (tip->next) {
360 submenu_list_t *nxt = tip->next;
362 char *txt1 = submenuItemGetText(&tip->item);
363 char *txt2 = submenuItemGetText(&nxt->item);
365 int cmp = stricmp(txt1, txt2);
367 if (cmp > 0) {
368 swap(tip, nxt);
370 if (tip == head)
371 head = nxt;
373 sorted = 0;
374 } else {
375 tip = tip->next;
380 *submenu = head;
383 static void menuNextH() {
384 if (!selected_item) {
385 selected_item = menu;
388 if(selected_item->next) {
389 selected_item = selected_item->next;
391 if (!selected_item->item->current)
392 selected_item->item->current = selected_item->item->submenu;
396 static void menuPrevH() {
397 if (!selected_item) {
398 selected_item = menu;
401 if(selected_item->prev) {
402 selected_item = selected_item->prev;
404 if (!selected_item->item->current)
405 selected_item->item->current = selected_item->item->submenu;
409 static void menuNextV() {
410 if (!selected_item)
411 return;
413 submenu_list_t *cur = selected_item->item->current;
415 if(cur && cur->next)
416 selected_item->item->current = cur->next;
419 static void menuPrevV() {
420 if (!selected_item)
421 return;
423 submenu_list_t *cur = selected_item->item->current;
425 // if the current item is on the page start, move the page start one page up before moving the item
426 if (selected_item->item->pagestart) {
427 if (selected_item->item->pagestart == cur) {
428 int itms = ((items_list_t*) gTheme->itemsList->extended)->displayedItems + 1; // +1 because the selection will move as well
430 while (--itms && selected_item->item->pagestart->prev)
431 selected_item->item->pagestart = selected_item->item->pagestart->prev;
433 } else
434 selected_item->item->pagestart = cur;
436 if(cur && cur->prev) {
437 selected_item->item->current = cur->prev;
441 static void menuNextPage() {
442 if (!selected_item)
443 return;
445 submenu_list_t *cur = selected_item->item->pagestart;
447 if (cur) {
448 int itms = ((items_list_t*) gTheme->itemsList->extended)->displayedItems + 1;
449 while (--itms && cur->next)
450 cur = cur->next;
452 selected_item->item->current = cur;
456 static void menuPrevPage() {
457 if (!selected_item)
458 return;
460 submenu_list_t *cur = selected_item->item->pagestart;
462 if (cur) {
463 int itms = ((items_list_t*) gTheme->itemsList->extended)->displayedItems + 1;
464 while (--itms && cur->prev)
465 cur = cur->prev;
467 selected_item->item->current = cur;
468 selected_item->item->pagestart = cur;
472 static void menuFirstPage() {
473 if (!selected_item)
474 return;
476 selected_item->item->current = selected_item->item->submenu;
479 static void menuLastPage() {
480 if (!selected_item)
481 return;
483 submenu_list_t *cur = selected_item->item->current;
485 if (cur) {
486 while (cur->next)
487 cur = cur->next; // go to end
489 selected_item->item->current = cur;
491 int itms = ((items_list_t*) gTheme->itemsList->extended)->displayedItems;
492 while (--itms && cur->prev) // and move back to have a full page
493 cur = cur->prev;
495 selected_item->item->pagestart = cur;
499 void menuSetSelectedItem(menu_item_t* item) {
500 menu_list_t* itm = menu;
502 while (itm) {
503 if (itm->item == item) {
504 selected_item = itm;
505 return;
508 itm = itm->next;
512 void menuRenderMenu() {
513 guiDrawBGPlasma();
515 if (!mainMenu)
516 return;
518 // draw the animated menu
519 if (!mainMenuCurrent)
520 mainMenuCurrent = mainMenu;
522 submenu_list_t* it = mainMenu;
524 // calculate the number of items
525 int count = 0; int sitem = 0;
526 for (; it; count++, it=it->next) {
527 if (it == mainMenuCurrent)
528 sitem = count;
531 int spacing = 25;
532 int y = (gTheme->usedHeight >> 1) - (spacing * (count >> 1));
533 int cp = 0; // current position
534 for (it = mainMenu; it; it = it->next, cp++) {
535 // render, advance
536 // TODO: Theme support for main menu (font)
537 fntRenderString(FNT_DEFAULT, 320, y, ALIGN_CENTER, submenuItemGetText(&it->item), (cp == sitem) ? gTheme->selTextColor : gTheme->textColor);
538 y += spacing;
542 void menuHandleInputMenu() {
543 if (!mainMenu)
544 return;
546 if (!mainMenuCurrent)
547 mainMenuCurrent = mainMenu;
549 if (getKey(KEY_UP)) {
550 if (mainMenuCurrent->prev)
551 mainMenuCurrent = mainMenuCurrent->prev;
552 else // rewind to the last item
553 while (mainMenuCurrent->next)
554 mainMenuCurrent = mainMenuCurrent->next;
557 if (getKey(KEY_DOWN)) {
558 if (mainMenuCurrent->next)
559 mainMenuCurrent = mainMenuCurrent->next;
560 else
561 mainMenuCurrent = mainMenu;
564 if (getKeyOn(KEY_CROSS)) {
565 // execute the item via looking at the id of it
566 int id = mainMenuCurrent->item.id;
568 if (id == MENU_SETTINGS) {
569 guiShowConfig();
570 } else if (id == MENU_GFX_SETTINGS) {
571 guiShowUIConfig();
572 } else if (id == MENU_IP_CONFIG) {
573 guiShowIPConfig();
574 } else if (id == MENU_SAVE_CHANGES) {
575 saveConfig(CONFIG_OPL | CONFIG_VMODE, 1);
576 } else if (id == MENU_START_HDL) {
577 handleHdlSrv();
578 } else if (id == MENU_ABOUT) {
579 guiShowAbout();
580 } else if (id == MENU_EXIT) {
581 sysExecExit();
582 } else if (id == MENU_POWER_OFF) {
583 sysPowerOff();
586 // so the exit press wont propagate twice
587 readPads();
590 if(getKeyOn(KEY_START) || getKeyOn(KEY_CIRCLE)) {
591 if (gAPPStartMode || gETHStartMode || gUSBStartMode || gHDDStartMode)
592 guiSwitchScreen(GUI_SCREEN_MAIN, TRANSITION_LEFT);
596 void menuRenderMain() {
597 if (!menu)
598 return;
600 if (!selected_item)
601 selected_item = menu;
603 if (!selected_item->item->current)
604 selected_item->item->current = selected_item->item->submenu;
606 submenu_list_t* cur = selected_item->item->current;
608 theme_element_t* elem = gTheme->mainElems.first;
609 while (elem) {
610 if (elem->drawElem)
611 elem->drawElem(selected_item, cur, NULL, elem);
613 elem = elem->next;
617 void menuHandleInputMain() {
618 if (!selected_item)
619 return;
621 if(getKey(KEY_LEFT)) {
622 menuPrevH();
623 } else if(getKey(KEY_RIGHT)) {
624 menuNextH();
625 } else if(getKey(KEY_UP)) {
626 menuPrevV();
627 } else if(getKey(KEY_DOWN)){
628 menuNextV();
629 } else if(getKeyOn(KEY_CROSS)) {
630 if (selected_item->item->current && gUseInfoScreen && gTheme->infoElems.first)
631 guiSwitchScreen(GUI_SCREEN_INFO, TRANSITION_DOWN);
632 else
633 selected_item->item->execCross(selected_item->item);
634 } else if(getKeyOn(KEY_TRIANGLE)) {
635 selected_item->item->execTriangle(selected_item->item);
636 } else if(getKeyOn(KEY_CIRCLE)) {
637 selected_item->item->execCircle(selected_item->item);
638 } else if(getKeyOn(KEY_SQUARE)) {
639 selected_item->item->execSquare(selected_item->item);
640 } else if(getKeyOn(KEY_START)) {
641 // reinit main menu - show/hide items valid in the active context
642 menuInitMainMenu();
643 guiSwitchScreen(GUI_SCREEN_MENU, TRANSITION_RIGHT);
644 } else if(getKeyOn(KEY_SELECT)) {
645 selected_item->item->refresh(selected_item->item);
646 } else if(getKey(KEY_L1)) {
647 menuPrevPage();
648 } else if(getKey(KEY_R1)) {
649 menuNextPage();
650 } else if (getKeyOn(KEY_L2)) { // home
651 menuFirstPage();
652 } else if (getKeyOn(KEY_R2)) { // end
653 menuLastPage();
657 void menuRenderInfo() {
658 submenu_list_t* cur = selected_item->item->current;
660 menuRequestConfig();
662 theme_element_t* elem = gTheme->infoElems.first;
663 while (elem) {
664 if (elem->drawElem)
665 elem->drawElem(selected_item, cur, itemConfig, elem);
667 elem = elem->next;
671 void menuHandleInputInfo() {
672 if(getKeyOn(KEY_CROSS)) {
673 selected_item->item->execCross(selected_item->item);
674 } else if(getKey(KEY_UP)) {
675 menuPrevV();
676 } else if(getKey(KEY_DOWN)){
677 menuNextV();
678 } else if(getKeyOn(KEY_CIRCLE)) {
679 guiSwitchScreen(GUI_SCREEN_MAIN, TRANSITION_UP);
680 } else if(getKey(KEY_L1)) {
681 menuPrevPage();
682 } else if(getKey(KEY_R1)) {
683 menuNextPage();
684 } else if (getKeyOn(KEY_L2)) {
685 menuFirstPage();
686 } else if (getKeyOn(KEY_R2)) {
687 menuLastPage();