X-Git-Url: http://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-libraries.git;a=blobdiff_plain;f=minimenu%2Fmmui.c;h=b362918c125ed9d3e0a65abef7d8f4f43c7accf8;hp=7a2e21d6948d6b2672aebe3f491155e617385fa1;hb=19e5cf271b1031c7aa3ff4c6c9c3935b13be2132;hpb=d49ca74fc248536cc496e1ab156fd7a479c0638a diff --git a/minimenu/mmui.c b/minimenu/mmui.c index 7a2e21d..b362918 100644 --- a/minimenu/mmui.c +++ b/minimenu/mmui.c @@ -9,6 +9,8 @@ #include /* for making ftw.h happy */ #include #include +#include + #include "SDL.h" #include "SDL_audio.h" #include "SDL_image.h" @@ -28,12 +30,18 @@ #include "../lib/pnd_pathiter.h" #include "pnd_utility.h" #include "pnd_pndfiles.h" +#include "pnd_notify.h" +#include "pnd_dbusnotify.h" #include "mmenu.h" #include "mmcat.h" #include "mmcache.h" #include "mmui.h" #include "mmwrapcmd.h" +#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 */ @@ -43,15 +51,23 @@ #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 */ SDL_Surface *sdl_realscreen = NULL; unsigned int sdl_ticks = 0; SDL_Thread *g_preview_thread = NULL; +SDL_Thread *g_timer_thread = NULL; enum { sdl_user_ticker = 0, sdl_user_finishedpreview = 1, sdl_user_finishedicon = 2, + sdl_user_checksd = 3, }; /* app state @@ -73,14 +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_icons; // 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 -extern mm_category_t *g_categories; -extern unsigned char g_categorycount; -extern mm_category_t _categories_invis [ MAX_CATS ]; -extern unsigned char _categories_inviscount; - -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 ) { @@ -143,6 +160,9 @@ unsigned char ui_setup ( void ) { } #endif + // key repeat + SDL_EnableKeyRepeat ( 500, 125 /* 150 */ ); + // images //IMG_Init ( IMG_INIT_JPG | IMG_INIT_PNG ); @@ -192,6 +212,14 @@ unsigned char ui_setup ( void ) { return ( 0 ); // couldn't set up SDL TTF } + // determine display context + if ( pnd_conf_get_as_int_d ( g_conf, "display.show_detail_pane", 1 ) > 0 ) { + ui_detail_hidden = 0; + } else { + ui_detail_hidden = 1; + } + ui_recache_context ( &ui_display_context ); + return ( 1 ); } @@ -219,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 @@ -235,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 // @@ -314,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; @@ -323,64 +387,118 @@ void ui_render ( void ) { unsigned int row, displayrow, col; mm_appref_t *appiter; - unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 ); + ui_context_t *c = &ui_display_context; // for convenience and shorthand - unsigned char row_max = pnd_conf_get_as_int_d ( g_conf, "grid.row_max", 4 ); - unsigned char col_max = pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ); + // 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 ); + } - unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 ); - unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 ); - unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 ); - unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 ); +#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; - unsigned int grid_offset_x = pnd_conf_get_as_int ( g_conf, "grid.grid_offset_x" ); - unsigned int grid_offset_y = pnd_conf_get_as_int ( g_conf, "grid.grid_offset_y" ); + // 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" ); - unsigned int icon_offset_x = pnd_conf_get_as_int ( g_conf, "grid.icon_offset_x" ); - unsigned int icon_offset_y = pnd_conf_get_as_int ( g_conf, "grid.icon_offset_y" ); - unsigned int icon_max_width = pnd_conf_get_as_int ( g_conf, "grid.icon_max_width" ); - unsigned int icon_max_height = pnd_conf_get_as_int ( g_conf, "grid.icon_max_height" ); - unsigned int sel_icon_offset_x = pnd_conf_get_as_int_d ( g_conf, "grid.sel_offoffset_x", 0 ); - unsigned int sel_icon_offset_y = pnd_conf_get_as_int_d ( g_conf, "grid.sel_offoffset_y", 0 ); + if ( previous_unique_id ) { - unsigned int text_width = pnd_conf_get_as_int ( g_conf, "grid.text_width" ); - unsigned int text_clip_x = pnd_conf_get_as_int ( g_conf, "grid.text_clip_x" ); - unsigned int text_offset_x = pnd_conf_get_as_int ( g_conf, "grid.text_offset_x" ); - unsigned int text_offset_y = pnd_conf_get_as_int ( g_conf, "grid.text_offset_y" ); + // 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; + } - unsigned int cell_width = pnd_conf_get_as_int ( g_conf, "grid.cell_width" ); - unsigned int cell_height = pnd_conf_get_as_int ( g_conf, "grid.cell_height" ); + if ( previter ) { + ui_selected = previter; + } - // how many total rows do we need? - icon_rows = g_categories [ ui_category ].refcount / col_max; - if ( g_categories [ ui_category ].refcount % col_max > 0 ) { - icon_rows++; - } + } // last known app? - // if no selected app yet, select the first one -#if 0 - if ( ! ui_selected ) { - ui_selected = g_categories [ ui_category ].refs; } #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 - ui_register_reset(); + if ( render_jobs_b ) { + ui_register_reset(); + } // ensure selection is visible if ( ui_selected ) { - int index = ui_selected_index(); - int topleft = col_max * ui_rows_scrolled_down; - int botright = ( col_max * ( ui_rows_scrolled_down + row_max ) - 1 ); + unsigned char autoscrolled = 1; + while ( autoscrolled ) { + autoscrolled = 0; - if ( index < topleft ) { - ui_rows_scrolled_down -= pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 ); - render_jobs_b |= R_ALL; - } 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; - } + int index = ui_selected_index(); + + 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 if ( ui_rows_scrolled_down < 0 ) { ui_rows_scrolled_down = 0; @@ -388,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 ) { @@ -416,16 +534,28 @@ void ui_render ( void ) { // tabs if ( g_imagecache [ IMG_TAB_SEL ].i && g_imagecache [ IMG_TAB_UNSEL ].i ) { - unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" ); - unsigned int tab_height = pnd_conf_get_as_int ( g_conf, "tabs.tab_height" ); - //unsigned int tab_selheight = pnd_conf_get_as_int ( g_conf, "tabs.tab_selheight" ); - unsigned int tab_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_x" ); - unsigned int tab_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_y" ); - unsigned int text_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_x" ); - unsigned int text_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_y" ); - unsigned int text_width = pnd_conf_get_as_int ( g_conf, "tabs.text_width" ); - unsigned int maxtab = ( screen_width / tab_width ) < g_categorycount ? ( screen_width / tab_width ) + ui_catshift : g_categorycount + ui_catshift; - unsigned int maxtabspot = ( screen_width / tab_width ); + static unsigned int tab_width; + static unsigned int tab_height; + static unsigned int tab_offset_x; + static unsigned int tab_offset_y; + static unsigned int text_offset_x; + static unsigned int text_offset_y; + static unsigned int text_width; + + static unsigned char tab_first_run = 1; + if ( tab_first_run ) { + tab_first_run = 0; + tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" ); + tab_height = pnd_conf_get_as_int ( g_conf, "tabs.tab_height" ); + tab_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_x" ); + tab_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.tab_offset_y" ); + text_offset_x = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_x" ); + text_offset_y = pnd_conf_get_as_int ( g_conf, "tabs.text_offset_y" ); + text_width = pnd_conf_get_as_int ( g_conf, "tabs.text_width" ); + } + + unsigned int maxtab = ( c -> screen_width / tab_width ) < g_categorycount ? ( c -> screen_width / tab_width ) + ui_catshift : g_categorycount + ui_catshift; + unsigned int maxtabspot = ( c -> screen_width / tab_width ); if ( g_categorycount > 0 ) { @@ -512,8 +642,7 @@ void ui_render ( void ) { // draw text SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_tab_font, g_categories [ col ].catname, tmpfontcolor ); + rtext = TTF_RenderText_Blended ( g_tab_font, g_categories [ col ] -> catname, c -> fontcolor ); src.x = 0; src.y = 0; src.w = rtext -> w < text_width ? rtext -> w : text_width; @@ -560,8 +689,8 @@ void ui_render ( void ) { // up? if ( ui_rows_scrolled_down && g_imagecache [ IMG_ARROW_UP ].i ) { - dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_x", 450 ); - dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_y", 80 ); + dest -> x = c -> arrow_up_x; + dest -> y = c -> arrow_up_y; SDL_BlitSurface ( g_imagecache [ IMG_ARROW_UP ].i, NULL /* whole image */, sdl_realscreen, dest ); dest++; @@ -569,9 +698,9 @@ void ui_render ( void ) { } // down? - if ( ui_rows_scrolled_down + row_max < icon_rows && g_imagecache [ IMG_ARROW_DOWN ].i ) { - dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_x", 450 ); - dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_y", 80 ); + if ( ui_rows_scrolled_down + c -> row_max < icon_rows && g_imagecache [ IMG_ARROW_DOWN ].i ) { + dest -> x = c -> arrow_down_x; + dest -> y = c -> arrow_down_y; SDL_BlitSurface ( g_imagecache [ IMG_ARROW_DOWN ].i, NULL /* whole image */, sdl_realscreen, dest ); dest++; @@ -582,10 +711,10 @@ void ui_render ( void ) { // show scrollbar as well src.x = 0; src.y = 0; - src.w = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_w", 10 ); - src.h = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_h", 100 ); - dest -> x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_x", 450 ); - dest -> y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_y", 100 ); + src.w = c -> arrow_bar_clip_w; + src.h = c -> arrow_bar_clip_h; + dest -> x = c -> arrow_bar_x; + dest -> y = c -> arrow_bar_y; SDL_BlitSurface ( g_imagecache [ IMG_ARROW_SCROLLBAR ].i, &src /* whole image */, sdl_realscreen, dest ); dest++; } // bar @@ -593,7 +722,7 @@ void ui_render ( void ) { } // r_bg, scroll bars // render detail pane bg - if ( render_jobs_b & R_DETAIL ) { + if ( render_jobs_b & R_DETAIL && ui_detail_hidden == 0 ) { if ( pnd_conf_get_as_int_d ( g_conf, "detailpane.show", 1 ) ) { @@ -622,18 +751,18 @@ void ui_render ( void ) { } // r_details // anything to render? - if ( render_jobs_b & R_GRID ) { + if ( render_jobs_b & R_GRID && g_categorycount ) { // if just rendering grid, and nothing else, better clear it first if ( ! ( render_jobs_b & R_BG ) ) { if ( g_imagecache [ IMG_BACKGROUND_800480 ].i ) { - src.x = grid_offset_x; - src.y = grid_offset_y + sel_icon_offset_y; - src.w = col_max * cell_width; - src.h = row_max * cell_height; + src.x = c -> grid_offset_x; + src.y = c -> grid_offset_y + c -> sel_icon_offset_y; + src.w = c -> col_max * c -> cell_width; + src.h = c -> row_max * c -> cell_height; - dest -> x = grid_offset_x; - dest -> y = grid_offset_y + sel_icon_offset_y; + dest -> x = c -> grid_offset_x; + dest -> y = c -> grid_offset_y + c -> sel_icon_offset_y; SDL_BlitSurface ( g_imagecache [ IMG_BACKGROUND_800480 ].i, &src, sdl_realscreen, dest ); dest++; @@ -641,118 +770,243 @@ void ui_render ( void ) { } } - if ( g_categories [ ui_category ].refs ) { + // 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 < 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 = grid_offset_x + ( col * cell_width ) + icon_offset_x + sel_icon_offset_x; - //dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y + ( ( icon_max_height - s -> h ) / 2 ); - dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y + sel_icon_offset_y; - SDL_BlitSurface ( s, NULL /* all */, sdl_realscreen, dest ); - dest++; - // text - dest -> x = grid_offset_x + ( col * cell_width ) + text_clip_x; - dest -> y = grid_offset_y + ( displayrow * cell_height ) + pnd_conf_get_as_int ( g_conf, "grid.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; - } - - } - - // 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 = grid_offset_x + ( col * cell_width ) + icon_offset_x + (( icon_max_width - iconsurface -> w ) / 2); - dest -> y = grid_offset_y + ( displayrow * cell_height ) + icon_offset_y + (( 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++; - + 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 ); } - // show text + // show title text if ( appiter -> ref -> title_en ) { SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_grid_font, appiter -> ref -> title_en, tmpfontcolor ); + rtext = TTF_RenderText_Blended ( g_grid_font, appiter -> ref -> title_en, c -> fontcolor ); src.x = 0; src.y = 0; - src.w = text_width < rtext -> w ? 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 > text_width ) { - dest -> x = grid_offset_x + ( col * cell_width ) + text_clip_x; - } else { - dest -> x = grid_offset_x + ( col * cell_width ) + 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 = grid_offset_y + ( displayrow * cell_height ) + 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.. + } + + // 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 ); - // are we done displaying rows? - if ( displayrow >= row_max ) { - break; - } + } + + } // 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++; + + } - } // while + // 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? @@ -761,8 +1015,8 @@ void ui_render ( void ) { } // r_grid - // detail panel - if ( ui_selected && render_jobs_b & R_DETAIL ) { + // detail panel - show app details or blank-message + if ( render_jobs_b & R_DETAIL && ui_detail_hidden == 0 ) { unsigned int cell_offset_x = pnd_conf_get_as_int ( g_conf, "detailtext.cell_offset_x" ); unsigned int cell_offset_y = pnd_conf_get_as_int ( g_conf, "detailtext.cell_offset_y" ); @@ -770,34 +1024,157 @@ void ui_render ( void ) { unsigned int desty = cell_offset_y; - char buffer [ 256 ]; + if ( ui_selected ) { - // full name - if ( ui_selected -> ref -> title_en ) { - SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_detailtext_font, ui_selected -> ref -> title_en, tmpfontcolor ); - src.x = 0; - src.y = 0; - src.w = rtext -> w < cell_width ? rtext -> w : cell_width; - src.h = rtext -> h; - dest -> x = cell_offset_x; - dest -> y = desty; - SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); - SDL_FreeSurface ( rtext ); - dest++; - desty += src.h; - } + char buffer [ 256 ]; + + // full name + if ( ui_selected -> ref -> title_en ) { + SDL_Surface *rtext; + rtext = TTF_RenderText_Blended ( g_detailtext_font, ui_selected -> ref -> title_en, c -> fontcolor ); + src.x = 0; + src.y = 0; + src.w = rtext -> w < cell_width ? rtext -> w : cell_width; + src.h = rtext -> h; + dest -> x = cell_offset_x; + dest -> y = desty; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); + SDL_FreeSurface ( rtext ); + dest++; + desty += src.h; + } - // category + // category #if 0 - if ( ui_selected -> ref -> main_category ) { + if ( ui_selected -> ref -> main_category ) { + + sprintf ( buffer, "Category: %s", ui_selected -> ref -> main_category ); + + SDL_Surface *rtext; + rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, c -> fontcolor ); + src.x = 0; + src.y = 0; + src.w = rtext -> w < cell_width ? rtext -> w : cell_width; + src.h = rtext -> h; + dest -> x = cell_offset_x; + dest -> y = desty; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); + SDL_FreeSurface ( rtext ); + dest++; + desty += src.h; + } +#endif + + // clock + if ( ui_selected -> ref -> clockspeed ) { + + sprintf ( buffer, "CPU Clock: %s", ui_selected -> ref -> clockspeed ); + + SDL_Surface *rtext; + rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, c -> fontcolor ); + src.x = 0; + src.y = 0; + src.w = rtext -> w < cell_width ? rtext -> w : cell_width; + src.h = rtext -> h; + dest -> x = cell_offset_x; + dest -> y = desty; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); + SDL_FreeSurface ( rtext ); + dest++; + desty += src.h; + } + + // show sub-app# on right side of cpu clock? + //if ( ui_selected -> ref -> subapp_number ) + { + sprintf ( buffer, "(app#%u)", ui_selected -> ref -> subapp_number ); + + SDL_Surface *rtext; + rtext = TTF_RenderText_Blended ( g_grid_font, buffer, c -> fontcolor ); + dest -> x = cell_offset_x + cell_width - rtext -> w; + dest -> y = desty - src.h; + SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest ); + SDL_FreeSurface ( rtext ); + dest++; + } + + // info hint +#if 0 // merged into hint-line + if ( ui_selected -> ref -> info_filename ) { + + sprintf ( buffer, "Documentation - hit Y" ); + + SDL_Surface *rtext; + rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, c -> fontcolor ); + src.x = 0; + src.y = 0; + src.w = rtext -> w < cell_width ? rtext -> w : cell_width; + src.h = rtext -> h; + dest -> x = cell_offset_x; + dest -> y = desty; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); + SDL_FreeSurface ( rtext ); + dest++; + desty += src.h; + } +#endif + + // notes + if ( ui_selected -> ovrh ) { + char *n; + unsigned char i; + char buffer [ 50 ]; + + desty += 5; // a touch of spacing can't hurt + + for ( i = 1; i < 4; i++ ) { + sprintf ( buffer, "Application-%u.note-%u", ui_selected -> ref -> subapp_number, i ); + n = pnd_conf_get_as_char ( ui_selected -> ovrh, buffer ); + + if ( n ) { + SDL_Surface *rtext; + rtext = TTF_RenderText_Blended ( g_detailtext_font, n, c -> fontcolor ); + src.x = 0; + src.y = 0; + src.w = rtext -> w < cell_width ? rtext -> w : cell_width; + src.h = rtext -> h; + dest -> x = cell_offset_x; + dest -> y = desty; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); + SDL_FreeSurface ( rtext ); + dest++; + desty += rtext -> h; + } + } // for - sprintf ( buffer, "Category: %s", ui_selected -> ref -> main_category ); + } // r_detail -> notes + + // preview pic + mm_cache_t *ic = cache_query_preview ( ui_selected -> ref -> unique_id ); + SDL_Surface *previewpic; + + if ( ic ) { + previewpic = ic -> i; + } else { + previewpic = g_imagecache [ IMG_PREVIEW_MISSING ].i; + } + + if ( previewpic ) { + dest -> x = pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_offset_x", 50 ) + + ( ( pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 50 ) - previewpic -> w ) / 2 ); + dest -> y = pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_offset_y", 50 ); + SDL_BlitSurface ( previewpic, NULL /* whole image */, sdl_realscreen, dest ); + dest++; + } + + } else { + + char *empty_message = "Press SELECT for menu"; SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, tmpfontcolor ); + + rtext = TTF_RenderText_Blended ( g_detailtext_font, empty_message, c -> fontcolor ); + src.x = 0; src.y = 0; src.w = rtext -> w < cell_width ? rtext -> w : cell_width; @@ -807,117 +1184,36 @@ void ui_render ( void ) { SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); SDL_FreeSurface ( rtext ); dest++; - desty += src.h; - } -#endif - - // clock - if ( ui_selected -> ref -> clockspeed ) { - sprintf ( buffer, "CPU Clock: %s", ui_selected -> ref -> clockspeed ); + desty += src.h; - SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, tmpfontcolor ); - src.x = 0; - src.y = 0; - src.w = rtext -> w < cell_width ? rtext -> w : cell_width; - src.h = rtext -> h; + rtext = TTF_RenderText_Blended ( g_detailtext_font, "START or B to run app", c -> fontcolor ); dest -> x = cell_offset_x; dest -> y = desty; SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); SDL_FreeSurface ( rtext ); dest++; desty += src.h; - } - - // show sub-app# on right side of cpu clock? - //if ( ui_selected -> ref -> subapp_number ) - { - sprintf ( buffer, "(app#%u)", ui_selected -> ref -> subapp_number ); - SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor ); - dest -> x = cell_offset_x + cell_width - rtext -> w; - dest -> y = desty - src.h; - SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest ); + rtext = TTF_RenderText_Blended ( g_detailtext_font, "SPACE for app menu", c -> fontcolor ); + dest -> x = cell_offset_x; + dest -> y = desty; + SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); SDL_FreeSurface ( rtext ); dest++; - } - - // info hint -#if 0 // merged into hint-line - if ( ui_selected -> ref -> info_filename ) { - - sprintf ( buffer, "Documentation - hit Y" ); + desty += src.h; - SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_detailtext_font, buffer, tmpfontcolor ); - src.x = 0; - src.y = 0; - src.w = rtext -> w < cell_width ? rtext -> w : cell_width; - src.h = rtext -> h; + rtext = TTF_RenderText_Blended ( g_detailtext_font, "A to toggle details", c -> fontcolor ); dest -> x = cell_offset_x; dest -> y = desty; SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); SDL_FreeSurface ( rtext ); dest++; desty += src.h; - } -#endif - - // notes - if ( ui_selected -> ovrh ) { - char *n; - unsigned char i; - char buffer [ 50 ]; - - desty += 5; // a touch of spacing can't hurt - - for ( i = 1; i < 4; i++ ) { - sprintf ( buffer, "Application-%u.note-%u", ui_selected -> ref -> subapp_number, i ); - n = pnd_conf_get_as_char ( ui_selected -> ovrh, buffer ); - - if ( n ) { - SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_detailtext_font, n, tmpfontcolor ); - src.x = 0; - src.y = 0; - src.w = rtext -> w < cell_width ? rtext -> w : cell_width; - src.h = rtext -> h; - dest -> x = cell_offset_x; - dest -> y = desty; - SDL_BlitSurface ( rtext, &src, sdl_realscreen, dest ); - SDL_FreeSurface ( rtext ); - dest++; - desty += rtext -> h; - } - } // for - - } // r_detail -> notes - // preview pic - mm_cache_t *ic = cache_query_preview ( ui_selected -> ref -> unique_id ); - SDL_Surface *previewpic; + } // r_detail && selected? - if ( ic ) { - previewpic = ic -> i; - } else { - previewpic = g_imagecache [ IMG_PREVIEW_MISSING ].i; - } - - if ( previewpic ) { - dest -> x = pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_offset_x", 50 ) + - ( ( pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 50 ) - previewpic -> w ) / 2 ); - dest -> y = pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_offset_y", 50 ); - SDL_BlitSurface ( previewpic, NULL /* whole image */, sdl_realscreen, dest ); - dest++; - } - - } // r_detail && selected? + } // r_detail // extras // @@ -936,8 +1232,7 @@ void ui_render ( void ) { sprintf ( buffer, "Battery: %u%%", batterylevel ); SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor ); + rtext = TTF_RenderText_Blended ( g_grid_font, buffer, c -> fontcolor ); dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.battery_x", 20 ); dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.battery_y", 450 ); SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest ); @@ -960,8 +1255,7 @@ void ui_render ( void ) { } SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor ); + rtext = TTF_RenderText_Blended ( g_grid_font, buffer, c -> fontcolor ); // clear bg if ( ! ( render_jobs_b & R_BG ) ) { @@ -995,8 +1289,7 @@ void ui_render ( void ) { strftime ( buffer, 50, "%a %H:%M %F", tm ); SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_grid_font, buffer, tmpfontcolor ); + rtext = TTF_RenderText_Blended ( g_grid_font, buffer, c -> fontcolor ); dest -> x = pnd_conf_get_as_int_d ( g_conf, "display.clock_x", 700 ); dest -> y = pnd_conf_get_as_int_d ( g_conf, "display.clock_y", 450 ); SDL_BlitSurface ( rtext, NULL /* all */, sdl_realscreen, dest ); @@ -1011,14 +1304,14 @@ void ui_render ( void ) { } // ui_render -void ui_process_input ( unsigned char block_p ) { +void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) { SDL_Event event; unsigned char ui_event = 0; // if we get a ui event, flip to 1 and break //static ui_sdl_button_e ui_mask = uisb_none; // current buttons down - while ( ! ui_event && - block_p ? SDL_WaitEvent ( &event ) : SDL_PollEvent ( &event ) ) + while ( ( ! ui_event ) && + /*block_p ?*/ SDL_WaitEvent ( &event ) /*: SDL_PollEvent ( &event )*/ ) { switch ( event.type ) { @@ -1026,11 +1319,23 @@ void ui_process_input ( unsigned char block_p ) { case SDL_USEREVENT: // update something + // the user-defined SDL events are all for threaded/delayed previews (and icons, which + // generally are not used); if we're in wide mode, we can skip previews + // to avoid slowing things down when they're not shown. + if ( event.user.code == sdl_user_ticker ) { - // timer went off, time to load something + if ( ui_detail_hidden ) { + break; // skip building previews when not showing them + } + + // timer went off, time to load something if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.load_previews_later", 0 ) ) { + if ( ! ui_selected ) { + break; + } + // load the preview pics now! pnd_disco_t *iter = ui_selected -> ref; @@ -1075,7 +1380,29 @@ void ui_process_input ( unsigned char block_p ) { // redraw, so we can show the newly loaded icon ui_event++; - } + } else if ( event.user.code == sdl_user_checksd ) { + // check if any inotify-type events occured, forcing us to rescan/re-disco the SDs + + unsigned char watch_dbus = 0; + unsigned char watch_inotify = 0; + + if ( dbh ) { + watch_dbus = pnd_dbusnotify_rediscover_p ( dbh ); + } + + if ( nh ) { + watch_inotify = pnd_notify_rediscover_p ( nh ); + } + + if ( watch_dbus || watch_inotify ) { + pnd_log ( pndn_debug, "dbusnotify detected SD event\n" ); + applications_free(); + applications_scan(); + + ui_event++; + } + + } // SDL user event render_mask |= CHANGED_EVERYTHING; @@ -1173,11 +1500,14 @@ void ui_process_input ( unsigned char block_p ) { #endif #if 1 // keyboard events - case SDL_KEYUP: + //case SDL_KEYUP: + case SDL_KEYDOWN: //pnd_log ( pndn_debug, "key up %u\n", event.key.keysym.sym ); // SDLK_LALT -> Start + // page up/down for y/x + // home/end for a and b // directional if ( event.key.keysym.sym == SDLK_RIGHT ) { @@ -1192,21 +1522,52 @@ void ui_process_input ( unsigned char block_p ) { } else if ( event.key.keysym.sym == SDLK_DOWN ) { ui_push_down(); ui_event++; - } else if ( event.key.keysym.sym == SDLK_SPACE || event.key.keysym.sym == SDLK_END ) { + } else if ( event.key.keysym.sym == SDLK_END ) { // B ui_push_exec(); ui_event++; - } else if ( event.key.keysym.sym == SDLK_RSHIFT ) { + } else if ( event.key.keysym.sym == SDLK_SPACE ) { // space + if ( ui_selected ) { + ui_menu_context ( ui_selected ); + } else { + // TBD: post an error? + } + render_mask |= CHANGED_EVERYTHING; + ui_event++; + } else if ( event.key.keysym.sym == SDLK_TAB || event.key.keysym.sym == SDLK_HOME ) { // tab or A + // if detail panel is togglable, then toggle it + // if not, make sure its ruddy well shown! + if ( ui_is_detail_hideable() ) { + ui_toggle_detail_pane(); + } else { + ui_detail_hidden = 0; + } + ui_event++; + } else if ( event.key.keysym.sym == SDLK_RSHIFT || event.key.keysym.sym == SDLK_COMMA ) { // left trigger or comma ui_push_ltrigger(); ui_event++; - } else if ( event.key.keysym.sym == SDLK_RCTRL ) { + } else if ( event.key.keysym.sym == SDLK_RCTRL || event.key.keysym.sym == SDLK_PERIOD ) { // right trigger or period ui_push_rtrigger(); ui_event++; - } else if ( event.key.keysym.sym == SDLK_PAGEUP ) { + + } else if ( event.key.keysym.sym == SDLK_PAGEUP ) { // Y // info if ( ui_selected ) { ui_show_info ( pnd_run_script, ui_selected -> ref ); ui_event++; } + } else if ( event.key.keysym.sym == SDLK_PAGEDOWN ) { // X + ui_push_backup(); + + // forget the selection, nolonger applies + ui_selected = NULL; + ui_set_selected ( ui_selected ); + // rescan the dir + if ( g_categories [ ui_category ] -> fspath ) { + category_fs_restock ( g_categories [ ui_category ] ); + } + // redraw the grid + render_mask |= CHANGED_EVERYTHING; + ui_event++; } else if ( event.key.keysym.sym == SDLK_LALT ) { // start button ui_push_exec(); @@ -1214,8 +1575,11 @@ void ui_process_input ( unsigned char block_p ) { } else if ( event.key.keysym.sym == SDLK_LCTRL /*LALT*/ ) { // select button char *opts [ 20 ] = { + "Toggle view icons<->list", "Reveal hidden category", "Shutdown Pandora", + "Configure Minimenu", + "Manage custom app categories", "Rescan for applications", "Cache previews to SD now", "Run a terminal/console", @@ -1224,23 +1588,42 @@ void ui_process_input ( unsigned char block_p ) { "Select a Minimenu skin", "About Minimenu" }; - int sel = ui_modal_single_menu ( opts, 9, "Minimenu", "Enter to select; other to return." ); + int sel = ui_modal_single_menu ( opts, 12, "Minimenu", "Enter to select; other to return." ); char buffer [ 100 ]; if ( sel == 0 ) { + if ( ui_viewmode == uiv_list ) { + ui_viewmode = uiv_icons; + } else { + ui_viewmode = uiv_list; + } + } else if ( sel == 1 ) { // do nothing ui_revealscreen(); - } else if ( sel == 1 ) { + } else if ( sel == 2 ) { + // 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 ); - } else if ( sel == 2 ) { + } else if ( sel == 3 ) { + // 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 == 4 ) { + // manage custom categories + ui_manage_categories(); + } else if ( sel == 5 ) { // rescan apps pnd_log ( pndn_debug, "Freeing up applications\n" ); applications_free(); pnd_log ( pndn_debug, "Rescanning applications\n" ); applications_scan(); - } else if ( sel == 3 ) { + } else if ( sel == 6 ) { // cache preview to SD now extern pnd_box_handle g_active_apps; pnd_box_handle h = g_active_apps; @@ -1263,7 +1646,7 @@ void ui_process_input ( unsigned char block_p ) { iter = pnd_box_get_next ( iter ); } // while - } else if ( sel == 4 ) { + } else if ( sel == 7 ) { // run terminal char *argv[5]; argv [ 0 ] = pnd_conf_get_as_char ( g_conf, "utility.terminal" ); @@ -1273,18 +1656,18 @@ void ui_process_input ( unsigned char block_p ) { ui_forkexec ( argv ); } - } else if ( sel == 5 ) { + } else if ( sel == 8 ) { char buffer [ PATH_MAX ]; sprintf ( buffer, "%s %s\n", MM_RUN, "/usr/pandora/scripts/op_switchgui.sh" ); emit_and_quit ( buffer ); - } else if ( sel == 6 ) { + } else if ( sel == 9 ) { emit_and_quit ( MM_QUIT ); - } else if ( sel == 7 ) { + } else if ( sel == 10 ) { // select skin if ( ui_pick_skin() ) { emit_and_quit ( MM_RESTART ); } - } else if ( sel == 8 ) { + } else if ( sel == 11 ) { // about char buffer [ PATH_MAX ]; sprintf ( buffer, "%s/about.txt", g_skinpath ); @@ -1300,31 +1683,48 @@ void ui_process_input ( unsigned char block_p ) { // many SDLK_keycodes map to ASCII ("a" is ascii(a)), so try to jump to a filename of that name, in this category? // and if already there, try to jump to next, maybe? // future: look for sequence typing? ie: user types 'm' then 'a', look for 'ma*' instead of 'm' then 'a' matching - if ( isalpha ( event.key.keysym.sym ) && g_categories [ ui_category ].refcount > 0 ) { - mm_appref_t *app = g_categories [ ui_category ].refs; + if ( isalpha ( event.key.keysym.sym ) && g_categories [ ui_category ] -> refcount > 0 ) { + mm_appref_t *app = g_categories [ ui_category ] -> refs; + + //fprintf ( stderr, "sel %s next %s\n", ui_selected -> ref -> title_en, ui_selected -> next -> ref -> title_en ); + + // are we already matching the same char? and next item is also same char? + if ( app && ui_selected && ui_selected -> next && + ui_selected -> ref -> title_en && ui_selected -> next -> ref -> title_en && + toupper ( ui_selected -> ref -> title_en [ 0 ] ) == toupper ( ui_selected -> next -> ref -> title_en [ 0 ] ) && + toupper ( ui_selected -> ref -> title_en [ 0 ] ) == toupper ( event.key.keysym.sym ) + ) + { + // just skip down one + app = ui_selected -> next; + } else { - // walk the category, looking for a first-char match - while ( app ) { - if ( app -> ref -> title_en && toupper ( app -> ref -> title_en [ 0 ] ) == toupper ( event.key.keysym.sym ) ) { - break; + // walk the category, looking for a first-char match + while ( app ) { + if ( app -> ref -> title_en && toupper ( app -> ref -> title_en [ 0 ] ) == toupper ( event.key.keysym.sym ) ) { + break; + } + app = app -> next; } - app = app -> next; - } + + } // same start letter, or new run? // found something, or no? if ( app ) { // looks like we found a potential match; try switching it to visible selection ui_selected = app; ui_set_selected ( ui_selected ); + ui_event++; } + } // SDLK_alphanumeric? } // SDLK_.... // extras -#if 0 - if ( event.key.keysym.sym == SDLK_q ) { +#if 1 + if ( event.key.keysym.sym == SDLK_ESCAPE ) { emit_and_quit ( MM_QUIT ); } #endif @@ -1366,6 +1766,16 @@ void ui_process_input ( unsigned char block_p ) { 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; @@ -1379,18 +1789,18 @@ void ui_push_left ( unsigned char forcecoil ) { pnd_conf_get_as_int_d ( g_conf, "grid.wrap_horiz_samerow", 0 ) && col == 0 ) { - unsigned int i = pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1; + unsigned int i = ui_display_context.col_max - 1; while ( i && ui_selected -> next ) { ui_push_right ( 0 ); i--; } - } else if ( g_categories [ ui_category ].refs == ui_selected ) { + } else if ( g_categories [ ui_category ] -> refs == ui_selected ) { // can't go any more left, 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; + mm_appref_t *i = g_categories [ ui_category ] -> refs; while ( i ) { if ( i -> next == ui_selected ) { ui_selected = i; @@ -1407,6 +1817,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? @@ -1416,7 +1836,7 @@ void ui_push_right ( unsigned char forcecoil ) { if ( forcecoil == 0 && pnd_conf_get_as_int_d ( g_conf, "grid.wrap_horiz_samerow", 0 ) && // and selected is far-right, or last icon in category (might not be far right) - ( ( col == pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ) - 1 ) || + ( ( col == ui_display_context.col_max - 1 ) || ( ui_selected -> next == NULL ) ) ) { @@ -1437,7 +1857,7 @@ void ui_push_right ( unsigned char forcecoil ) { } } else { - ui_selected = g_categories [ ui_category ].refs; + ui_selected = g_categories [ ui_category ] -> refs; } ui_set_selected ( ui_selected ); @@ -1446,24 +1866,49 @@ void ui_push_right ( unsigned char forcecoil ) { } void ui_push_up ( void ) { - unsigned char col_max = pnd_conf_get_as_int ( g_conf, "grid.col_max" ); + 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 ); if ( row == 0 && - pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 1 ) == 0 ) + pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 0 ) == 0 ) { // wrap around instead unsigned int col = ui_determine_screen_col ( ui_selected ); // go to end - ui_selected = g_categories [ ui_category ].refs; + ui_selected = g_categories [ ui_category ] -> refs; while ( ui_selected -> next ) { ui_selected = ui_selected -> next; } @@ -1480,7 +1925,7 @@ void ui_push_up ( void ) { // scroll down to show it int r = ui_determine_row ( ui_selected ) - 1; - if ( r - pnd_conf_get_as_int ( g_conf, "grid.row_max" ) > 0 ) { + if ( r - ui_display_context.row_max > 0 ) { ui_rows_scrolled_down = (unsigned int) r; } @@ -1498,7 +1943,25 @@ void ui_push_up ( void ) { } void ui_push_down ( void ) { - unsigned char col_max = pnd_conf_get_as_int ( g_conf, "grid.col_max" ); + + 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 ) { @@ -1506,19 +1969,19 @@ void ui_push_down ( void ) { unsigned int row = ui_determine_row ( ui_selected ); // max rows? - unsigned int icon_rows = g_categories [ ui_category ].refcount / col_max; - if ( g_categories [ ui_category ].refcount % col_max > 0 ) { + unsigned int icon_rows = g_categories [ ui_category ] -> refcount / col_max; + if ( g_categories [ ui_category ] -> refcount % col_max > 0 ) { icon_rows++; } // we at the end? if ( row == ( icon_rows - 1 ) && - pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 1 ) == 0 ) + pnd_conf_get_as_int_d ( g_conf, "grid.wrap_vert_stop", 0 ) == 0 ) { unsigned char col = ui_determine_screen_col ( ui_selected ); - ui_selected = g_categories [ ui_category ].refs; + ui_selected = g_categories [ ui_category ] -> refs; while ( col ) { ui_selected = ui_selected -> next; @@ -1545,12 +2008,88 @@ void ui_push_down ( void ) { return; } +// 'backup' is currently 'X', for going back up in a folder/subcat without having to hit exec on the '..' entry +void ui_push_backup ( void ) { + + // a subcat-as-dir, or a dir browser? + if ( g_categories [ ui_category] -> fspath ) { + // dir browser, just climb our way back up + + // go up + char *c; + + // lop off last word; if the thing ends with /, lop that one, then the next word. + while ( ( c = strrchr ( g_categories [ ui_category] -> fspath, '/' ) ) ) { + *c = '\0'; // lop off the last hunk + if ( *(c+1) != '\0' ) { + break; + } + } // while + + // nothing left? + if ( g_categories [ ui_category] -> fspath [ 0 ] == '\0' ) { + free ( g_categories [ ui_category] -> fspath ); + g_categories [ ui_category] -> fspath = strdup ( "/" ); + } + + } else { + // a pnd subcat .. are we in one, or at the 'top'? + char *pcatname = g_categories [ ui_category ] -> parent_catname; + + if ( ! pcatname ) { + return; // we're at the 'top' already + } + + // set to first cat! + ui_category = 0; + + // republish cats .. shoudl just be the one + category_publish ( CFNORMAL, NULL ); + + if ( pcatname ) { + ui_category = category_index ( pcatname ); + + // ensure tab visible? + unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" ); + if ( ui_category > ui_catshift + ( ui_display_context.screen_width / tab_width ) - 1 ) { + ui_catshift = ui_category - ( ui_display_context.screen_width / tab_width ) + 1; + } + + } + + } // dir or subcat? + + return; +} + void ui_push_exec ( void ) { if ( ! ui_selected ) { 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 ) { @@ -1559,40 +2098,59 @@ void ui_push_exec ( void ) { } if ( ui_selected -> ref -> object_type == pnd_object_type_directory ) { - // delve up/down the dir tree - if ( strcmp ( ui_selected -> ref -> title_en, ".." ) == 0 ) { - // go up - char *c; + // check if this guy is a dir-browser tab, or is a directory on a pnd tab + if ( ! g_categories [ ui_category] -> fspath ) { + // pnd subcat as dir - // lop off last word; if the thing ends with /, lop that one, then the next word. - while ( ( c = strrchr ( g_categories [ ui_category].fspath, '/' ) ) ) { - *c = '\0'; // lop off the last hunk - if ( *(c+1) != '\0' ) { - break; - } - } // while + // are we already in a subcat? if so, go back to parent; there is no grandparenting or deeper + if ( g_categories [ ui_category ] -> parent_catname ) { + // go back up + ui_push_backup(); + + } else { + // delve into subcat + + // set to first cat! + ui_category = 0; + ui_catshift = 0; + + // republish cats .. shoudl just be the one + category_publish ( CFBYNAME, ui_selected -> ref -> object_path ); - // nothing left? - if ( g_categories [ ui_category].fspath [ 0 ] == '\0' ) { - strcpy ( g_categories [ ui_category].fspath, "/" ); } + // forget the selection, nolonger applies + ui_selected = NULL; + ui_set_selected ( ui_selected ); + // redraw the grid + render_mask |= CHANGED_EVERYTHING; + } else { - // go down - strcat ( g_categories [ ui_category].fspath, "/" ); - strcat ( g_categories [ ui_category].fspath, ui_selected -> ref -> title_en ); - } - pnd_log ( pndn_debug, "Cat %s is now in path %s\n", g_categories [ ui_category ].catname, g_categories [ ui_category ].fspath ); + // delve up/down the dir tree + if ( strcmp ( ui_selected -> ref -> title_en, ".." ) == 0 ) { + ui_push_backup(); + + } else { + // go down + char *temp = malloc ( strlen ( g_categories [ ui_category] -> fspath ) + strlen ( ui_selected -> ref -> title_en ) + 1 + 1 ); + sprintf ( temp, "%s/%s", g_categories [ ui_category] -> fspath, ui_selected -> ref -> title_en ); + free ( g_categories [ ui_category] -> fspath ); + g_categories [ ui_category] -> fspath = temp; + } + + pnd_log ( pndn_debug, "Cat %s is now in path %s\n", g_categories [ ui_category ] -> catname, g_categories [ ui_category ]-> fspath ); + + // forget the selection, nolonger applies + ui_selected = NULL; + ui_set_selected ( ui_selected ); + // rescan the dir + category_fs_restock ( g_categories [ ui_category ] ); + // redraw the grid + render_mask |= CHANGED_SELECTION; - // rescan the dir - category_fs_restock ( &(g_categories [ ui_category]) ); - // forget the selection, nolonger applies - ui_selected = NULL; - ui_set_selected ( ui_selected ); - // redraw the grid - render_mask |= CHANGED_SELECTION; + } // directory browser or pnd subcat? } else { // just run it arbitrarily? @@ -1607,32 +2165,114 @@ void ui_push_exec ( void ) { pnd_apps_exec_disco ( pnd_run_script, d, PND_EXEC_OPTION_NORUN, NULL ); char buffer [ PATH_MAX ]; sprintf ( buffer, "%s %s\n", MM_RUN, pnd_apps_exec_runline() ); - emit_and_quit ( buffer ); + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.live_on_run", 0 ) == 0 ) { + emit_and_quit ( buffer ); + } else { + emit_and_run ( buffer ); + } } } else { // random bin file + + // is it even executable? if we don't have handlers for non-executables yet (Jan 2011 we don't), + // then don't even try to run things not-flagged as executable.. but wait most people are on + // FAT filesystems, what a drag, we can't tell at the fs level. + // ... but we can still invoke 'file' and grep out the good bits, at least. + // + // open a stream reading 'file /path/to/file' and check output for 'executable' + // -- not checking for "ARM" so it can pick up x86 (or whatever native) executables in build environment + unsigned char is_executable = 0; + + // popen test + { + char popenbuf [ FILENAME_MAX ]; + snprintf ( popenbuf, FILENAME_MAX, "%s %s/%s", MIMETYPE_EXE, g_categories [ ui_category ] -> fspath, ui_selected -> ref -> title_en ); + + FILE *marceau; + if ( ! ( marceau = popen ( popenbuf, "r" ) ) ) { + return; // error, we need some useful error handling and dialog boxes here + } + + if ( fgets ( popenbuf, FILENAME_MAX, marceau ) ) { + //printf ( "File test returns: %s\n", popenbuf ); + if ( strstr ( popenbuf, "executable" ) != NULL ) { + is_executable = 1; + } + } + + pclose ( marceau ); + + } // popen test + + if ( ! is_executable ) { + fprintf ( stderr, "ERROR: File to invoke is not executable, skipping. (%s)\n", ui_selected -> ref -> title_en ); + return; // need some error handling here + } + +#if 0 // eat up any pending SDL events and toss 'em? + { + SDL_PumpEvents(); + SDL_Event e; + while ( SDL_PeepEvents ( &e, 1, SDL_GETEVENT, SDL_ALLEVENTS ) > 0 ) { + // spin + } + } +#endif + #if 1 + // just exec it + // + + // get CWD so we can restore it on return char cwd [ PATH_MAX ]; getcwd ( cwd, PATH_MAX ); - chdir ( g_categories [ ui_category ].fspath ); - pnd_exec_no_wait_1 ( ui_selected -> ref -> title_en, NULL ); + // full path to executable so we don't rely on implicit "./" + char execbuf [ FILENAME_MAX ]; + snprintf ( execbuf, FILENAME_MAX, "%s/%s", g_categories [ ui_category ] -> fspath, ui_selected -> ref -> title_en ); + + // do it! + chdir ( g_categories [ ui_category ] -> fspath ); + exec_raw_binary ( execbuf /*ui_selected -> ref -> title_en*/ ); chdir ( cwd ); #else + // DEPRECATED / NOT TESTED + // get mmwrapper to run it char buffer [ PATH_MAX ]; - sprintf ( buffer, "%s %s/%s\n", MM_RUN, g_categories [ ui_category ].fspath, ui_selected -> ref -> title_en ); - emit_and_quit ( buffer ); + sprintf ( buffer, "%s %s/%s\n", MM_RUN, g_categories [ ui_category ] -> fspath, ui_selected -> ref -> title_en ); + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.live_on_run", 0 ) == 0 ) { + emit_and_quit ( buffer ); + } else { + emit_and_run ( buffer ); + } #endif } // pnd or bin? } // dir or file? } else { + + // set app-run speed + int use_run_speed = pnd_conf_get_as_int_d ( g_conf, "minimenu.use_run_speed", 0 ); + if ( use_run_speed > 0 ) { + int mm_speed = pnd_conf_get_as_int_d ( g_conf, "minimenu.run_speed", -1 ); + if ( mm_speed > -1 ) { + char buffer [ 512 ]; + snprintf ( buffer, 500, "sudo /usr/pandora/scripts/op_cpuspeed.sh %d", mm_speed ); + system ( buffer ); + } + } // do speed change? + + // request app to run and quit mmenu pnd_apps_exec_disco ( pnd_run_script, ui_selected -> ref, PND_EXEC_OPTION_NORUN, NULL ); char buffer [ PATH_MAX ]; sprintf ( buffer, "%s %s\n", MM_RUN, pnd_apps_exec_runline() ); - emit_and_quit ( buffer ); + if ( pnd_conf_get_as_int_d ( g_conf, "minimenu.live_on_run", 0 ) == 0 ) { + emit_and_quit ( buffer ); + } else { + emit_and_run ( buffer ); + } } return; @@ -1640,16 +2280,20 @@ void ui_push_exec ( void ) { void ui_push_ltrigger ( void ) { unsigned char oldcat = ui_category; - unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 ); + unsigned int screen_width = ui_display_context.screen_width; unsigned int tab_width = pnd_conf_get_as_int ( g_conf, "tabs.tab_width" ); if ( g_categorycount == 0 ) { return; } + if ( g_icon_thread_busy ) { + ui_stop_defered_icon_thread(); + } + if ( ui_category > 0 ) { ui_category--; - category_fs_restock ( &(g_categories [ ui_category ]) ); + category_fs_restock ( g_categories [ ui_category ] ); } else { if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) { ui_category = g_categorycount - 1; @@ -1657,7 +2301,7 @@ void ui_push_ltrigger ( void ) { if ( ui_category >= ( screen_width / tab_width ) ) { ui_catshift = ui_category - ( screen_width / tab_width ) + 1; } - category_fs_restock ( &(g_categories [ ui_category ]) ); + category_fs_restock ( g_categories [ ui_category ] ); } } @@ -1675,6 +2319,7 @@ void ui_push_ltrigger ( void ) { ui_rows_scrolled_down = 0; render_mask |= CHANGED_CATEGORY; + ui_start_defered_icon_thread(); return; } @@ -1686,17 +2331,21 @@ void ui_push_rtrigger ( void ) { return; } - unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 ); + 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" ); if ( ui_category < ( g_categorycount - 1 ) ) { ui_category++; - category_fs_restock ( &(g_categories [ ui_category ]) ); + category_fs_restock ( g_categories [ ui_category ] ); } else { if ( pnd_conf_get_as_int_d ( g_conf, "tabs.wraparound", 0 ) > 0 ) { ui_category = 0; ui_catshift = 0; - category_fs_restock ( &(g_categories [ ui_category ]) ); + category_fs_restock ( g_categories [ ui_category ] ); } } @@ -1714,6 +2363,7 @@ void ui_push_rtrigger ( void ) { ui_rows_scrolled_down = 0; render_mask |= CHANGED_CATEGORY; + ui_start_defered_icon_thread(); return; } @@ -1750,18 +2400,12 @@ void ui_loadscreen ( void ) { SDL_Rect dest; - unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 ); - unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 ); - unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 ); - unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 ); - // clear the screen SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 ); // render text SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_big_font, "Setting up menu...", tmpfontcolor ); + rtext = TTF_RenderText_Blended ( g_big_font, "Setting up menu...", ui_display_context.fontcolor ); dest.x = 20; dest.y = 20; SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, &dest ); @@ -1775,11 +2419,6 @@ void ui_discoverscreen ( unsigned char clearscreen ) { SDL_Rect dest; - unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 ); - unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 ); - unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 ); - unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 ); - // clear the screen if ( clearscreen ) { SDL_FillRect( SDL_GetVideoSurface(), NULL, 0 ); @@ -1798,8 +2437,7 @@ void ui_discoverscreen ( unsigned char clearscreen ) { // render text SDL_Surface *rtext; - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - rtext = TTF_RenderText_Blended ( g_big_font, "Looking for applications...", tmpfontcolor ); + rtext = TTF_RenderText_Blended ( g_big_font, "Looking for applications...", ui_display_context.fontcolor ); if ( clearscreen ) { dest.x = 20; dest.y = 20; @@ -1914,7 +2552,7 @@ int ui_selected_index ( void ) { return ( -1 ); // no index } - mm_appref_t *r = g_categories [ ui_category ].refs; + mm_appref_t *r = g_categories [ ui_category ] -> refs; int counter = 0; while ( r ) { if ( r == ui_selected ) { @@ -1932,6 +2570,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 } @@ -1976,14 +2617,7 @@ int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *f unsigned int sel = 0; - unsigned int font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 ); - unsigned int font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 ); - unsigned int font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 ); - unsigned int font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 ); - - SDL_Color tmpfontcolor = { font_rgba_r, font_rgba_g, font_rgba_b, font_rgba_a }; - - SDL_Color selfontcolor = { 0/*font_rgba_r*/, font_rgba_g, font_rgba_b, font_rgba_a }; + SDL_Color selfontcolor = { 0/*font_rgba_r*/, ui_display_context.font_rgba_g, ui_display_context.font_rgba_b, ui_display_context.font_rgba_a }; unsigned int i; SDL_Event event; @@ -1999,16 +2633,16 @@ int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *f // show dialog background if ( g_imagecache [ IMG_DETAIL_BG ].i ) { - src.x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ); - src.y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ); - src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> w; - src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_PANEL ].i)) -> h; + src.x = 0; + src.y = 0; + src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_BG ].i)) -> w; + src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_BG ].i)) -> h; dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ); dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ); SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest ); // repeat for darken? - SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest ); - SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest ); + //SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest ); + //SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest ); //SDL_UpdateRects ( sdl_realscreen, 1, &dest ); dest++; } @@ -2024,7 +2658,7 @@ int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *f // show header if ( title ) { - rtext = TTF_RenderText_Blended ( g_tab_font, title, tmpfontcolor ); + rtext = TTF_RenderText_Blended ( g_tab_font, title, ui_display_context.fontcolor ); dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20; dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 20; SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest ); @@ -2034,7 +2668,7 @@ int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *f // show footer if ( footer ) { - rtext = TTF_RenderText_Blended ( g_tab_font, footer, tmpfontcolor ); + rtext = TTF_RenderText_Blended ( g_tab_font, footer, ui_display_context.fontcolor ); dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20; dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h @@ -2051,7 +2685,7 @@ int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *f if ( sel == i ) { rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], selfontcolor ); } else { - rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], tmpfontcolor ); + rtext = TTF_RenderText_Blended ( g_tab_font, argv [ i ], ui_display_context.fontcolor ); } dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20; dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 40 + ( 20 * ( i + 1 - first_visible ) ); @@ -2070,7 +2704,8 @@ int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *f switch ( event.type ) { - case SDL_KEYUP: + //case SDL_KEYUP: + case SDL_KEYDOWN: if ( event.key.keysym.sym == SDLK_UP ) { if ( sel ) { @@ -2120,29 +2755,29 @@ int ui_modal_single_menu ( char *argv[], unsigned int argc, char *title, char *f unsigned char ui_determine_row ( mm_appref_t *a ) { unsigned int row = 0; - mm_appref_t *i = g_categories [ ui_category ].refs; + mm_appref_t *i = g_categories [ ui_category ] -> refs; while ( i != a ) { i = i -> next; row++; } // while - row /= pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ); + row /= ui_display_context.col_max; return ( row ); } unsigned char ui_determine_screen_row ( mm_appref_t *a ) { - return ( ui_determine_row ( a ) % pnd_conf_get_as_int_d ( g_conf, "grid.row_max", 5 ) ); + return ( ui_determine_row ( a ) % ui_display_context.row_max ); } unsigned char ui_determine_screen_col ( mm_appref_t *a ) { unsigned int col = 0; - mm_appref_t *i = g_categories [ ui_category ].refs; + mm_appref_t *i = g_categories [ ui_category ] -> refs; while ( i != a ) { i = i -> next; col++; } // while - col %= pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ); + col %= ui_display_context.col_max; return ( col ); } @@ -2297,6 +2932,9 @@ void ui_touch_act ( unsigned int x, unsigned int y ) { } ui_category = t -> catnum; render_mask |= CHANGED_CATEGORY; + // rescan the dir + category_fs_restock ( g_categories [ ui_category ] ); + ui_start_defered_icon_thread(); } break; @@ -2326,6 +2964,45 @@ unsigned char ui_forkexec ( char *argv[] ) { return ( 1 ); } +unsigned char ui_threaded_timer_create ( void ) { + + g_timer_thread = SDL_CreateThread ( (void*)ui_threaded_timer, NULL ); + + if ( ! g_timer_thread ) { + pnd_log ( pndn_error, "ERROR: Couldn't create timer thread\n" ); + return ( 0 ); + } + + return ( 1 ); +} + +int ui_threaded_timer ( pnd_disco_t *p ) { + + // this timer's job is to .. + // - do nothing for quite awhile + // - on wake, post event to SDL event queue, so that main thread will check if SD insert/eject occurred + // - goto 10 + + unsigned int delay_s = 2; // seconds + + while ( 1 ) { + + // pause... + //sleep ( delay_s ); + SDL_Delay ( delay_s * 1000 ); + + // .. trigger SD check + SDL_Event e; + bzero ( &e, sizeof(SDL_Event) ); + e.type = SDL_USEREVENT; + e.user.code = sdl_user_checksd; + SDL_PushEvent ( &e ); + + } // while + + return ( 0 ); +} + unsigned char ui_threaded_defered_preview ( pnd_disco_t *p ) { if ( ! cache_preview ( p, pnd_conf_get_as_int_d ( g_conf, "previewpic.cell_width", 200 ), @@ -2347,20 +3024,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; @@ -2368,72 +3033,138 @@ 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 = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 ); - 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 ] && g_categories [ ui_category ] -> fspath ) { + printf ( "Restock on start: '%s'\n", g_categories [ ui_category ] -> fspath ); + category_fs_restock ( g_categories [ ui_category ] ); + } + // 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; - while ( iter ) { + g_icon_thread_busy = 1; - // cache it - if ( iter -> pnd_icon_pos && - ! cache_icon ( iter, maxwidth, maxheight ) ) - { - pnd_log ( pndn_warning, " Couldn't load icon: '%s'\n", IFNULL(iter->title_en,"No Name") ); - } else { + // work at it in order within current category - // 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 ); + mm_appref_t *refiter = g_categories [ ui_category ] ? g_categories [ ui_category ] -> refs : NULL; + while ( refiter && ! g_icon_thread_stop ) { + iter = refiter -> ref; - //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 ) ); + // has an icon that is not already cached? + if ( ( iter -> pnd_icon_pos ) || + ( iter -> icon && iter -> object_flags & PND_DISCO_LIBPND_DD ) + ) + { + + // 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") ); - } + } else { - // next - iter = pnd_box_get_next ( iter ); - } // while + // 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 ) ); + + } + + // 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 ); } @@ -2723,7 +3454,7 @@ void ui_aboutscreen ( char *textpath ) { SDL_Event e; - if ( SDL_PeepEvents ( &e, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_KEYUP) ) > 0 ) { + if ( SDL_PeepEvents ( &e, 1, SDL_GETEVENT, SDL_EVENTMASK(/*SDL_KEYUP|*/SDL_KEYDOWN) ) > 0 ) { return; } @@ -2754,52 +3485,1176 @@ void ui_revealscreen ( void ) { char *labels [ 500 ]; unsigned int labelmax = 0; unsigned int i; + char fulllabel [ 200 ]; - if ( ! _categories_inviscount ) { + if ( ! category_count ( CFHIDDEN ) ) { return; // nothing to do } - for ( i = 0; i < _categories_inviscount; i++ ) { - labels [ labelmax++ ] = _categories_invis [ i ].catname; + // switch to hidden categories + category_publish ( CFHIDDEN, NULL ); + + // build up labels to show in menu + for ( i = 0; i < g_categorycount; i++ ) { + + if ( g_categories [ i ] -> parent_catname ) { + sprintf ( fulllabel, "%s [%s]", g_categories [ i ] -> catname, g_categories [ i ] -> parent_catname ); + } else { + sprintf ( fulllabel, "%s", g_categories [ i ] -> catname ); + } + + labels [ labelmax++ ] = strdup ( fulllabel ); } + // show menu int sel = ui_modal_single_menu ( labels, labelmax, "Temporary Category Reveal", "Enter to select; other to return." ); + // if selected, try to set this guy to visible if ( sel >= 0 ) { - if ( category_query ( _categories_invis [ sel ].catname ) ) { - // already present - return; - } - // fix up category name, if its been hacked - if ( strchr ( _categories_invis [ sel ].catname, '.' ) ) { - char *t = _categories_invis [ sel ].catname; - _categories_invis [ sel ].catname = strdup ( strchr ( _categories_invis [ sel ].catname, '.' ) + 1 ); +#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 ); } - // copy invisi-cat into live-cat - memmove ( &(g_categories [ g_categorycount ]), &(_categories_invis [ sel ]), sizeof(mm_category_t) ); - g_categorycount++; - // move subsequent invisi-cats up, so the selected invisi-cat is nolonger existing in invisi-list at - // all (avoid double-free() later) - memmove ( &(_categories_invis [ sel ]), &(_categories_invis [ sel + 1 ]), sizeof(mm_category_t) * ( _categories_inviscount - sel - 1 ) ); - _categories_inviscount--; +#endif + + // reflag this guy to be visible + g_categories [ sel ] -> catflags = CFNORMAL; + + // switch to the new category.. cache name. + char *switch_to_name = g_categories [ sel ] -> catname; + + // republish categories + category_publish ( CFNORMAL, NULL ); - // switch to the new category - ui_category = g_categorycount - 1; + // switch to the new category.. with the cached name! + ui_category = category_index ( switch_to_name ); // ensure visibility - unsigned int screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 ); + 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; } - // redraw tabs - render_mask |= CHANGED_CATEGORY; } + // republish categories + category_publish ( CFNORMAL, NULL ); + + // redraw tabs + render_mask |= CHANGED_CATEGORY; + ui_start_defered_icon_thread(); + + return; +} + +void ui_recache_context ( ui_context_t *c ) { + + c -> screen_width = pnd_conf_get_as_int_d ( g_conf, "display.screen_width", 800 ); + + c -> font_rgba_r = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_r", 200 ); + c -> font_rgba_g = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_g", 200 ); + c -> font_rgba_b = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_b", 200 ); + c -> font_rgba_a = pnd_conf_get_as_int_d ( g_conf, "display.font_rgba_a", 100 ); + + c -> grid_offset_x = pnd_conf_get_as_int ( g_conf, "grid.grid_offset_x" ); + c -> grid_offset_y = pnd_conf_get_as_int ( g_conf, "grid.grid_offset_y" ); + + c -> icon_offset_x = pnd_conf_get_as_int ( g_conf, "grid.icon_offset_x" ); + c -> icon_offset_y = pnd_conf_get_as_int ( g_conf, "grid.icon_offset_y" ); + c -> icon_max_width = pnd_conf_get_as_int ( g_conf, "grid.icon_max_width" ); + c -> icon_max_height = pnd_conf_get_as_int ( g_conf, "grid.icon_max_height" ); + c -> sel_icon_offset_x = pnd_conf_get_as_int_d ( g_conf, "grid.sel_offoffset_x", 0 ); + c -> sel_icon_offset_y = pnd_conf_get_as_int_d ( g_conf, "grid.sel_offoffset_y", 0 ); + + c -> text_width = pnd_conf_get_as_int ( g_conf, "grid.text_width" ); + c -> text_clip_x = pnd_conf_get_as_int ( g_conf, "grid.text_clip_x" ); + c -> text_offset_x = pnd_conf_get_as_int ( g_conf, "grid.text_offset_x" ); + c -> text_offset_y = pnd_conf_get_as_int ( g_conf, "grid.text_offset_y" ); + c -> text_hilite_offset_y = pnd_conf_get_as_int ( g_conf, "grid.text_hilite_offset_y" ); + + c -> row_max = pnd_conf_get_as_int_d ( g_conf, "grid.row_max", 4 ); + c -> col_max = pnd_conf_get_as_int_d ( g_conf, "grid.col_max", 5 ); + + c -> cell_width = pnd_conf_get_as_int ( g_conf, "grid.cell_width" ); + c -> cell_height = pnd_conf_get_as_int ( g_conf, "grid.cell_height" ); + + c -> arrow_bar_x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_x", 450 ); + c -> arrow_bar_y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_y", 100 ); + c -> arrow_bar_clip_w = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_w", 10 ); + c -> arrow_bar_clip_h = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_clip_h", 100 ); + c -> arrow_up_x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_x", 450 ); + c -> arrow_up_y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_y", 80 ); + c -> arrow_down_x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_x", 450 ); + c -> arrow_down_y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_y", 80 ); + + // font colour + 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 ) { + // if detail panel is hidden, and theme cannot support it, unhide the bloody thing. (This may help + // when someone is amid theme hacking or changing.) + if ( ! ui_is_detail_hideable() ) { + ui_detail_hidden = 0; + } + + // still hidden? + if ( ui_detail_hidden ) { + + c -> row_max = pnd_conf_get_as_int_d ( g_conf, "grid.row_max_w", c -> row_max ); + c -> col_max = pnd_conf_get_as_int_d ( g_conf, "grid.col_max_w", c -> col_max ); + + c -> cell_width = pnd_conf_get_as_int_d ( g_conf, "grid.cell_width_w", c -> cell_width ); + c -> cell_height = pnd_conf_get_as_int_d ( g_conf, "grid.cell_height_w", c -> cell_height ); + + c -> arrow_bar_x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_x_w", 450 ); + c -> arrow_bar_y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_bar_y_w", 100 ); + c -> arrow_up_x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_x_w", 450 ); + c -> arrow_up_y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_up_y_w", 80 ); + c -> arrow_down_x = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_x_w", 450 ); + c -> arrow_down_y = pnd_conf_get_as_int_d ( g_conf, "grid.arrow_down_y_w", 80 ); + + } // if detail hidden.. still. + + } // if detail hidden + + return; +} + +unsigned char ui_is_detail_hideable ( void ) { + + // if skin has a bit of info for wide-mode, we assume wide-mode is available + if ( pnd_conf_get_as_char ( g_conf, "grid.row_max_w" ) != NULL ) { + return ( 1 ); + } + + // else not! + return ( 0 ); +} + +void ui_toggle_detail_pane ( void ) { + + // no bitmask trickery here; I like it to be stand-out obvious at 3am. + + if ( ui_detail_hidden ) { + ui_detail_hidden = 0; + } else { + ui_detail_hidden = 1; + } + + // repull skin config + ui_recache_context ( &ui_display_context ); + + // redraw + render_mask |= CHANGED_EVERYTHING; + + return; +} + +void ui_menu_context ( mm_appref_t *a ) { + + unsigned char rescan_apps = 0; + unsigned char context_alive = 1; + + enum { + context_done = 0, + context_file_info, + context_file_delete, + context_app_info, + context_app_hide, + context_app_recategorize, + context_app_recategorize_sub, + context_app_rename, + context_app_cpuspeed, + context_app_run, + context_app_notes1, + context_app_notes2, + context_app_notes3, + context_menu_max + }; + + char *verbiage[] = { + "Done (return to grid)", // context_done + "Get info about file/dir", // context_file_info + "Delete file/dir", // context_file_delete + "Get info", // context_app_info + "Hide application", // hide + "Recategorize", // recategorize + "Recategorize subcategory", // recategorize + "Change displayed title", // rename + "Set CPU speed for launch", // cpuspeed + "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 ]; + char *menustring [ context_menu_max ]; + unsigned char menumax = 0; + + // ++ done + menu [ menumax ] = context_done; menustring [ menumax++ ] = verbiage [ context_done ]; + + // hook up appropriate menu options based on tab-type and object-type + if ( g_categories [ ui_category ] -> fspath ) { + return; // TBD + 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 ]; + menu [ menumax ] = context_app_recategorize; menustring [ menumax++ ] = verbiage [ context_app_recategorize ]; + 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 + while ( context_alive ) { + + int sel = ui_modal_single_menu ( menustring, menumax, a -> ref -> title_en ? a -> ref -> title_en : "Quickpick Menu" /* title */, "B or Enter; other to cancel." /* footer */ ); + + if ( sel < 0 ) { + context_alive = 0; + + } else { + + switch ( menu [ sel ] ) { + + case context_done: + context_alive = 0; + break; + + case context_file_info: + break; + + case context_file_delete: + //ui_menu_twoby ( "Delete - Are you sure?", "B/enter; other to cancel", "Delete", "Do not delete" ); + break; + + case context_app_info: + break; + + case context_app_hide: + { + // determine key + char confkey [ 1000 ]; + snprintf ( confkey, 999, "%s.%s", "appshow", a -> ref -> unique_id ); + + // turn app 'off' + pnd_conf_set_char ( g_conf, confkey, "0" ); + + // 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++; + } + + context_alive = 0; // nolonger visible, so lets just get out + + } + + break; + + case context_app_recategorize: + { + char *opts [ 250 ]; + unsigned char optmax = 0; + unsigned char i; + + // 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 ) { + break; + } + + if ( ! freedesktop_complete [ i ].parent_cat ) { + opts [ optmax++ ] = freedesktop_complete [ i ].cat; + } + + i++; + } // while + + // picker + char prompt [ 101 ]; + snprintf ( prompt, 100, "Pick category [%s]", a -> ref -> main_category ? a -> ref -> main_category : "NoParentCategory" ); + + int sel = ui_modal_single_menu ( opts, optmax, prompt /*"Select parent category"*/, "Enter to select; other to skip." ); + + if ( sel >= 0 ) { + char confirm [ 1001 ]; + snprintf ( confirm, 1000, "Confirm: %s", opts [ sel ] ); + + 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; + + case context_app_recategorize_sub: + { + char *opts [ 250 ]; + unsigned char optmax = 0; + 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; + } + + // 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 ) && + ( strcasecmp ( freedesktop_complete [ i ].parent_cat, whichparentarewe ) == 0 ) + ) + { + opts [ optmax++ ] = freedesktop_complete [ i ].cat; + } + + i++; + } // while + + char prompt [ 101 ]; + //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." ); + + if ( sel >= 0 ) { + char confirm [ 1001 ]; + snprintf ( confirm, 1000, "Confirm: %s", opts [ sel ] ); + + if ( ui_menu_twoby ( confirm, "B/enter; other to cancel", "Confirm sub-categorization", "Do not set sub-category" ) == 1 ) { + ovr_replace_or_add ( a, "maincategorysub1", opts [ sel ] ); + rescan_apps++; + } + + } + + if ( mmcustom_is_ready() ) { + mmcustom_shutdown(); + } + + } + break; + + case context_app_rename: + { + char namebuf [ 101 ]; + unsigned char changed; + + changed = ui_menu_get_text_line ( "Rename application", "Use keyboard; Enter when done.", + a -> ref -> title_en ? a -> ref -> title_en : "blank", namebuf, 30, 0 /* alphanumeric */ ); + + if ( changed ) { + char confirm [ 1001 ]; + snprintf ( confirm, 1000, "Confirm: %s", namebuf ); + + if ( ui_menu_twoby ( confirm, "B/enter; other to cancel", "Confirm rename", "Do not rename" ) == 1 ) { + ovr_replace_or_add ( a, "title", namebuf ); + rescan_apps++; + } + + } + + } + + break; + + case context_app_cpuspeed: + { + char namebuf [ 101 ]; + unsigned char changed; + + changed = ui_menu_get_text_line ( "Specify runspeed", "Use keyboard; Enter when done.", + a -> ref -> clockspeed ? a -> ref -> clockspeed : "500", namebuf, 6, 1 /* numeric */ ); + + if ( changed ) { + char confirm [ 1001 ]; + snprintf ( confirm, 1000, "Confirm: %s", namebuf ); + + if ( ui_menu_twoby ( confirm, "B/enter; other to cancel", "Confirm clockspeed", "Do not set" ) == 1 ) { + ovr_replace_or_add ( a, "clockspeed", namebuf ); + rescan_apps++; + } + + } + + } + + break; + + case context_app_run: + 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; + + } // switch + + } // if useful return + + } // menu is alive? + + // rescan apps? + if ( rescan_apps ) { + applications_free(); + applications_scan(); + } + + 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; + opts [ 1 ] = two; + int sel = ui_modal_single_menu ( opts, 2, title, footer ); + if ( sel < 0 ) { + return ( 0 ); + } + return ( sel + 1 ); +} + +unsigned char ui_menu_get_text_line ( char *title, char *footer, char *initialvalue, + char *r_buffer, unsigned char maxlen, unsigned char numbersonlyp ) +{ + SDL_Rect rects [ 40 ]; + SDL_Rect *dest = rects; + SDL_Rect src; + SDL_Surface *rtext; + + char hacktext [ 1024 ]; + unsigned char shifted = 0; + + bzero ( rects, sizeof(SDL_Rect) * 40 ); + + if ( initialvalue ) { + if ( initialvalue == r_buffer ) { + // already good to go + } else { + strncpy ( r_buffer, initialvalue, maxlen ); + } + } else { + bzero ( r_buffer, maxlen ); + } + + while ( 1 ) { + + // clear + dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ); + dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ); + dest -> w = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> w; + dest -> h = ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h; + SDL_FillRect( sdl_realscreen, dest, 0 ); + + // show dialog background + if ( g_imagecache [ IMG_DETAIL_BG ].i ) { + src.x = 0; + src.y = 0; + src.w = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_BG ].i)) -> w; + src.h = ((SDL_Surface*)(g_imagecache [ IMG_DETAIL_BG ].i)) -> h; + dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ); + dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ); + SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_BG ].i, &src, sdl_realscreen, dest ); + dest++; + } + + // show dialog frame + if ( g_imagecache [ IMG_DETAIL_PANEL ].i ) { + dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ); + dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ); + SDL_BlitSurface ( g_imagecache [ IMG_DETAIL_PANEL ].i, NULL /* whole image */, sdl_realscreen, dest ); + dest++; + } + + // show header + if ( title ) { + rtext = TTF_RenderText_Blended ( g_tab_font, title, ui_display_context.fontcolor ); + dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20; + dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 20; + SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest ); + SDL_FreeSurface ( rtext ); + dest++; + } + + // show footer + if ( footer ) { + rtext = TTF_RenderText_Blended ( g_tab_font, footer, ui_display_context.fontcolor ); + dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20; + dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + + ((SDL_Surface*) g_imagecache [ IMG_DETAIL_PANEL ].i) -> h + - 60; + SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest ); + SDL_FreeSurface ( rtext ); + dest++; + } + + // show text line - and embed cursor + dest -> x = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_x", 460 ) + 20; + dest -> y = pnd_conf_get_as_int_d ( g_conf, "detailpane.pane_offset_y", 60 ) + 40 + ( 20 * ( 0/*i*/ + 1 - 0/*first_visible*/ ) ); + + strncpy ( hacktext, r_buffer, 1000 ); + strncat ( hacktext, "\n", 1000 ); // add [] in most fonts + + rtext = TTF_RenderText_Blended ( g_tab_font, hacktext, ui_display_context.fontcolor ); + SDL_BlitSurface ( rtext, NULL /* full src */, sdl_realscreen, dest ); + SDL_FreeSurface ( rtext ); + dest++; + + // update all the rects and send it all to sdl + SDL_UpdateRects ( sdl_realscreen, dest - rects, rects ); + dest = rects; + + // check for input + SDL_Event event; + while ( SDL_WaitEvent ( &event ) ) { + + switch ( event.type ) { + + case SDL_KEYUP: + if ( event.key.keysym.sym == SDLK_LSHIFT || event.key.keysym.sym == SDLK_RSHIFT ) { + shifted = 0; + } + break; + + case SDL_KEYDOWN: + + if ( event.key.keysym.sym == SDLK_LEFT || event.key.keysym.sym == SDLK_BACKSPACE ) { + if ( strlen ( r_buffer ) > 0 ) { + 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" + // 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; + + } else if ( event.key.keysym.sym == SDLK_ESCAPE || + event.key.keysym.sym == SDLK_PAGEUP || + event.key.keysym.sym == SDLK_PAGEDOWN || + event.key.keysym.sym == SDLK_HOME + ) + { + return ( 0 ); + + } else { + + if ( isprint(event.key.keysym.sym) ) { + + unsigned char good = 1; + + if ( numbersonlyp && ( ! isdigit(event.key.keysym.sym) ) ) { + good = 0; + } + + if ( maxlen && strlen(r_buffer) >= maxlen ) { + good = 0; + } + + if ( good ) { + char b [ 2 ] = { '\0', '\0' }; + if ( shifted ) { + b [ 0 ] = toupper ( event.key.keysym.sym ); + } else { + b [ 0 ] = event.key.keysym.sym; + } + strncat ( r_buffer, b, maxlen ); + } // good? + + } // printable? + + } + + break; + + } // switch + + break; + + } // 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 ); + + char fullpath [ PATH_MAX ]; + + sprintf ( fullpath, "%s/%s", a -> ref -> object_path, a -> ref -> object_filename ); + char *dot = strrchr ( fullpath, '.' ); + if ( dot ) { + sprintf ( dot, PXML_SAMEPATH_OVERRIDE_FILEEXT ); + } else { + pnd_log ( pndn_error, "ERROR: Bad pnd-path in disco_t! %s\n", fullpath ); + return ( 0 ); + } + + struct stat statbuf; + + if ( stat ( fullpath, &statbuf ) == 0 ) { + // file exists + pnd_conf_handle h; + + h = pnd_conf_fetch_by_path ( fullpath ); + + if ( ! h ) { + return ( 0 ); // fail! + } + + char key [ 101 ]; + snprintf ( key, 100, "Application-%u.%s", a -> ref -> subapp_number, keybase ); + + pnd_conf_set_char ( h, key, newvalue ); + + return ( pnd_conf_write ( h, fullpath ) ); + + } else { + // file needs to be created - easy! + + FILE *f = fopen ( fullpath, "w" ); + + if ( f ) { + fprintf ( f, "Application-%u.%s\t%s\n", a -> ref -> subapp_number, keybase, newvalue ); + fclose ( f ); + + } else { + return ( 0 ); // fail! + } + + } // new or used? + + 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; }