X-Git-Url: https://git.openpandora.org/cgi-bin/gitweb.cgi?p=pandora-libraries.git;a=blobdiff_plain;f=minimenu%2Fmmui.c;h=aeb16134d548be49336eb0e832b628c00928979e;hp=add1cfbdde5ace4043bc1df6511e887244c03dcc;hb=e1ef286f894b1447d0777c30cbf2e9fffdd9c122;hpb=87cb80180edb9ed39ce606fb90d8ddb1b1956b1e diff --git a/minimenu/mmui.c b/minimenu/mmui.c index add1cfb..aeb1613 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,6 +30,8 @@ #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" @@ -36,6 +40,7 @@ #include "mmwrapcmd.h" #include "mmconf.h" #include "mmui_context.h" +#include "freedesktop_cats.h" #define CHANGED_NOTHING (0) #define CHANGED_CATEGORY (1<<0) /* changed to different category */ @@ -45,15 +50,19 @@ #define CHANGED_EVERYTHING (1<<4) /* redraw it all! */ unsigned int render_mask = CHANGED_EVERYTHING; +#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 @@ -79,11 +88,6 @@ ui_context_t ui_display_context; // display paramaters: see mmui_context. 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 static int ui_selected_index ( void ); @@ -342,15 +346,19 @@ void ui_render ( void ) { ui_context_t *c = &ui_display_context; // for convenience and shorthand // how many total rows do we need? - icon_rows = g_categories [ ui_category ].refcount / c -> col_max; - if ( g_categories [ ui_category ].refcount % c -> col_max > 0 ) { - icon_rows++; + if ( g_categorycount ) { + icon_rows = g_categories [ ui_category ] -> refcount / c -> col_max; + if ( g_categories [ ui_category ] -> refcount % c -> col_max > 0 ) { + icon_rows++; + } + } else { + icon_rows = 0; } #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 ) ) { - ui_selected = g_categories [ ui_category ].refs; + ui_selected = g_categories [ ui_category ] -> refs; } #endif @@ -516,7 +524,7 @@ void ui_render ( void ) { // draw text SDL_Surface *rtext; - rtext = TTF_RenderText_Blended ( g_tab_font, g_categories [ col ].catname, c -> fontcolor ); + 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; @@ -625,7 +633,7 @@ 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 ) ) { @@ -644,9 +652,9 @@ void ui_render ( void ) { } } - if ( g_categories [ ui_category ].refs ) { + if ( g_categories [ ui_category ] -> refs ) { - appiter = g_categories [ ui_category ].refs; + appiter = g_categories [ ui_category ] -> refs; row = 0; displayrow = 0; @@ -1028,14 +1036,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 ) { @@ -1043,11 +1051,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 ) { + 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; @@ -1092,7 +1112,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; @@ -1212,9 +1254,17 @@ 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 ) { // space or B + } else if ( event.key.keysym.sym == SDLK_END ) { // B ui_push_exec(); ui_event++; + } 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! @@ -1224,18 +1274,32 @@ void ui_process_input ( unsigned char block_p ) { ui_detail_hidden = 0; } ui_event++; - } else if ( event.key.keysym.sym == SDLK_RSHIFT ) { // left trigger + } 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 ) { // right trigger + } 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 ) { // 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(); @@ -1337,8 +1401,8 @@ 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 ); @@ -1368,12 +1432,10 @@ void ui_process_input ( unsigned char block_p ) { // 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_.... @@ -1441,12 +1503,12 @@ void ui_push_left ( unsigned char forcecoil ) { 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; @@ -1493,7 +1555,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 ); @@ -1519,7 +1581,7 @@ void ui_push_up ( void ) { 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; } @@ -1562,8 +1624,8 @@ 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++; } @@ -1574,7 +1636,7 @@ void ui_push_down ( void ) { 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; @@ -1601,6 +1663,52 @@ 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 ); + } + + } // dir or subcat? + + return; +} + void ui_push_exec ( void ) { if ( ! ui_selected ) { @@ -1615,40 +1723,57 @@ 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; + // 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(); - // 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; + } 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; + + } // directory browser or pnd subcat? } else { // just run it arbitrarily? @@ -1672,16 +1797,73 @@ void ui_push_exec ( void ) { } 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 ); + 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 { @@ -1730,7 +1912,7 @@ void ui_push_ltrigger ( void ) { 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; @@ -1738,7 +1920,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 ] ); } } @@ -1772,12 +1954,12 @@ void ui_push_rtrigger ( void ) { 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 ] ); } } @@ -1983,7 +2165,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 ) { @@ -2045,14 +2227,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; @@ -2093,7 +2268,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 ); @@ -2103,7 +2278,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 @@ -2120,7 +2295,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 ) ); @@ -2190,7 +2365,7 @@ 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++; @@ -2207,7 +2382,7 @@ unsigned char ui_determine_screen_row ( mm_appref_t *a ) { 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++; @@ -2367,6 +2542,8 @@ 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 ] ); } break; @@ -2396,6 +2573,44 @@ 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 ); + + // .. 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 ), @@ -2446,7 +2661,7 @@ void ui_post_scan ( void ) { // 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 ) { + if ( strcasecmp ( g_categories [ i ] -> catname, dc ) == 0 ) { ui_category = i; // ensure visibility unsigned int screen_width = ui_display_context.screen_width; @@ -2464,6 +2679,12 @@ void ui_post_scan ( void ) { } // default cat + // if we're sent right to a dirbrowser tab, restock it now (normally we restock on entry) + if ( g_categories [ ui_category ] -> fspath ) { + printf ( "Restock on start: '%s'\n", g_categories [ ui_category ] -> fspath ); + category_fs_restock ( g_categories [ ui_category ] ); + } + // redraw all render_mask |= CHANGED_EVERYTHING; @@ -2793,7 +3014,7 @@ void ui_aboutscreen ( char *textpath ) { SDL_Event e; - if ( SDL_PeepEvents ( &e, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_KEYUP|SDL_KEYDOWN) ) > 0 ) { + if ( SDL_PeepEvents ( &e, 1, SDL_GETEVENT, SDL_EVENTMASK(/*SDL_KEYUP|*/SDL_KEYDOWN) ) > 0 ) { return; } @@ -2824,41 +3045,52 @@ 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 ( 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--; - // switch to the new category - ui_category = g_categorycount - 1; + // 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.. with the cached name! + ui_category = category_index ( switch_to_name ); // ensure visibility unsigned int screen_width = ui_display_context.screen_width; @@ -2871,6 +3103,10 @@ void ui_revealscreen ( void ) { render_mask |= CHANGED_CATEGORY; } + for ( i = 0; i < g_categorycount; i++ ) { + free ( labels [ i ] ); + } + return; } @@ -2979,3 +3215,469 @@ void ui_toggle_detail_pane ( void ) { 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_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 + }; + + 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 { + //menu [ menumax ] = context_app_info; menustring [ menumax++ ] = verbiage [ context_app_info ]; + 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_run; menustring [ menumax++ ] = verbiage [ context_app_run ]; + } + + // 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 ) ); + + // 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; + + i = 0; + while ( 1 ) { + + if ( ! freedesktop_complete [ i ].cat ) { + break; + } + + if ( ! freedesktop_complete [ i ].parent_cat ) { + opts [ optmax++ ] = freedesktop_complete [ i ].cat; + } + + i++; + } // while + + 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++; + } + + } + + } + break; + + case context_app_recategorize_sub: + { + char *opts [ 250 ]; + unsigned char optmax = 0; + unsigned char i; + + i = 0; + while ( 1 ) { + + if ( ! freedesktop_complete [ i ].cat ) { + break; + } + + if ( freedesktop_complete [ i ].parent_cat ) { + opts [ optmax++ ] = freedesktop_complete [ i ].cat; + } + + i++; + } // while + + char prompt [ 101 ]; + snprintf ( prompt, 100, "Pick subcategory [%s]", a -> ref -> main_category1 ? a -> ref -> main_category1 : "NoSubcategory" ); + + 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++; + } + + } + + } + 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; + + default: + return; + + } // switch + + } // if useful return + + } // menu is alive? + + // rescan apps? + if ( rescan_apps ) { + applications_free(); + applications_scan(); + } + + return; +} + +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 ) { + 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 ) { + char *eol = strchr ( r_buffer, '\0' ); + *( eol - 1 ) = '\0'; + } else if ( event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_END ) { // return, or "B" + return ( 1 ); + + } 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 { + fprintf ( stderr, "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 ); +}