3 #include <limits.h> /* for PATH_MAX */
4 #define __USE_GNU /* for strndup */
5 #include <string.h> /* for strdup */
11 #include "pnd_container.h"
13 #include "pnd_discovery.h"
14 #include "pnd_notify.h"
15 #include "pnd_dbusnotify.h"
21 #include "mmwrapcmd.h"
23 static unsigned char conf_render_text ( TTF_Font *f, char *buffer, SDL_Rect *dest, unsigned int x, unsigned int y, unsigned char selected );
24 static unsigned char conf_render_line ( SDL_Rect *dest, unsigned int y );
25 static char *conf_format_int ( int v, change_type_e c );
27 confitem_t page_general[] = {
28 { "Default tab to show", "On startup, Minimenu will try to switch to this tab", NULL /* default */, "categories.default_cat", ct_visible_tab_list },
29 #if 0 // and also in mmenu.c -- something crashes during image caching
30 { "Set CPU speed within Minimenu", "Whether the next setting is applied or not", "0", "minimenu.use_mm_speed", ct_boolean },
31 { "CPU speed within Minimenu", "Set low; speed to run Minimenu at", "400", "minimenu.mm_speed", ct_cpu_speed },
33 { "Show 'All' tab", "Whethor an All tab is shown or not", "1", "categories.do_all_cat", ct_boolean },
34 { "Show directory browser tabs", "Show a tab for each SD card?", "0", "filesystem.do_browser", ct_boolean },
35 { "Detail panel on start?", "Show or hide the detail panel when menu starts", "1", "display.show_detail_pane", ct_boolean },
36 { "Default list view not icons?", "Normally we default to icons view not list", "0", "display.viewmode_list", ct_boolean },
37 { "Sub-categories as folders?", "If no, uses tabs instead of folders within tabs.", "1", "tabs.subcat_as_folders", ct_boolean },
38 { "Sub-category apps in their main cat?","If no, apps with subcategories are only in subcategories","1", "tabs.subcat_to_parent", ct_boolean },
39 { "Remember selected app", "On return to menu select previous app; default to selected", "1", "minimenu.start_selected", ct_boolean },
40 { "Auto discover pnd apps?", "If no, turn on diectory browser to manually find apps", "1", "filesystem.do_pnd_disco", ct_boolean },
41 // dropped option -- we now strictly enforce free desktop categories (or user defined, but no more bogus PXML categories)
42 // { "Keep bad categories in Other?", "Lazy dev! Put broken categories into Other to keep clean", "1", "categories.good_cats_only", ct_boolean },
43 { "Set CPU speed when leaving", "Whether the next setting is applied or not", "0", "minimenu.use_run_speed", ct_boolean },
44 { "CPU speed when leaving", "Before running app, set this speed; app may override.", "500", "minimenu.run_speed", ct_cpu_speed },
45 { "Wrap tab change", "Changing tab left or right, does it wrap around?", "0", "tabs.wraparound", ct_boolean },
46 { "Grid stop vertical", "Changing selection up or down, does it stop or wrap?", "0", "grid.wrap_vert_stop", ct_boolean },
47 { "Live (not exit) on app run?", "Normally menu exits (to save ram) on app run", "0", "minimenu.live_on_run", ct_boolean },
48 { "Force wallpaper with..", "You can force an override over themes background", "/pandora/appdata/mmenu/wallpaper.png", "minimenu.force_wallpaper", ct_filename },
49 { "", "", NULL, NULL, ct_nil },
50 { "^- Back up to main", "Go back to top level of configuration", NULL, NULL, ct_switch_page, NULL },
54 confitem_t page_appshowhide [ CONF_MAX_LISTLENGTH ] = {
55 { "^- Back up to main", "Go back to top level of configuration", NULL, NULL, ct_switch_page, NULL },
56 { CONF_APPLIST_TAG, "Show or hide this application", "1", "appshow", ct_nil },
60 confitem_t page_tabshowhide [ CONF_MAX_LISTLENGTH ] = {
61 { "^- Back up to main", "Go back to top level of configuration", NULL, NULL, ct_switch_page, NULL },
62 { CONF_TABLIST_TAG, "Show or hide or reorder this tab", "1", "tabshow", ct_nil },
66 confitem_t pages[] = {
67 { "General Options", "Miscellaneous handy options", NULL /* default */, NULL, ct_switch_page, page_general },
68 { "Show/Hide Applications", "Each application can be hidden/revealed", NULL /* default */, NULL, ct_switch_page, page_appshowhide },
69 { "Show/Hide/Order Tabs", "Each tab can be hidden/revealed or re-ordered", NULL /* default */, NULL, ct_switch_page, page_tabshowhide },
70 { "Manage Custom Categories", "Can make your own categories/tabs", NULL /* default */, NULL, ct_go_manage_categories },
71 { "Select a Minimenu Skin", "Download alternate look and feels!", NULL /* default */, NULL, ct_go_manage_skins },
72 { "", "", NULL, NULL, ct_nil },
73 { "Exit configuration", "Quit and save configuration", NULL /* default */, NULL, ct_exit },
74 { "", "", NULL, NULL, ct_nil },
75 { "Reset to defaults", "Remove any custom options set in this UI", NULL /* default */, NULL, ct_reset },
79 extern pnd_conf_handle g_conf;
80 extern SDL_Surface *sdl_realscreen;
81 extern mm_imgcache_t g_imagecache [ IMG_TRUEMAX ];
82 extern pnd_box_handle g_active_apps;
84 unsigned char conf_run_menu ( confitem_t *toplevel ) {
85 confitem_t *page = toplevel;
87 unsigned int first_visible = 0;
88 unsigned char max_visible = 12;
90 confitem_t *lastpage = NULL;
100 if ( lastpage != page ) {
101 conf_prepare_page ( page );
105 conf_display_page ( page, sel, first_visible, max_visible );
108 while ( SDL_WaitEvent ( &event ) ) {
110 switch ( event.type ) {
115 if ( event.key.keysym.sym == SDLK_UP ) {
122 if ( sel < first_visible ) {
128 } while ( page [ sel ].type == ct_nil );
130 } else if ( event.key.keysym.sym == SDLK_DOWN ) {
134 if ( page [ sel + 1 ].text ) {
138 if ( sel >= first_visible + max_visible ) {
144 } while ( page [ sel ].type == ct_nil );
146 } else if ( event.key.keysym.sym == SDLK_PAGEUP ) {
149 } else if ( event.key.keysym.sym == SDLK_LEFT || event.key.keysym.sym == SDLK_RIGHT ) {
151 unsigned char left = 0;
152 if ( event.key.keysym.sym == SDLK_LEFT ) {
156 switch ( page [ sel ].type ) {
160 int v = pnd_conf_get_as_int ( g_conf, page [ sel ].key );
161 if ( v == PND_CONF_BADNUM ) {
174 sprintf ( buffer, "%u", v );
175 pnd_conf_set_char ( g_conf, page [ sel ].key, buffer );
180 case ct_visible_tab_list:
182 if ( g_categorycount ) {
183 char *v = pnd_conf_get_as_char ( g_conf, page [ sel ].key );
186 for ( n = 0; n < g_categorycount; n++ ) {
187 if ( strcmp ( v, g_categories [ n ] -> catname ) == 0 ) {
191 if ( n < g_categorycount ) {
193 pnd_conf_set_char ( g_conf, page [ sel ].key, g_categories [ n - 1 ] -> catname );
194 } else if ( ! left && n + 1 < g_categorycount ) {
195 pnd_conf_set_char ( g_conf, page [ sel ].key, g_categories [ n + 1 ] -> catname );
198 pnd_conf_set_char ( g_conf, page [ sel ].key, g_categories [ 0 ] -> catname );
201 pnd_conf_set_char ( g_conf, page [ sel ].key, g_categories [ 0 ] -> catname );
203 } // if category count
209 int v = pnd_conf_get_as_int ( g_conf, page [ sel ].key );
210 if ( v == PND_CONF_BADNUM ) {
220 sprintf ( buffer, "%u", v );
222 pnd_conf_set_char ( g_conf, page [ sel ].key, buffer );
230 case ct_go_manage_categories:
231 case ct_go_manage_skins:
239 } else if ( event.key.keysym.sym == SDLK_ESCAPE ) {
240 emit_and_quit ( MM_QUIT );
242 } else if ( event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_END ) { // return, or "B"
244 switch ( page [ sel ].type ) {
247 return ( 1 /* always cause restart for now */ );
252 conf_reset_to_default ( g_conf );
258 page = page [ sel ].newhead;
259 sel = 0; // should use a stack..
261 case ct_go_manage_categories:
262 ui_manage_categories();
264 case ct_go_manage_skins:
265 if ( ui_pick_skin() ) {
266 emit_and_quit ( MM_RESTART );
272 case ct_visible_tab_list:
282 break; // case sdl_key_up
284 } // switch what SDL event
286 break; // get out of sdl-wait-event loop
291 return ( 1 /* always cause restart for now */ );
294 void conf_display_page ( confitem_t *page, unsigned int selitem, unsigned int first_visible, unsigned int max_visible ) {
295 extern TTF_Font *g_big_font;
296 extern TTF_Font *g_tab_font;
299 SDL_Rect rects [ MAXRECTS ];
300 SDL_Rect *dest = rects;
301 bzero ( dest, sizeof(SDL_Rect)*MAXRECTS );
303 unsigned short int tx, ty;
306 if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) {
309 dest -> w = sdl_realscreen -> w;
310 dest -> h = sdl_realscreen -> h;
311 SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, NULL /* whole image */, sdl_realscreen, dest /* 0,0 */ );
324 dest += conf_render_text ( g_big_font, "Minimenu Configuration", dest, 10, 10, CONF_UNSELECTED );
325 dest += conf_render_line ( dest, 45 );
331 if ( first_visible > 0 ) {
334 SDL_BlitSurface ( g_imagecache [ IMG_ARROW_UP ].i, NULL /* whole image */, sdl_realscreen, dest );
339 if ( first_visible + max_visible < conf_determine_pagelength ( page ) ) {
342 SDL_BlitSurface ( g_imagecache [ IMG_ARROW_DOWN ].i, NULL /* whole image */, sdl_realscreen, dest );
350 unsigned char counter = first_visible;
351 while ( page [ counter ].text ) {
354 conf_render_text ( g_tab_font, page [ counter ].text, dest, tx, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
357 switch ( page [ counter ].type ) {
362 case ct_visible_tab_list:
364 char *v = pnd_conf_get_as_char ( g_conf, page [ counter ].key );
366 conf_render_text ( g_tab_font, v, dest, tx + 400, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
368 conf_render_text ( g_tab_font, "Not specified", dest, tx + 400, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
374 int v = pnd_conf_get_as_int ( g_conf, page [ counter ].key );
375 conf_render_text ( g_tab_font, conf_format_int ( v, page [ counter ].type ), dest, tx + 400, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
380 int v = pnd_conf_get_as_int ( g_conf, page [ counter ].key );
381 conf_render_text ( g_tab_font, conf_format_int ( v, page [ counter ].type ), dest, tx + 400, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
385 conf_render_text ( g_tab_font, page [ counter ].def, dest, tx + 400, ty, counter == selitem ? CONF_SELECTED : CONF_UNSELECTED );
389 case ct_go_manage_categories:
390 case ct_go_manage_skins:
396 if ( counter - first_visible >= max_visible - 1 ) {
405 // description and default
406 if ( page [ selitem ].desc ) {
407 dest += conf_render_text ( g_tab_font, page [ selitem ].desc, dest, 380, 400, CONF_UNSELECTED );
409 if ( page [ selitem ].def ) {
410 char buffer [ 100 ] = "Default: ";
412 switch ( page [ selitem ].type ) {
414 sprintf ( buffer + strlen ( buffer ), "%s", conf_format_int ( atoi ( page [ selitem ].def ), ct_boolean ) );
417 sprintf ( buffer + strlen ( buffer ), "%s", conf_format_int ( atoi ( page [ selitem ].def ), ct_cpu_speed ) );
420 sprintf ( buffer + strlen ( buffer ), "%s", page [ selitem ].def );
426 case ct_visible_tab_list:
427 case ct_go_manage_categories:
428 case ct_go_manage_skins:
432 dest += conf_render_text ( g_tab_font, buffer, dest, 380, 420, CONF_UNSELECTED );
434 dest += conf_render_text ( g_tab_font, "No default value", dest, 380, 420, CONF_UNSELECTED );
437 // cursor's conf item count number - not for top level, just the sublevels
438 if ( page != pages ) {
440 sprintf ( buffer, "Config item %d of %d", selitem + 1, conf_determine_pagelength ( page ) );
441 /*dest += */conf_render_text ( g_tab_font, buffer, dest, 380, 440, CONF_UNSELECTED );
445 dest += conf_render_line ( dest, 380 );
446 dest += conf_render_text ( g_tab_font, "D-pad Up/down; Y return to index", dest, 10, 400, CONF_UNSELECTED );
447 dest += conf_render_text ( g_tab_font, "Left and right to alter selected item", dest, 10, 420, CONF_UNSELECTED );
448 dest += conf_render_text ( g_tab_font, "B or Enter to activate an option", dest, 10, 440, CONF_UNSELECTED );
450 // update all the rects and send it all to sdl
451 // - at this point, we could probably just do 1 rect, of the
452 // whole screen, and be faster :/
453 SDL_UpdateRects ( sdl_realscreen, dest - rects, rects );
458 unsigned char conf_render_text ( TTF_Font *f, char *buffer, SDL_Rect *dest, unsigned int x, unsigned int y, unsigned char selected ) {
459 unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 );
460 unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 );
461 unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 );
462 unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 );
464 SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a };
465 SDL_Color selfontcolor = { 0/*font_rgba_r*/, font_rgba_g, font_rgba_b, font_rgba_a };
467 SDL_Surface *rtext = TTF_RenderText_Blended ( f, buffer, selected ? selfontcolor : tmpfontcolor );
470 SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest );
471 SDL_FreeSurface ( rtext );
476 unsigned char conf_render_line ( SDL_Rect *dest, unsigned int y ) {
481 SDL_Surface *i = g_imagecache [ IMG_TAB_LINE ].i;
483 while ( dest -> x + i -> w < 800 ) {
484 SDL_BlitSurface ( i, NULL, sdl_realscreen, dest );
489 dest -> w = 480 - 10;
495 char *conf_format_int ( int v, change_type_e c ) {
496 static char buffer [ 50 ];
503 if ( v == PND_CONF_BADNUM ) {
504 strcpy ( buffer, "Leave Alone" );
506 sprintf ( buffer, "%u", v );
513 if ( v == PND_CONF_BADNUM ) {
514 strcpy ( buffer, "FUBAR" );
515 } else if ( v == 0 ) {
516 strcpy ( buffer, "no" );
517 } else if ( v == 1 ) {
518 strcpy ( buffer, "yes" );
520 strcpy ( buffer, "FUBAR 2" );
531 case ct_visible_tab_list:
533 case ct_go_manage_categories:
534 case ct_go_manage_skins:
542 unsigned char conf_prepare_page ( confitem_t *page ) {
545 confitem_t *p = page;
546 confitem_t *template = NULL;
547 while ( p -> text != NULL ) {
549 if ( strcmp ( p -> text, CONF_APPLIST_TAG ) == 0 ) {
550 // rewrite this and subsequent items to be the listing
554 template -> text = ""; // so it won't get repopulated again later
557 pnd_disco_t *iter = pnd_box_get_head ( g_active_apps );
559 while ( p - page < CONF_MAX_LISTLENGTH && iter ) {
561 p -> text = strndup ( iter -> title_en ? iter -> title_en : "Unnamed", 40 );
562 p -> desc = strdup ( iter -> unique_id );
565 sprintf ( buffer, "%s.%s", template -> key, iter -> unique_id );
566 p -> key = strdup ( buffer );
567 p -> type = ct_boolean;
570 // create to positive if not existant
571 if ( ! pnd_conf_get_as_char ( g_conf, buffer ) ) {
572 pnd_conf_set_char ( g_conf, buffer, "1" );
575 iter = pnd_box_get_next ( iter );
577 } // while not run off end of buffer
581 } else if ( strcmp ( p -> text, CONF_TABLIST_TAG ) == 0 ) {
582 // rewrite this and subsequent items to be the listing
586 template -> text = ""; // so it won't get repopulated again later
588 // switch categories being published
589 category_publish ( CFALL, NULL );
593 char catname [ 512 ];
594 char *actual_catname;
595 char finalbuf [ 101 ];
597 for ( i = 0; i < g_categorycount; i++ ) {
599 // if this is an invisi-guy, it has parent-cat prepended; we want the real cat name.
600 strncpy ( catname, g_categories [ i ] -> catname, 500 );
602 if ( ( actual_catname = strchr ( catname, '.' ) ) ) {
603 actual_catname++; // skip the period
605 actual_catname = catname;
607 //fprintf ( stderr, "conf ui; got '%s' but showing '%s'\n", cc [ i ].catname, actual_catname );
609 if ( strncmp ( g_categories [ i ] -> catname, "All ", 4 ) == 0 ) {
610 // skip All tab, since it is generated, and managed by another config item
614 if ( g_categories [ i ] -> parent_catname ) {
615 snprintf ( finalbuf, 100, "%s [%s]", actual_catname, g_categories [ i ] -> parent_catname );
617 strncpy ( finalbuf, actual_catname, 100 );
620 p -> text = strndup ( finalbuf, 40 );
624 sprintf ( buffer, "%s.%s", template -> key, actual_catname );
625 p -> key = strdup ( buffer );
626 p -> type = ct_boolean;
629 // create to positive if not existant
630 if ( ! pnd_conf_get_as_char ( g_conf, buffer ) ) {
631 pnd_conf_set_char ( g_conf, buffer, "1" );
634 //fprintf ( stderr, "Created tabshow entry '%s'\n", cc [ i ].catname );
639 // switch categories being published
640 category_publish ( CFNORMAL, NULL );
651 unsigned char conf_write ( pnd_conf_handle h, char *fullpath ) {
653 // cherry pick the named keys from the conf-ui-array
654 // spill out the apps ands tabs 'broups' of conf keys
656 FILE *f = fopen ( fullpath, "w" );
662 fprintf ( f, "# Machine written; do not edit.\n" );
663 fprintf ( f, "# If you do edit, its KEY<tab>VALUE<newline>, nothing extra.\n" );
666 // deal with general keys
667 confitem_t *ci = page_general;
668 while ( ci -> text ) {
669 // does this item have a key? a value? if so, try to emit it.
671 char *v = pnd_conf_get_as_char ( h, ci -> key );
673 fprintf ( f, "%s\t%s\n", ci -> key, v );
679 // deal with apps and tabs
680 char *v = pnd_box_get_head ( g_conf );
683 // does item begin with app or tab tag?
684 char *k = pnd_box_get_key ( v );
687 ( strncasecmp ( k, "appshow.", 8 ) == 0 ||
688 strncasecmp ( k, "tabshow.", 8 ) == 0 )
691 fprintf ( f, "%s\t%s\n", k, v );
694 v = pnd_box_get_next ( v );
697 // really, should write out keys that are not found in the conf items..
698 // but since g_conf is merged with other conf files, that may just
699 // make for big dumps erroneously.. hmm :/
700 char *previous_unique_id = pnd_conf_get_as_char ( g_conf, "minimenu.last_known_app_uid" );
701 char *lastcat = pnd_conf_get_as_char ( g_conf, "minimenu.last_known_catname" );
702 char *lastparentcat = pnd_conf_get_as_char ( g_conf, "minimenu.last_known_parentcatname" );
703 if ( previous_unique_id ) {
704 fprintf ( f, "%s\t%s\n", "minimenu.last_known_app_uid", previous_unique_id );
707 fprintf ( f, "%s\t%s\n", "minimenu.last_known_catname", lastcat );
709 if ( lastparentcat ) {
710 fprintf ( f, "%s\t%s\n", "minimenu.last_known_parentcatname", lastparentcat );
718 void conf_merge_into ( char *fullpath, pnd_conf_handle h ) {
720 char buffer [ 1024 ];
723 f = fopen ( fullpath, "r" );
729 while ( fgets ( buffer, 1000, f ) ) {
731 // trim trailing spaces
732 s = strchr ( buffer, ' ' );
738 s = strchr ( buffer, '#' );
743 s = strchr ( buffer, '\n' );
748 // if theres anything left..
749 if ( buffer [ 0 ] != '\0' ) {
751 // assume FOO<tab>BAR<newline> since this really should be machine written, not human screwed with; or if human
752 // edited, assume they know what to do :) I even put in some 'docs' in the conf file.
753 char *s = strchr ( buffer, '\t' );
758 // set it; libpnd conf code already handles 'existant' or 'new'
759 pnd_conf_set_char ( h, buffer, s + 1 );
772 char *conf_determine_location ( pnd_conf_handle h ) {
773 static char path [ PATH_MAX ];
775 bzero ( path, PATH_MAX );
777 if ( ! getenv ( "HOME" ) ) {
778 return ( "." ); // wtf?
781 snprintf ( path, PATH_MAX - strlen(CONF_PREF_FILENAME) - 1, "%s/%s", getenv ( "HOME" ), CONF_PREF_FILENAME );
786 void conf_setup_missing ( pnd_conf_handle h ) {
788 confitem_t *ci = page_general;
790 while ( ci -> text ) {
792 // does this item have a default value?
795 // it does, so lets see if we can pull a current value in; if not, set one
796 char *v = pnd_conf_get_as_char ( h, ci -> key );
799 fprintf ( stderr, "pref conf: no value present in config, better set to default; key is '%s' def '%s'\n", ci -> key, ci -> def );
800 pnd_conf_set_char ( h, ci -> key, ci -> def );
811 void conf_reset_to_default ( pnd_conf_handle h ) {
813 // reset all keys to default value - if present
814 // reset all apps to show
815 // reset all tabs to show
817 // deal with general keys
818 confitem_t *ci = page_general;
819 while ( ci -> text ) {
821 // does this item have a default value? if so, set it
822 if ( ci -> key && ci -> def ) {
823 pnd_conf_set_char ( h, ci -> key, ci -> def );
829 // deal with apps and tabs
830 char *v = pnd_box_get_head ( g_conf );
833 next = pnd_box_get_next ( v );
835 // does item begin with app or tab tag?
836 char *k = pnd_box_get_key ( v );
839 ( strncasecmp ( k, "appshow.", 8 ) == 0 ||
840 strncasecmp ( k, "tabshow.", 8 ) == 0 )
843 pnd_conf_set_char ( g_conf, k, "1" );
852 unsigned int conf_determine_pagelength ( confitem_t *page ) {
853 confitem_t *p = page;
854 while ( p -> text ) {