X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-libraries.git;a=blobdiff_plain;f=minimenu%2Fmmui.c;h=36c1628a99161c24ef95b4b48308710d8c1a917d;hp=6baf2df2da5b4d2bdfe2425c95fff6061f8f3aba;hb=25471871d6ea1ffe24b123e91d021305d57683a6;hpb=29f6c321cc5c45c480a3681f62511d45db3e2158 diff --git a/minimenu/mmui.c b/minimenu/mmui.c index 6baf2df..36c1628 100644 --- a/minimenu/mmui.c +++ b/minimenu/mmui.c @@ -41,6 +41,7 @@ #include "mmconf.h" #include "mmui_context.h" #include "freedesktop_cats.h" +#include "mmcustom_cats.h" #define CHANGED_NOTHING (0) #define CHANGED_CATEGORY (1<<0) /* changed to different category */ @@ -50,6 +51,10 @@ #define CHANGED_EVERYTHING (1<<4) /* redraw it all! */ unsigned int render_mask = CHANGED_EVERYTHING; +SDL_Thread *g_icon_thread = NULL; +unsigned char g_icon_thread_stop = 0; /* if !0 means thread should stop and maim app may block until it goes back to 0.. */ +unsigned char g_icon_thread_busy = 0; /* if !0 means thread is running right now */ + #define MIMETYPE_EXE "/usr/bin/file" /* check for file type prior to invocation */ /* SDL @@ -84,12 +89,15 @@ int ui_rows_scrolled_down = 0; // number of rows that should be missing mm_appref_t *ui_selected = NULL; unsigned char ui_category = 0; // current category unsigned char ui_catshift = 0; // how many cats are offscreen to the left +ui_viewmode_e ui_viewmode = uiv_list; // default to traditional icon view; why or why is viewstate not per-viewmode :/ ui_context_t ui_display_context; // display paramaters: see mmui_context.h unsigned char ui_detail_hidden = 0; // if >0, detail panel is hidden // FUTURE: If multiple panels can be shown/hidden, convert ui_detail_hidden to a bitmask -static SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ); // height -1 means ignore +SDL_Surface *ui_scale_image ( SDL_Surface *s, unsigned int maxwidth, int maxheight ); // height -1 means ignore static int ui_selected_index ( void ); +static void ui_start_defered_icon_thread ( void ); +static void ui_stop_defered_icon_thread ( void ); unsigned char ui_setup ( void ) { @@ -153,7 +161,7 @@ unsigned char ui_setup ( void ) { #endif // key repeat - SDL_EnableKeyRepeat ( 500, 150 ); + SDL_EnableKeyRepeat ( 500, 125 /* 150 */ ); // images //IMG_Init ( IMG_INIT_JPG | IMG_INIT_PNG ); @@ -239,12 +247,17 @@ mm_imgcache_t g_imagecache [ IMG_TRUEMAX ] = { { IMG_HOURGLASS, "graphics.IMG_HOURGLASS", }, { IMG_FOLDER, "graphics.IMG_FOLDER", }, { IMG_EXECBIN, "graphics.IMG_EXECBIN", }, + { IMG_SUBCATFOLDER, "graphics.IMG_SUBCATFOLDER", "graphics.IMG_FOLDER", }, + { IMG_DOTDOTFOLDER, "graphics.IMG_DOTDOTFOLDER", "graphics.IMG_FOLDER", }, { IMG_MAX, NULL }, + { IMG_LIST_ALPHAMASK, NULL }, // generated + { IMG_LIST_ALPHAMASK_W, NULL }, // generated }; unsigned char ui_imagecache ( char *basepath ) { unsigned int i; char fullpath [ PATH_MAX ]; + unsigned char try; // loaded @@ -255,30 +268,60 @@ unsigned char ui_imagecache ( char *basepath ) { exit ( -1 ); } - char *filename = pnd_conf_get_as_char ( g_conf, g_imagecache [ i ].confname ); + for ( try = 0; try < 2; try++ ) { - if ( ! filename ) { - pnd_log ( pndn_error, "ERROR: Missing filename in conf for key: %s\n", g_imagecache [ i ].confname ); - return ( 0 ); - } + char *filename; - if ( filename [ 0 ] == '/' ) { - strncpy ( fullpath, filename, PATH_MAX ); - } else { - sprintf ( fullpath, "%s/%s", basepath, filename ); - } + if ( try == 0 ) { + filename = pnd_conf_get_as_char ( g_conf, g_imagecache [ i ].confname ); + } else { + if ( g_imagecache [ i ].alt_confname ) { + filename = pnd_conf_get_as_char ( g_conf, g_imagecache [ i ].alt_confname ); + } else { + return ( 0 ); + } + } - if ( ! ( g_imagecache [ i ].i = IMG_Load ( fullpath ) ) ) { - pnd_log ( pndn_error, "ERROR: Couldn't load static cache image: %s\n", fullpath ); - return ( 0 ); - } + if ( ! filename ) { + pnd_log ( pndn_error, "ERROR: (Try %u) Missing filename in conf for key: %s\n", try + 1, g_imagecache [ i ].confname ); + if ( try == 0 ) { continue; } else { return ( 0 ); } + } + + if ( filename [ 0 ] == '/' ) { + strncpy ( fullpath, filename, PATH_MAX ); + } else { + sprintf ( fullpath, "%s/%s", basepath, filename ); + } + + if ( ( g_imagecache [ i ].i = IMG_Load ( fullpath ) ) ) { + break; // no retry needed + } else { + pnd_log ( pndn_error, "ERROR: (Try %u) Couldn't load static cache image: %s\n", try + 1, fullpath ); + if ( try == 0 ) { continue; } else { return ( 0 ); } + } + + } // try twice } // for // generated + // + //g_imagecache [ IMG_SELECTED_ALPHAMASK ].i = SDL_CreateRGBSurface ( SDL_SWSURFACE, 60, 60, 32, 0xFF0000, 0x00FF00, 0xFF, 0xFF000000 ); //boxRGBA ( g_imagecache [ IMG_SELECTED_ALPHAMASK ].i, 0, 0, 60, 60, 100, 100, 100, 250 ); + SDL_Surface *p = g_imagecache [ IMG_SELECTED_ALPHAMASK ].i; + g_imagecache [ IMG_LIST_ALPHAMASK ].i = SDL_ConvertSurface ( p, p -> format, p -> flags ); + g_imagecache [ IMG_LIST_ALPHAMASK_W ].i = SDL_ConvertSurface ( p, p -> format, p -> flags ); + + g_imagecache [ IMG_LIST_ALPHAMASK ].i = + ui_scale_image ( g_imagecache [ IMG_LIST_ALPHAMASK ].i, + pnd_conf_get_as_int ( g_conf, "grid.col_max" ) * pnd_conf_get_as_int ( g_conf, "grid.cell_width" ) , -1 ); + + g_imagecache [ IMG_LIST_ALPHAMASK_W ].i = + ui_scale_image ( g_imagecache [ IMG_LIST_ALPHAMASK_W ].i, + pnd_conf_get_as_int ( g_conf, "grid.col_max_w" ) * pnd_conf_get_as_int ( g_conf, "grid.cell_width_w" ) , -1 ); + // post processing // @@ -334,6 +377,7 @@ void ui_render ( void ) { // render everything // unsigned int icon_rows; + unsigned int icon_visible_rows = 0; // list view, max number of visible rows #define MAXRECTS 200 SDL_Rect rects [ MAXRECTS ], src; @@ -345,23 +389,71 @@ void ui_render ( void ) { ui_context_t *c = &ui_display_context; // for convenience and shorthand - // how many total rows do we need? - if ( g_categorycount ) { - icon_rows = g_categories [ ui_category ] -> refcount / c -> col_max; - if ( g_categories [ ui_category ] -> refcount % c -> col_max > 0 ) { - icon_rows++; - } - } else { - icon_rows = 0; + // on demand icon loading + static int load_visible = -1; + if ( load_visible == -1 ) { + load_visible = pnd_conf_get_as_int_d ( g_conf, "minimenu.load_visible_icons", 0 ); } #if 1 // if no selected app yet, select the first one if ( ! ui_selected && pnd_conf_get_as_int_d ( g_conf, "minimenu.start_selected", 0 ) ) { + + // pick first visible app ui_selected = g_categories [ ui_category ] -> refs; + + // change.. so we pick first visible if option is set .. but now we also try to restore + // selection to the last app selected in previous session (if there is one.) + char *previous_unique_id = pnd_conf_get_as_char ( g_conf, "minimenu.last_known_app_uid" ); + + if ( previous_unique_id ) { + + // 1) we should already be in the right category, since its set in ui_post_scan to minimenu.last_known_catname + // 2) so we just pick the app in question.. + mm_appref_t *previter = g_categories [ ui_category ] -> refs; + while ( previter ) { + if ( strcmp ( previter -> ref -> unique_id, previous_unique_id ) == 0 ) { + break; + } + previter = previter -> next; + } + + if ( previter ) { + ui_selected = previter; + } + + } // last known app? + } #endif + // how many total rows do we need? + if ( g_categorycount ) { + + if ( ui_viewmode == uiv_list ) { + // in list view, dimension of grid area is .. + // grid height == cell-height * row-max + // font height == display_context -> text_height + // padding == icon_offset_y + // max visible --> row-max == grid height / ( font-height + padding ) + + icon_rows = g_categories [ ui_category ] -> refcount; // one app per row + icon_visible_rows = ( c -> cell_height * c -> row_max ) / ( c -> text_height + c -> icon_offset_y ); + + } else { + + icon_rows = g_categories [ ui_category ] -> refcount / c -> col_max; + if ( g_categories [ ui_category ] -> refcount % c -> col_max > 0 ) { + icon_rows++; + } + + } + + } else { + icon_rows = 0; + icon_visible_rows = 0; + } + // reset touchscreen regions if ( render_jobs_b ) { ui_register_reset(); @@ -375,18 +467,36 @@ void ui_render ( void ) { autoscrolled = 0; int index = ui_selected_index(); - int topleft = c -> col_max * ui_rows_scrolled_down; - int botright = ( c -> col_max * ( ui_rows_scrolled_down + c -> row_max ) - 1 ); - - if ( index < topleft ) { - ui_rows_scrolled_down -= pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 ); - render_jobs_b |= R_ALL; - autoscrolled = 1; - } else if ( index > botright ) { - ui_rows_scrolled_down += pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 ); - render_jobs_b |= R_ALL; - autoscrolled = 1; - } + + if ( ui_viewmode == uiv_list ) { + + if ( index >= ui_rows_scrolled_down + icon_visible_rows ) { + ui_rows_scrolled_down += 1; + autoscrolled = 1; + render_jobs_b |= R_ALL; + } else if ( index < ui_rows_scrolled_down ) { + ui_rows_scrolled_down -= 1; + autoscrolled = 1; + render_jobs_b |= R_ALL; + } + + } else { + // icons + + int topleft = c -> col_max * ui_rows_scrolled_down; + int botright = ( c -> col_max * ( ui_rows_scrolled_down + c -> row_max ) - 1 ); + + if ( index < topleft ) { + ui_rows_scrolled_down -= pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 ); + render_jobs_b |= R_ALL; + autoscrolled = 1; + } else if ( index > botright ) { + ui_rows_scrolled_down += pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 ); + render_jobs_b |= R_ALL; + autoscrolled = 1; + } + + } // viewmode } // while autoscrolling @@ -396,7 +506,7 @@ void ui_render ( void ) { ui_rows_scrolled_down = icon_rows; } - } // ensire visible + } // ensure visible // render background if ( render_jobs_b & R_BG ) { @@ -660,117 +770,243 @@ void ui_render ( void ) { } } + // any apps to render at all? if ( g_categories [ ui_category ] -> refs ) { - appiter = g_categories [ ui_category ] -> refs; - row = 0; - displayrow = 0; + // render grid differently based on view mode.. + // + if ( ui_viewmode == uiv_list ) { - // until we run out of apps, or run out of space - while ( appiter != NULL ) { + appiter = g_categories [ ui_category ] -> refs; + row = 0; // row under consideration (ie: could be scrolled off) + displayrow = 0; // rows displayed - for ( col = 0; col < c -> col_max && appiter != NULL; col++ ) { + while ( appiter != NULL ) { // do we even need to render it? or are we suppressing it due to rows scrolled off the top? if ( row >= ui_rows_scrolled_down ) { - // selected? show hilights + // background hilight + SDL_Surface *hilight = ui_detail_hidden ? g_imagecache [ IMG_LIST_ALPHAMASK_W ].i : g_imagecache [ IMG_LIST_ALPHAMASK ].i; if ( appiter == ui_selected ) { - SDL_Surface *s = g_imagecache [ IMG_SELECTED_ALPHAMASK ].i; - // icon - //dest -> x = grid_offset_x + ( col * cell_width ) + icon_offset_x + ( ( icon_max_width - s -> w ) / 2 ); - dest -> x = c -> grid_offset_x + ( col * c -> cell_width ) + c -> icon_offset_x + c -> sel_icon_offset_x; - //dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y + ( ( icon_max_height - s -> h ) / 2 ); - dest -> y = c -> grid_offset_y + ( displayrow * c -> cell_height ) + c -> icon_offset_y + c -> sel_icon_offset_y; - SDL_BlitSurface ( s, NULL /* all */, sdl_realscreen, dest ); - dest++; - // text - dest -> x = c -> grid_offset_x + ( col * c -> cell_width ) + c -> text_clip_x; - dest -> y = c -> grid_offset_y + ( displayrow * c -> cell_height ) + c -> text_hilite_offset_y; - SDL_BlitSurface ( g_imagecache [ IMG_SELECTED_HILITE ].i, NULL /* all */, sdl_realscreen, dest ); + src.x = 0; + src.y = 0; + src.w = hilight -> w; + src.h = c -> text_height + c -> icon_offset_y; + dest -> x = c -> grid_offset_x + c -> text_clip_x; + dest -> y = c -> grid_offset_y + ( displayrow * ( c -> text_height + c -> icon_offset_y ) ) - ( c -> icon_offset_y / 2 ); + SDL_BlitSurface ( hilight, &src, sdl_realscreen, dest ); dest++; - } // selected? + } // show icon mm_cache_t *ic = cache_query_icon ( appiter -> ref -> unique_id ); - SDL_Surface *iconsurface; if ( ic ) { - iconsurface = ic -> i; - } else { - //pnd_log ( pndn_warning, "WARNING: TBD: Need Missin-icon icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") ); - - // no icon override; was this a pnd-file (show the unknown icon then), or was this generated from - // filesystem (file or directory icon) - if ( appiter -> ref -> object_flags & PND_DISCO_GENERATED ) { - if ( appiter -> ref -> object_type == pnd_object_type_directory ) { - iconsurface = g_imagecache [ IMG_FOLDER ].i; - } else { - iconsurface = g_imagecache [ IMG_EXECBIN ].i; - } - } else { - iconsurface = g_imagecache [ IMG_ICON_MISSING ].i; - } - + dest -> x = c -> grid_offset_x + c -> text_clip_x; + dest -> y = c -> grid_offset_y + ( displayrow * ( c -> text_height + c -> icon_offset_y ) ) - ( c -> icon_offset_y / 2 ); + SDL_BlitSurface ( ic -> itiny, NULL, sdl_realscreen, dest ); } - // got an icon I hope? - if ( iconsurface ) { - //pnd_log ( pndn_debug, "Got an icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") ); - - src.x = 0; - src.y = 0; - src.w = 60; - src.h = 60; - dest -> x = c -> grid_offset_x + ( col * c -> cell_width ) + c -> icon_offset_x + (( c -> icon_max_width - iconsurface -> w ) / 2); - dest -> y = c -> grid_offset_y + ( displayrow * c -> cell_height ) + c -> icon_offset_y + (( c -> icon_max_height - iconsurface -> h ) / 2); - - SDL_BlitSurface ( iconsurface, &src, sdl_realscreen, dest ); - - // store touch info - ui_register_app ( appiter, dest -> x, dest -> y, src.w, src.h ); - - dest++; - - } - - // show text + // show title text if ( appiter -> ref -> title_en ) { SDL_Surface *rtext; rtext = TTF_RenderText_Blended ( g_grid_font, appiter -> ref -> title_en, c -> fontcolor ); src.x = 0; src.y = 0; - src.w = c -> text_width < rtext -> w ? c -> text_width : rtext -> w; + src.w = hilight -> w; //c -> text_width < rtext -> w ? c -> text_width : rtext -> w; src.h = rtext -> h; - if ( rtext -> w > c -> text_width ) { - dest -> x = c -> grid_offset_x + ( col * c -> cell_width ) + c -> text_clip_x; - } else { - dest -> x = c -> grid_offset_x + ( col * c -> cell_width ) + c -> text_offset_x - ( rtext -> w / 2 ); + + dest -> x = c -> grid_offset_x + c -> text_clip_x; + dest -> x += 20; // so all title-text line up, regardless of icon presence +#if 0 + if ( ic ) { + dest -> x += 20; //((SDL_Surface*)ic -> i) -> w; } - dest -> y = c -> grid_offset_y + ( displayrow * c -> cell_height ) + c -> text_offset_y; +#endif + + dest -> y = c -> grid_offset_y + ( displayrow * ( c -> text_height + c -> icon_offset_y ) ); SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); SDL_FreeSurface ( rtext ); dest++; } - } // display now? or scrolled away.. + } // visible or scrolled? + + if ( row >= ui_rows_scrolled_down ) { + displayrow++; + } + + // are we done displaying rows? + if ( displayrow >= icon_visible_rows ) { + break; + } - // next appiter = appiter -> next; + row++; - } // for column 1...X + } // while - if ( row >= ui_rows_scrolled_down ) { - displayrow++; - } + } else { - row ++; + appiter = g_categories [ ui_category ] -> refs; + row = 0; + displayrow = 0; + + // until we run out of apps, or run out of space + while ( appiter != NULL ) { + + for ( col = 0; col < c -> col_max && appiter != NULL; col++ ) { + + // do we even need to render it? or are we suppressing it due to rows scrolled off the top? + if ( row >= ui_rows_scrolled_down ) { + + // selected? show hilights + if ( appiter == ui_selected ) { + SDL_Surface *s = g_imagecache [ IMG_SELECTED_ALPHAMASK ].i; + // icon + //dest -> x = grid_offset_x + ( col * cell_width ) + icon_offset_x + ( ( icon_max_width - s -> w ) / 2 ); + dest -> x = c -> grid_offset_x + ( col * c -> cell_width ) + c -> icon_offset_x + c -> sel_icon_offset_x; + //dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y + ( ( icon_max_height - s -> h ) / 2 ); + dest -> y = c -> grid_offset_y + ( displayrow * c -> cell_height ) + c -> icon_offset_y + c -> sel_icon_offset_y; + SDL_BlitSurface ( s, NULL /* all */, sdl_realscreen, dest ); + dest++; + // text + dest -> x = c -> grid_offset_x + ( col * c -> cell_width ) + c -> text_clip_x; + dest -> y = c -> grid_offset_y + ( displayrow * c -> cell_height ) + c -> text_hilite_offset_y; + SDL_BlitSurface ( g_imagecache [ IMG_SELECTED_HILITE ].i, NULL /* all */, sdl_realscreen, dest ); + dest++; + } // selected? + + // show icon + mm_cache_t *ic = cache_query_icon ( appiter -> ref -> unique_id ); + SDL_Surface *iconsurface; + + // if icon not in cache, and its a pnd-file source, perhaps try to load it right now.. + if ( ( ! ic ) && + ( load_visible ) && + ( ! ( appiter -> ref -> object_flags & PND_DISCO_GENERATED ) ) + ) + { + // try to load any icons that.. + // - are not yet loaded + // - did not fail a previous load attempt + // this way user can upfront load all icons, or defer all icons, or even defer all icons + // and still try to load visible ones 'just before needed'; so not at mmenu load time, but + // as needed (only the ones needed.) + + if ( ( appiter -> ref -> pnd_icon_pos ) || + ( appiter -> ref -> icon && appiter -> ref -> object_flags & PND_DISCO_LIBPND_DD ) + ) + { + + // try to cache it? + if ( ! cache_icon ( appiter -> ref, ui_display_context.icon_max_width, ui_display_context.icon_max_width ) ) { + // erm.. + } - // are we done displaying rows? - if ( displayrow >= c -> row_max ) { - break; - } + // avoid churn + appiter -> ref -> pnd_icon_pos = 0; + if ( appiter -> ref -> icon ) { + free ( appiter -> ref -> icon ); + appiter -> ref -> icon = NULL; + } + + // pick up as if nothing happened.. + ic = cache_query_icon ( appiter -> ref -> unique_id ); - } // while + } + + } // load icon during rendering? + + if ( ic ) { + iconsurface = ic -> i; + } else { + //pnd_log ( pndn_warning, "WARNING: TBD: Need Missin-icon icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") ); + + // no icon override; was this a pnd-file (show the unknown icon then), or was this generated from + // filesystem (file or directory icon) + if ( appiter -> ref -> object_flags & PND_DISCO_GENERATED ) { + if ( appiter -> ref -> object_type == pnd_object_type_directory ) { + + // is this a subcat, a .., or a filesystem folder? + //iconsurface = g_imagecache [ IMG_FOLDER ].i; + if ( g_categories [ ui_category ] -> fspath ) { + iconsurface = g_imagecache [ IMG_FOLDER ].i; + } else if ( strcmp ( appiter -> ref -> title_en, ".." ) == 0 ) { + iconsurface = g_imagecache [ IMG_DOTDOTFOLDER ].i; + } else { + iconsurface = g_imagecache [ IMG_SUBCATFOLDER ].i; + } + + } else { + iconsurface = g_imagecache [ IMG_EXECBIN ].i; + } + } else { + iconsurface = g_imagecache [ IMG_ICON_MISSING ].i; + } + + } + + // got an icon I hope? + if ( iconsurface ) { + //pnd_log ( pndn_debug, "Got an icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") ); + + src.x = 0; + src.y = 0; + src.w = 60; + src.h = 60; + dest -> x = c -> grid_offset_x + ( col * c -> cell_width ) + c -> icon_offset_x + (( c -> icon_max_width - iconsurface -> w ) / 2); + dest -> y = c -> grid_offset_y + ( displayrow * c -> cell_height ) + c -> icon_offset_y + (( c -> icon_max_height - iconsurface -> h ) / 2); + + SDL_BlitSurface ( iconsurface, &src, sdl_realscreen, dest ); + + // store touch info + ui_register_app ( appiter, dest -> x, dest -> y, src.w, src.h ); + + dest++; + + } + + // show text + if ( appiter -> ref -> title_en ) { + SDL_Surface *rtext; + rtext = TTF_RenderText_Blended ( g_grid_font, appiter -> ref -> title_en, c -> fontcolor ); + src.x = 0; + src.y = 0; + src.w = c -> text_width < rtext -> w ? c -> text_width : rtext -> w; + src.h = rtext -> h; + if ( rtext -> w > c -> text_width ) { + dest -> x = c -> grid_offset_x + ( col * c -> cell_width ) + c -> text_clip_x; + } else { + dest -> x = c -> grid_offset_x + ( col * c -> cell_width ) + c -> text_offset_x - ( rtext -> w / 2 ); + } + dest -> y = c -> grid_offset_y + ( displayrow * c -> cell_height ) + c -> text_offset_y; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); + SDL_FreeSurface ( rtext ); + dest++; + } + + } // display now? or scrolled away.. + + // next + appiter = appiter -> next; + + } // for column 1...X + + if ( row >= ui_rows_scrolled_down ) { + displayrow++; + } + + row ++; + + // are we done displaying rows? + if ( displayrow >= c -> row_max ) { + break; + } + + } // while + + } // icon view } else { // no apps to render? @@ -1342,6 +1578,7 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) { "Reveal hidden category", "Shutdown Pandora", "Configure Minimenu", + "Manage custom app categories", "Rescan for applications", "Cache previews to SD now", "Run a terminal/console", @@ -1350,13 +1587,15 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) { "Select a Minimenu skin", "About Minimenu" }; - int sel = ui_modal_single_menu ( opts, 10, "Minimenu", "Enter to select; other to return." ); + int sel = ui_modal_single_menu ( opts, 11, "Minimenu", "Enter to select; other to return." ); char buffer [ 100 ]; if ( sel == 0 ) { // do nothing ui_revealscreen(); } else if ( sel == 1 ) { + // store conf on exit, so that last app/cat can be cached.. for ED :) + conf_write ( g_conf, conf_determine_location ( g_conf ) ); // shutdown sprintf ( buffer, "sudo poweroff" ); system ( buffer ); @@ -1364,16 +1603,20 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) { // configure mm unsigned char restart = conf_run_menu ( NULL ); conf_write ( g_conf, conf_determine_location ( g_conf ) ); + conf_write ( g_conf, CONF_PREF_TEMPPATH ); if ( restart ) { emit_and_quit ( MM_RESTART ); } } else if ( sel == 3 ) { + // manage custom categories + ui_manage_categories(); + } else if ( sel == 4 ) { // rescan apps pnd_log ( pndn_debug, "Freeing up applications\n" ); applications_free(); pnd_log ( pndn_debug, "Rescanning applications\n" ); applications_scan(); - } else if ( sel == 4 ) { + } else if ( sel == 5 ) { // cache preview to SD now extern pnd_box_handle g_active_apps; pnd_box_handle h = g_active_apps; @@ -1396,7 +1639,7 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) { iter = pnd_box_get_next ( iter ); } // while - } else if ( sel == 5 ) { + } else if ( sel == 6 ) { // run terminal char *argv[5]; argv [ 0 ] = pnd_conf_get_as_char ( g_conf, "utility.terminal" ); @@ -1406,18 +1649,18 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) { ui_forkexec ( argv ); } - } else if ( sel == 6 ) { + } else if ( sel == 7 ) { char buffer [ PATH_MAX ]; sprintf ( buffer, "%s %s\n", MM_RUN, "/usr/pandora/scripts/op_switchgui.sh" ); emit_and_quit ( buffer ); - } else if ( sel == 7 ) { - emit_and_quit ( MM_QUIT ); } else if ( sel == 8 ) { + emit_and_quit ( MM_QUIT ); + } else if ( sel == 9 ) { // select skin if ( ui_pick_skin() ) { emit_and_quit ( MM_RESTART ); } - } else if ( sel == 9 ) { + } else if ( sel == 10 ) { // about char buffer [ PATH_MAX ]; sprintf ( buffer, "%s/about.txt", g_skinpath ); @@ -1516,6 +1759,16 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) { void ui_push_left ( unsigned char forcecoil ) { + if ( ui_viewmode == uiv_list ) { + ui_context_t *c = &ui_display_context; + int i; + int imax = ( c -> cell_height * c -> row_max ) / ( c -> text_height + c -> icon_offset_y ); // visible rows + for ( i = 0; i < imax; i++ ) { + ui_push_up(); + } + return; + } + if ( ! ui_selected ) { ui_push_right ( 0 ); return; @@ -1557,6 +1810,16 @@ void ui_push_left ( unsigned char forcecoil ) { void ui_push_right ( unsigned char forcecoil ) { + if ( ui_viewmode == uiv_list ) { + ui_context_t *c = &ui_display_context; + int i; + int imax = ( c -> cell_height * c -> row_max ) / ( c -> text_height + c -> icon_offset_y ); // visible rows + for ( i = 0; i < imax; i++ ) { + ui_push_down(); + } + return; + } + if ( ui_selected ) { // what column we in? @@ -1596,12 +1859,37 @@ void ui_push_right ( unsigned char forcecoil ) { } void ui_push_up ( void ) { + unsigned char col_max = ui_display_context.col_max; + // not selected? well, no where to go.. if ( ! ui_selected ) { return; } + if ( ui_viewmode == uiv_list ) { + + if ( g_categories [ ui_category ] -> refs == ui_selected ) { + // can't go any more up, we're at the head + + } else { + // figure out the previous item; yay for singly linked list :/ + mm_appref_t *i = g_categories [ ui_category ] -> refs; + while ( i ) { + if ( i -> next == ui_selected ) { + ui_selected = i; + break; + } + i = i -> next; + } + + } + + // for list view.. we're done + ui_set_selected ( ui_selected ); + return; + } + // what row we in? unsigned int row = ui_determine_row ( ui_selected ); @@ -1648,6 +1936,24 @@ void ui_push_up ( void ) { } void ui_push_down ( void ) { + + if ( ui_viewmode == uiv_list ) { + + if ( ui_selected ) { + + if ( ui_selected -> next ) { + ui_selected = ui_selected -> next; + } + + } else { + ui_selected = g_categories [ ui_category ] -> refs; // frist! + } + + // list view? we're done. + ui_set_selected ( ui_selected ); + return; + } + unsigned char col_max = ui_display_context.col_max; if ( ui_selected ) { @@ -1755,6 +2061,28 @@ void ui_push_exec ( void ) { return; } + // cache the category/app so we can come back to it another time + if ( ui_selected ) { + pnd_conf_set_char ( g_conf, "minimenu.last_known_app_uid", ui_selected -> ref -> unique_id ); + } + if ( g_categories [ 0 ] ) { + pnd_conf_set_char ( g_conf, "minimenu.last_known_catname", g_categories [ ui_category ] -> catname ); + + // and also the parent cat.. + if ( g_categories [ ui_category ] -> parent_catname ) { + pnd_conf_set_char ( g_conf, "minimenu.last_known_parentcatname", g_categories [ ui_category ] -> parent_catname ); + } else { + char *kv = pnd_box_find_by_key ( g_conf, "minimenu.last_known_parentcatname" ); + if ( kv ) { + pnd_box_delete_node ( g_conf, kv ); + } + + } + } + + // cache last known cat/app to /tmp, so we can use it again later + conf_write ( g_conf, CONF_PREF_TEMPPATH ); + // was this icon generated from filesystem, or from pnd-file? if ( ui_selected -> ref -> object_flags & PND_DISCO_GENERATED ) { @@ -1952,6 +2280,10 @@ void ui_push_ltrigger ( void ) { return; } + if ( g_icon_thread_busy ) { + ui_stop_defered_icon_thread(); + } + if ( ui_category > 0 ) { ui_category--; category_fs_restock ( g_categories [ ui_category ] ); @@ -1980,6 +2312,7 @@ void ui_push_ltrigger ( void ) { ui_rows_scrolled_down = 0; render_mask |= CHANGED_CATEGORY; + ui_start_defered_icon_thread(); return; } @@ -1991,6 +2324,10 @@ void ui_push_rtrigger ( void ) { return; } + if ( g_icon_thread_busy ) { + ui_stop_defered_icon_thread(); + } + unsigned int screen_width = ui_display_context.screen_width; unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" ); @@ -2019,6 +2356,7 @@ void ui_push_rtrigger ( void ) { ui_rows_scrolled_down = 0; render_mask |= CHANGED_CATEGORY; + ui_start_defered_icon_thread(); return; } @@ -2225,6 +2563,9 @@ void ui_set_selected ( mm_appref_t *r ) { render_mask |= CHANGED_SELECTION; + // preview pic stuff + // + if ( ! pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) { return; // no desire to defer anything } @@ -2586,6 +2927,7 @@ void ui_touch_act ( unsigned int x, unsigned int y ) { render_mask |= CHANGED_CATEGORY; // rescan the dir category_fs_restock ( g_categories [ ui_category ] ); + ui_start_defered_icon_thread(); } break; @@ -2639,7 +2981,8 @@ int ui_threaded_timer ( pnd_disco_t *p ) { while ( 1 ) { // pause... - sleep ( delay_s ); + //sleep ( delay_s ); + SDL_Delay ( delay_s * 1000 ); // .. trigger SD check SDL_Event e; @@ -2674,20 +3017,8 @@ unsigned char ui_threaded_defered_preview ( pnd_disco_t *p ) { return ( 0 ); } -SDL_Thread *g_icon_thread = NULL; void ui_post_scan ( void ) { - // if deferred icon load, kick off the thread now - if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_icons_later", 0 ) == 1 ) { - - g_icon_thread = SDL_CreateThread ( (void*)ui_threaded_defered_icon, NULL ); - - if ( ! g_icon_thread ) { - pnd_log ( pndn_error, "ERROR: Couldn't create icon thread\n" ); - } - - } // deferred icon load - // reset view ui_selected = NULL; ui_rows_scrolled_down = 0; @@ -2695,34 +3026,65 @@ void ui_post_scan ( void ) { ui_category = 0; ui_catshift = 0; - // do we have a preferred category to jump to? + // do we have a preferred category to jump to? or a last known one? char *dc = pnd_conf_get_as_char ( g_conf, "categories.default_cat" ); - if ( dc ) { + char *lastcat = pnd_conf_get_as_char ( g_conf, "minimenu.last_known_catname" ); + if ( ( dc ) || + ( pnd_conf_get_as_int_d ( g_conf, "minimenu.start_selected", 0 ) && lastcat ) ) + { + char *catpick = NULL; + + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.start_selected", 0 ) && lastcat ) { + catpick = lastcat; + + // if this is a subcat, we have some doctoring to do :/ the hackishness is really + // starting to show.. + if ( pnd_conf_get_as_char ( g_conf, "minimenu.last_known_parentcatname" ) ) { + // in subcat view, we only have one cat + ui_category = 0; + ui_catshift = 0; + // the cat name to search for is Child*Parent + char key [ 512 ]; + sprintf ( key, "%s*%s", + pnd_conf_get_as_char ( g_conf, "minimenu.last_known_catname" ) + , pnd_conf_get_as_char ( g_conf, "minimenu.last_known_parentcatname" ) ); + category_publish ( CFBYNAME, key ); + // since we forced it by hand, no need to do a cat-scan below + catpick = NULL; + } + + } else if ( dc ) { + catpick = dc; + } // attempt to find default cat; if we do find it, select it; otherwise // default behaviour will pick first cat (ie: usually All) - unsigned int i; - for ( i = 0; i < g_categorycount; i++ ) { - if ( strcasecmp ( g_categories [ i ] -> catname, dc ) == 0 ) { - ui_category = i; - // ensure visibility - unsigned int screen_width = ui_display_context.screen_width; - unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" ); - if ( ui_category > ui_catshift + ( screen_width / tab_width ) - 1 ) { - ui_catshift = ui_category - ( screen_width / tab_width ) + 1; + if ( catpick ) { + unsigned int i; + + for ( i = 0; i < g_categorycount; i++ ) { + if ( strcasecmp ( g_categories [ i ] -> catname, catpick ) == 0 ) { + ui_category = i; + // ensure visibility + unsigned int screen_width = ui_display_context.screen_width; + unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" ); + if ( ui_category > ui_catshift + ( screen_width / tab_width ) - 1 ) { + ui_catshift = ui_category - ( screen_width / tab_width ) + 1; + } + break; } - break; } - } - if ( i == g_categorycount ) { - pnd_log ( pndn_warning, " User defined default category '%s' but not found, so using default behaviour\n", dc ); - } + if ( i == g_categorycount ) { + pnd_log ( pndn_warning, " User defined default category or last known cat '%s' but not found, so using default behaviour\n", catpick ); + } + + } // cat change? } // default cat // if we're sent right to a dirbrowser tab, restock it now (normally we restock on entry) - if ( g_categories [ ui_category ] -> fspath ) { + if ( g_categories [ ui_category ] && g_categories [ ui_category ] -> fspath ) { printf ( "Restock on start: '%s'\n", g_categories [ ui_category ] -> fspath ); category_fs_restock ( g_categories [ ui_category ] ); } @@ -2730,43 +3092,72 @@ void ui_post_scan ( void ) { // redraw all render_mask |= CHANGED_EVERYTHING; + // if deferred icon load, kick off the thread now + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_icons_later", 0 ) == 1 ) { + ui_start_defered_icon_thread(); + } // deferred icon load + return; } unsigned char ui_threaded_defered_icon ( void *p ) { extern pnd_box_handle g_active_apps; - pnd_box_handle h = g_active_apps; unsigned char maxwidth, maxheight; maxwidth = pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_width", 50 ); maxheight = pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_height", 50 ); - pnd_disco_t *iter = pnd_box_get_head ( h ); + pnd_disco_t *iter; + + g_icon_thread_busy = 1; + + // work at it in order within current category - while ( iter ) { + mm_appref_t *refiter = g_categories [ ui_category ] ? g_categories [ ui_category ] -> refs : NULL; + while ( refiter && ! g_icon_thread_stop ) { + iter = refiter -> ref; - // cache it - if ( iter -> pnd_icon_pos && - ! cache_icon ( iter, maxwidth, maxheight ) ) + // has an icon that is not already cached? + if ( ( iter -> pnd_icon_pos ) || + ( iter -> icon && iter -> object_flags & PND_DISCO_LIBPND_DD ) + ) { - pnd_log ( pndn_warning, " Couldn't load icon: '%s'\n", IFNULL(iter->title_en,"No Name") ); - } else { + + // try to cache it? + if ( ! cache_icon ( iter, maxwidth, maxheight ) ) { + //pnd_log ( pndn_warning, " Couldn't load icon: '%s'\n", IFNULL(iter->title_en,"No Name") ); - // trigger that we completed - SDL_Event e; - bzero ( &e, sizeof(SDL_Event) ); - e.type = SDL_USEREVENT; - e.user.code = sdl_user_finishedicon; - SDL_PushEvent ( &e ); + } else { - //pnd_log ( pndn_warning, " Finished deferred load icon: '%s'\n", IFNULL(iter->title_en,"No Name") ); - usleep ( pnd_conf_get_as_int_d ( g_conf, "minimenu.defer_icon_us", 50000 ) ); + // trigger that we completed + SDL_Event e; + bzero ( &e, sizeof(SDL_Event) ); + e.type = SDL_USEREVENT; + e.user.code = sdl_user_finishedicon; + SDL_PushEvent ( &e ); - } + //pnd_log ( pndn_warning, " Finished deferred load icon: '%s'\n", IFNULL(iter->title_en,"No Name") ); + //usleep ( pnd_conf_get_as_int_d ( g_conf, "minimenu.defer_icon_us", 50000 ) ); - // next - iter = pnd_box_get_next ( iter ); - } // while + } + + // avoid churn + iter -> pnd_icon_pos = 0; + if ( iter -> icon ) { + free ( iter -> icon ); + iter -> icon = NULL; + } + + // let user do something.. + SDL_Delay ( 200 ); + + } // has icon + + refiter = refiter -> next; + } + + // mark as done + g_icon_thread_busy = 0; return ( 0 ); } @@ -3116,11 +3507,13 @@ void ui_revealscreen ( void ) { if ( sel >= 0 ) { // fix up category name, if its been hacked +#if 0 // prepending and .. wtf crap is this if ( strchr ( g_categories [ sel ] -> catname, '.' ) ) { char *t = g_categories [ sel ] -> catname; g_categories [ sel ] -> catname = strdup ( strchr ( g_categories [ sel ] -> catname, '.' ) + 1 ); free ( t ); } +#endif // reflag this guy to be visible g_categories [ sel ] -> catflags = CFNORMAL; @@ -3141,13 +3534,14 @@ void ui_revealscreen ( void ) { ui_catshift = ui_category - ( screen_width / tab_width ) + 1; } - // redraw tabs - render_mask |= CHANGED_CATEGORY; } - for ( i = 0; i < g_categorycount; i++ ) { - free ( labels [ i ] ); - } + // republish categories + category_publish ( CFNORMAL, NULL ); + + // redraw tabs + render_mask |= CHANGED_CATEGORY; + ui_start_defered_icon_thread(); return; } @@ -3196,6 +3590,15 @@ void ui_recache_context ( ui_context_t *c ) { SDL_Color tmp = { c -> font_rgba_r, c -> font_rgba_g, c -> font_rgba_b, c -> font_rgba_a }; c -> fontcolor = tmp; + // determine font height + if ( g_grid_font ) { + SDL_Surface *rtext; + rtext = TTF_RenderText_Blended ( g_grid_font, "M", c -> fontcolor ); + c -> text_height = rtext -> h; + } else { + c -> text_height = 10; + } + // now that we've got 'normal' (detail pane shown) param's, lets check if detail pane // is hidden; if so, override some values with those alternate skin values where possible. if ( ui_detail_hidden ) { @@ -3274,6 +3677,9 @@ void ui_menu_context ( mm_appref_t *a ) { context_app_rename, context_app_cpuspeed, context_app_run, + context_app_notes1, + context_app_notes2, + context_app_notes3, context_menu_max }; @@ -3287,7 +3693,10 @@ void ui_menu_context ( mm_appref_t *a ) { "Recategorize subcategory", // recategorize "Change displayed title", // rename "Set CPU speed for launch", // cpuspeed - "Run application" // run + "Run application", // run + "Edit notes line 1", // notes1 + "Edit notes line 2", // notes2 + "Edit notes line 3", // notes3 }; unsigned short int menu [ context_menu_max ]; @@ -3303,6 +3712,11 @@ void ui_menu_context ( mm_appref_t *a ) { menu [ menumax ] = context_file_info; menustring [ menumax++ ] = verbiage [ context_file_info ]; menu [ menumax ] = context_file_delete; menustring [ menumax++ ] = verbiage [ context_file_delete ]; } else { + + if ( a -> ref -> object_type == pnd_object_type_directory ) { + return; // don't do anything if the guy is a subcat-as-folder + } + //menu [ menumax ] = context_app_info; menustring [ menumax++ ] = verbiage [ context_app_info ]; menu [ menumax ] = context_app_run; menustring [ menumax++ ] = verbiage [ context_app_run ]; menu [ menumax ] = context_app_hide; menustring [ menumax++ ] = verbiage [ context_app_hide ]; @@ -3310,6 +3724,9 @@ void ui_menu_context ( mm_appref_t *a ) { menu [ menumax ] = context_app_recategorize_sub; menustring [ menumax++ ] = verbiage [ context_app_recategorize_sub ]; menu [ menumax ] = context_app_rename; menustring [ menumax++ ] = verbiage [ context_app_rename ]; menu [ menumax ] = context_app_cpuspeed; menustring [ menumax++ ] = verbiage [ context_app_cpuspeed ]; + menu [ menumax ] = context_app_notes1; menustring [ menumax++ ] = verbiage [ context_app_notes1 ]; + menu [ menumax ] = context_app_notes2; menustring [ menumax++ ] = verbiage [ context_app_notes2 ]; + menu [ menumax ] = context_app_notes3; menustring [ menumax++ ] = verbiage [ context_app_notes3 ]; } // operate the menu @@ -3349,13 +3766,43 @@ void ui_menu_context ( mm_appref_t *a ) { // write conf, so it will take next time conf_write ( g_conf, conf_determine_location ( g_conf ) ); + conf_write ( g_conf, CONF_PREF_TEMPPATH ); + + // can we just 'hide' this guy without reloading all apps? (this is for you, EvilDragon) + if ( 0 ) { + // + // DOESN'T WORK YET; other parts of app are still hanging onto some values and blow up + // + char *uid = strdup ( a -> ref -> unique_id ); + unsigned int i; + for ( i = 0; i < g_categorycount; i++ ) { + mm_appref_t *p = g_categories [ i ] -> refs; + mm_appref_t *n; + while ( p ) { + n = p -> next; + + if ( strcmp ( p -> ref -> unique_id, uid ) == 0 ) { + free ( p ); + if ( g_categories [ i ] -> refcount ) { + g_categories [ i ] -> refcount--; + } + } + + p = n; + } // while for each appref + } // for each cat/tab + + free ( uid ); + + } else { + // request rescan and wrap up + rescan_apps++; + } - // request rescan and wrap up - rescan_apps++; context_alive = 0; // nolonger visible, so lets just get out } - + break; case context_app_recategorize: @@ -3364,7 +3811,19 @@ void ui_menu_context ( mm_appref_t *a ) { unsigned char optmax = 0; unsigned char i; - i = 0; + // show custom categories + if ( mmcustom_setup() ) { + + for ( i = 0; i < mmcustom_count; i++ ) { + if ( mmcustom_complete [ i ].parent_cat == NULL ) { + opts [ optmax++ ] = mmcustom_complete [ i ].cat; + } + } + + } + + // show FD categories + i = 2; // skip first two - Other and NoParentCategory while ( 1 ) { if ( ! freedesktop_complete [ i ].cat ) { @@ -3378,6 +3837,7 @@ void ui_menu_context ( mm_appref_t *a ) { i++; } // while + // picker char prompt [ 101 ]; snprintf ( prompt, 100, "Pick category [%s]", a -> ref -> main_category ? a -> ref -> main_category : "NoParentCategory" ); @@ -3390,10 +3850,16 @@ void ui_menu_context ( mm_appref_t *a ) { if ( ui_menu_twoby ( confirm, "B/enter; other to cancel", "Confirm categorization", "Do not set category" ) == 1 ) { ovr_replace_or_add ( a, "maincategory", opts [ sel ] ); rescan_apps++; + // when changing main cat, reset subcat, otherwise you go from Game/Emu to Network/Emu and get sent to Other right away + ovr_replace_or_add ( a, "maincategorysub1", freedesktop_complete [ 2 ].cat ); } } + if ( mmcustom_is_ready() ) { + mmcustom_shutdown(); + } + } break; @@ -3401,16 +3867,40 @@ void ui_menu_context ( mm_appref_t *a ) { { char *opts [ 250 ]; unsigned char optmax = 0; - unsigned char i; + unsigned char i = 0; + + char *whichparentarewe; + if ( g_categories [ ui_category ] -> parent_catname ) { + whichparentarewe = g_categories [ ui_category ] -> parent_catname; + } else { + whichparentarewe = g_categories [ ui_category ] -> catname; + } - i = 0; + // add NoSubcategory magic one + opts [ optmax++ ] = freedesktop_complete [ 2 ].cat; + + // add custom categories + if ( mmcustom_setup() ) { + + for ( i = 0; i < mmcustom_count; i++ ) { + if ( mmcustom_complete [ i ].parent_cat && strcmp ( mmcustom_complete [ i ].parent_cat, whichparentarewe ) == 0 ) { + opts [ optmax++ ] = mmcustom_complete [ i ].cat; + } + } + + } + + // add FD categories while ( 1 ) { if ( ! freedesktop_complete [ i ].cat ) { break; } - if ( freedesktop_complete [ i ].parent_cat ) { + if ( ( freedesktop_complete [ i ].parent_cat ) && + ( strcasecmp ( freedesktop_complete [ i ].parent_cat, whichparentarewe ) == 0 ) + ) + { opts [ optmax++ ] = freedesktop_complete [ i ].cat; } @@ -3418,7 +3908,8 @@ void ui_menu_context ( mm_appref_t *a ) { } // while char prompt [ 101 ]; - snprintf ( prompt, 100, "Pick subcategory [%s]", a -> ref -> main_category1 ? a -> ref -> main_category1 : "NoSubcategory" ); + //snprintf ( prompt, 100, "Currently: %s", a -> ref -> main_category1 ? a -> ref -> main_category1 : "NoSubcategory" ); + snprintf ( prompt, 100, "%s [%s]", a -> ref -> main_category1 ? a -> ref -> main_category1 : "NoSubcategory", whichparentarewe ); int sel = ui_modal_single_menu ( opts, optmax, prompt /*"Select subcategory"*/, "Enter to select; other to skip." ); @@ -3433,6 +3924,10 @@ void ui_menu_context ( mm_appref_t *a ) { } + if ( mmcustom_is_ready() ) { + mmcustom_shutdown(); + } + } break; @@ -3486,6 +3981,48 @@ void ui_menu_context ( mm_appref_t *a ) { ui_push_exec(); break; + case context_app_notes1: + case context_app_notes2: + case context_app_notes3: + { + char namebuf [ 101 ] = ""; + + char key [ 501 ]; + unsigned char notenum; + + // which note line? + if ( menu [ sel ] == context_app_notes1 ) { + notenum = 1; + } else if ( menu [ sel ] == context_app_notes2 ) { + notenum = 2; + } else if ( menu [ sel ] == context_app_notes3 ) { + notenum = 3; + } + + // figure out key for looking up existing, and for storing replacement + snprintf ( key, 500, "Application-%u.note-%u", a -> ref -> subapp_number, notenum ); + + // do we have existing value? + if ( a -> ovrh ) { + char *existing = pnd_conf_get_as_char ( a -> ovrh, key ); + if ( existing ) { + strncpy ( namebuf, existing, 100 ); + } + } + + unsigned char changed; + + changed = ui_menu_get_text_line ( "Enter replacement note", "Use keyboard; Enter when done.", + namebuf, namebuf, 30, 0 /* not-numeric-forced */ ); + + if ( changed ) { + ovr_replace_or_add ( a, strchr ( key, '.' ) + 1, namebuf ); + rescan_apps++; + } + + } + break; + default: return; @@ -3504,6 +4041,16 @@ void ui_menu_context ( mm_appref_t *a ) { return; } +unsigned char ui_menu_oneby ( char *title, char *footer, char *one ) { + char *opts [ 2 ]; + opts [ 0 ] = one; + int sel = ui_modal_single_menu ( opts, 1, title, footer ); + if ( sel < 0 ) { + return ( 0 ); + } + return ( sel + 1 ); +} + unsigned char ui_menu_twoby ( char *title, char *footer, char *one, char *two ) { char *opts [ 3 ]; opts [ 0 ] = one; @@ -3529,7 +4076,11 @@ unsigned char ui_menu_get_text_line ( char *title, char *footer, char *initialva bzero ( rects, sizeof(SDL_Rect) * 40 ); if ( initialvalue ) { - strncpy ( r_buffer, initialvalue, maxlen ); + if ( initialvalue == r_buffer ) { + // already good to go + } else { + strncpy ( r_buffer, initialvalue, maxlen ); + } } else { bzero ( r_buffer, maxlen ); } @@ -3620,8 +4171,16 @@ unsigned char ui_menu_get_text_line ( char *title, char *footer, char *initialva char *eol = strchr ( r_buffer, '\0' ); *( eol - 1 ) = '\0'; } + + } else if ( event.key.keysym.sym == SDLK_UP ) { + r_buffer [ 0 ] = '\0'; // truncate! + } else if ( event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_END ) { // return, or "B" - return ( 1 ); + // on Enter/Return or B, if the buffer has 1 or more chars, we return it as valid.. otherwise, invalid. + if ( strlen ( r_buffer ) > 0 ) { + return ( 1 ); + } + return ( 0 ); } else if ( event.key.keysym.sym == SDLK_LSHIFT || event.key.keysym.sym == SDLK_RSHIFT ) { shifted = 1; @@ -3671,12 +4230,12 @@ unsigned char ui_menu_get_text_line ( char *title, char *footer, char *initialva } // while waiting for input } // while - + return ( 0 ); } unsigned char ovr_replace_or_add ( mm_appref_t *a, char *keybase, char *newvalue ) { - printf ( "setting %s:%u - '%s' to '%s' - %s/%s\n", a -> ref -> title_en, a -> ref -> subapp_number, keybase, newvalue, a -> ref -> object_path, a -> ref -> object_filename ); + //printf ( "setting %s:%u - '%s' to '%s' - %s/%s\n", a -> ref -> title_en, a -> ref -> subapp_number, keybase, newvalue, a -> ref -> object_path, a -> ref -> object_filename ); char fullpath [ PATH_MAX ]; @@ -3685,7 +4244,7 @@ unsigned char ovr_replace_or_add ( mm_appref_t *a, char *keybase, char *newvalue if ( dot ) { sprintf ( dot, PXML_SAMEPATH_OVERRIDE_FILEEXT ); } else { - fprintf ( stderr, "ERROR: Bad pnd-path in disco_t! %s\n", fullpath ); + pnd_log ( pndn_error, "ERROR: Bad pnd-path in disco_t! %s\n", fullpath ); return ( 0 ); } @@ -3725,3 +4284,370 @@ unsigned char ovr_replace_or_add ( mm_appref_t *a, char *keybase, char *newvalue return ( 1 ); } + +void ui_manage_categories ( void ) { + unsigned char require_app_scan = 0; + + if ( ! mmcustom_setup() ) { + return; // error + } + + char *opts [ 20 ] = { + "List custom categories", + "List custom subcategories", + "Register custom category", + "Register custom subcategory", + "Unregister custom category", + "Unregister custom subcategory", + "Done" + }; + + while ( 1 ) { + + int sel = ui_modal_single_menu ( opts, 7, "Custom Categories", "B to select; other to cancel." ); + + switch ( sel ) { + + case 0: // list custom + ui_pick_custom_category ( 0 ); + break; + + case 1: // list custom sub + if ( mmcustom_count ) { + + char *maincat = ui_pick_custom_category ( 2 ); + + if ( maincat ) { + unsigned int subcount = mmcustom_count_subcats ( maincat ); + char titlebuf [ 201 ]; + + snprintf ( titlebuf, 200, "Category: %s", maincat ); + + if ( subcount == 0 ) { + ui_menu_oneby ( titlebuf, "B/Enter to accept", "Category has no subcategories." ); + } else { + + char **list = malloc ( subcount * sizeof(char*) ); + int i; + unsigned int counter = 0; + + for ( i = 0; i < mmcustom_count; i++ ) { + if ( mmcustom_complete [ i ].parent_cat && strcasecmp ( mmcustom_complete [ i ].parent_cat, maincat ) == 0 ) { + list [ counter++ ] = mmcustom_complete [ i ].cat; + } + } + + ui_modal_single_menu ( list, counter, titlebuf, "Any button to exit." ); + + free ( list ); + + } // more than 0 subcats? + + } // user picked a main cat? + + } else { + ui_menu_oneby ( "Warning", "B/Enter to accept", "There are none registered." ); + } + break; + + case 2: // register custom + { + unsigned char changed; + char namebuf [ 101 ] = ""; + + changed = ui_menu_get_text_line ( "Enter unique category name", "Use keyboard; Enter when done.", + "Pandora", namebuf, 30, 0 /* alphanumeric */ ); + + // did the user enter something? + if ( changed ) { + + // for now, force use of '*' into something else as we use * internally :/ (FIXME) + { + char *fixme; + while ( ( fixme = strchr ( namebuf, '*' ) ) ) { + *fixme = '_'; + } + } + + // and if so, is it existant already or not? + if ( mmcustom_query ( namebuf, NULL ) ) { + ui_menu_oneby ( "Warning", "B/Enter to accept", "Already a registered category." ); + } else if ( freedesktop_category_query ( namebuf, NULL ) ) { + ui_menu_oneby ( "Warning", "B/Enter to accept", "Already a Standard category." ); + } else { + + char confirm [ 1001 ]; + snprintf ( confirm, 1000, "Confirm: %s", namebuf ); + + if ( ui_menu_twoby ( confirm, "B/enter; other to cancel", "Confirm new category", "Do not register" ) == 1 ) { + // register, save, recycle the current list + mmcustom_register ( namebuf, NULL ); + mmcustom_write ( NULL ); + mmcustom_shutdown(); + mmcustom_setup(); + } + + } // dupe? + + } // entered something? + + } + break; + + case 3: // register custom sub + if ( 1 /*mmcustom_count -- we allow FD cats now, so this isn't applicable error */ ) { + + char *maincat = ui_pick_custom_category ( 1 /* include FD */ ); + + if ( maincat ) { + char titlebuf [ 201 ]; + + snprintf ( titlebuf, 200, "Subcat of: %s", maincat ); + + unsigned char changed; + char namebuf [ 101 ] = ""; + + changed = ui_menu_get_text_line ( titlebuf, "Use keyboard; Enter when done.", "Submarine", namebuf, 30, 0 /* alphanumeric */ ); + + // did the user enter something? + if ( changed ) { + + // for now, force use of '*' into something else as we use * internally :/ (FIXME) + { + char *fixme; + while ( ( fixme = strchr ( namebuf, '*' ) ) ) { + *fixme = '_'; + } + } + + // and if so, is it existant already or not? + if ( mmcustom_query ( namebuf, maincat ) ) { + ui_menu_oneby ( "Warning", "B/Enter to accept", "Already a subcategory." ); + } else if ( freedesktop_category_query ( namebuf, maincat ) ) { + ui_menu_oneby ( "Warning", "B/Enter to accept", "Already a Standard subcategory." ); + } else { + + char confirm [ 1001 ]; + snprintf ( confirm, 1000, "Confirm: %s [%s]", namebuf, maincat ); + + if ( ui_menu_twoby ( confirm, "B/enter; other to cancel", "Confirm new category", "Do not register" ) == 1 ) { + // register, save, recycle the current list + mmcustom_register ( namebuf, maincat ); + mmcustom_write ( NULL ); + mmcustom_shutdown(); + mmcustom_setup(); + } + + } // dupe? + + } // entered something? + + } // selected parent cat? + + } else { + ui_menu_oneby ( "Warning", "B/Enter to accept", "No categories registered." ); + } + break; + + case 4: // unreg custom + if ( mmcustom_count ) { + char *maincat = ui_pick_custom_category ( 0 ); + + if ( maincat ) { + char confirm [ 1001 ]; + snprintf ( confirm, 1000, "Confirm remove: %s", maincat ); + + if ( ui_menu_twoby ( confirm, "B/enter; other to cancel", "Confirm unregister", "Do not unregister" ) == 1 ) { + // register, save, recycle the current list + mmcustom_unregister ( maincat, NULL ); + mmcustom_write ( NULL ); + mmcustom_shutdown(); + mmcustom_setup(); + } + + } // picked? + + } else { + ui_menu_oneby ( "Warning", "B/Enter to accept", "There are none registered." ); + } + break; + + case 5: // unreg custom sub + if ( mmcustom_count ) { + char *maincat = ui_pick_custom_category ( 2 ); + + if ( maincat ) { + unsigned int subcount = mmcustom_count_subcats ( maincat ); + char titlebuf [ 201 ]; + + snprintf ( titlebuf, 200, "Category: %s", maincat ); + + if ( subcount == 0 ) { + ui_menu_oneby ( titlebuf, "B/Enter to accept", "Category has no subcategories." ); + } else { + + char **list = malloc ( subcount * sizeof(char*) ); + int i; + unsigned int counter = 0; + + for ( i = 0; i < mmcustom_count; i++ ) { + if ( mmcustom_complete [ i ].parent_cat && strcasecmp ( mmcustom_complete [ i ].parent_cat, maincat ) == 0 ) { + list [ counter++ ] = mmcustom_complete [ i ].cat; + } + } + + int sel = ui_modal_single_menu ( list, counter, titlebuf, "B to selct; other to exit." ); + + if ( sel >= 0 ) { + char confirm [ 1001 ]; + snprintf ( confirm, 1000, "Confirm remove: %s", list [ sel ] ); + + if ( ui_menu_twoby ( confirm, "B/enter; other to cancel", "Confirm unregister", "Do not unregister" ) == 1 ) { + // register, save, recycle the current list + mmcustom_unregister ( list [ sel ], maincat ); + mmcustom_write ( NULL ); + mmcustom_shutdown(); + mmcustom_setup(); + } + + } // confirm kill? + + free ( list ); + + } // more than 0 subcats? + + } // user picked a main cat? + + } else { + ui_menu_oneby ( "Warning", "B/Enter to accept", "There are none registered." ); + } + break; + + } // switch + + // exeunt + if ( sel < 0 || sel > 5 ) { + break; + } + + } // while running the menu + + // shut down custom cats + mmcustom_shutdown(); + + // reload apps? + if ( require_app_scan ) { + applications_free(); + applications_scan(); + } + + // redraw + render_mask |= CHANGED_EVERYTHING; + + return; +} + +// mode 0 == custom main only; 1 == custom main + FD main; 2 == custom main + FD mains-with-custom-subs +char *ui_pick_custom_category ( unsigned char mode ) { + char **list; + int i; + unsigned int counter = 0; + + // alloc space for list, depending on scope + if ( mode > 0 ) { + list = malloc ( (mmcustom_count+freedesktop_count()) * sizeof(char*) ); + } else { + list = malloc ( mmcustom_count * sizeof(char*) ); + } + + // add custom mains + for ( i = 0; i < mmcustom_count; i++ ) { + if ( mmcustom_complete [ i ].parent_cat == NULL ) { + list [ counter++ ] = mmcustom_complete [ i ].cat; + } + } + + // add FD if needed + if ( mode > 0 ) { + i = 3; + + while ( 1 ) { + + if ( ! freedesktop_complete [ i ].cat ) { + break; + } + + // if FD main cat + if ( freedesktop_complete [ i ].parent_cat == NULL ) { + + // mode 1 == include them all + // mode 2 == include them if they have a custom subcat + if ( ( mode == 1 ) || + ( mmcustom_subcount ( freedesktop_complete [ i ].cat ) ) ) + { + list [ counter++ ] = freedesktop_complete [ i ].cat; + } + + } // if parent cat + + i++; + } // while + + } // if + + // we actually showing anything? + if ( ! counter ) { + ui_menu_oneby ( "Warning", "B/Enter to accept", "There are none registered." ); + return ( NULL ); + } + + // do it + int sel = ui_modal_single_menu ( list, counter, "Custom Categories", "Any button to exit." ); + + if ( sel < 0 ) { + free ( list ); + return ( NULL ); + } + + char *foo = list [ sel ]; + free ( list ); + + return ( foo ); +} + +void ui_start_defered_icon_thread ( void ) { + + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_icons_later", 0 ) != 1 ) { + return; + } + + if ( g_icon_thread_busy ) { + //fprintf ( stderr, "REM: Waiting for thread to stop..\n" ); + ui_stop_defered_icon_thread(); + } + + //fprintf ( stderr, "WARN: Starting new icon caching thread..\n" ); + g_icon_thread = SDL_CreateThread ( (void*)ui_threaded_defered_icon, NULL ); + + if ( ! g_icon_thread ) { + pnd_log ( pndn_error, "ERROR: Couldn't create icon thread\n" ); + } + + return; +} + +void ui_stop_defered_icon_thread ( void ) { + time_t started = time ( NULL ); + + // ask thread to stop, then wait for it (if two run at same time, or if we change + // category content under neath it, could be bad..) + g_icon_thread_stop = 1; + while ( g_icon_thread_busy ) { + time ( NULL ); // spin + } + g_icon_thread_stop = 0; + + fprintf ( stderr, "REM: Thread stoppage took %u seconds.\n", (int) ( time ( NULL ) - started ) ); + + return; +}