minimenu:
authorskeezix <skeezix@flotsam-vm.(none)>
Mon, 5 Apr 2010 20:38:08 +0000 (16:38 -0400)
committerskeezix <skeezix@flotsam-vm.(none)>
Mon, 5 Apr 2010 20:38:08 +0000 (16:38 -0400)
- mmcatmap.conf may be stuck into /pandora/mmenu on SD cards, in order to specify category mapping conf stuff (or random conf overrides)
- now includes a directory browser/launcher
- default is on (shows a tab for each non-empty dir in /media)
- could just make one tab for /media .. *shrug*
- may be disabled
- may specify all sorts of tabs to open by using a searchpath
- can run pnd-files (will work in the normal way)
- can run non-pnd files (will set CWD and run them without exiting minimenu)
- can browse up and down the dir tree

12 files changed:
deployment/etc/pandora/conf/mmenu.conf
include/pnd_discovery.h
lib/pnd_discovery.c
minimenu/mmcache.c
minimenu/mmcat.c
minimenu/mmcat.h
minimenu/mmcatmap.conf [new file with mode: 0644]
minimenu/mmenu.c
minimenu/mmenu.conf
minimenu/mmui.c
minimenu/mmui.h
minimenu/skin/default/mmskin.conf

index b5aa584..d4165ff 100644 (file)
@@ -58,19 +58,14 @@ cache_path          /pandora/appdata/mmenu.pvwcache              # where to write cached ima
 cache_findpath         /media/mmcblk[12]p?/pandora/appdata/mmenu.pvwcache           # where to look for cached preview pics
 
 [categories]
-do_all_cat             1               # if >0, will show an All category; if 0, skip it, just your cats.
-# Normally for mmenu, an encountered category is just used as is. 5 cats exist, you get 5 tabs.
-# If map_on is >0, then category transforms will occur
-#   @NEWCAT oldcat1:oldcat2     <- means oldcat1, if found, will map to NEWCAT. "@" is discarded.
-#   NOTE: FreeDesktop rules do not allow categories with spaces in the name; if needed, I can add it with quoting.
-# If map_default_on is set (>0), then any unmapped categories will be forced into the default category bucket (map_default_cat.)
-#   If map_default_on is off (=0), then unmapped categories will become their own categories as normal.
-#   Should probably still have an @ line to create the default category, since creating the cats comes before assigning defaults
-# NOTE: Individual app overrides occur at the time of app scanning, so before this category mapping occurs and thus is effected
-map_on                 0               # if >0, will do category mapping at all; if 0, don't do any of this.
-map_default_on         0               # if >0, any unmapped category will get forced to map_default_cat; set to 0 to leave unmapped cats alone
-map_default_cat                Spam            # see map_default_on
-# NOTE: List the categories in reverse order to how you wish them in the tab list; last one shows up as first tab
-@Woogle                        Audio
-@Jimmy                 Game
-@Spam
+catmap_searchpath      /media/*/pandora/mmenu:/etc/pandora/mmenu:./minimenu
+catmap_confname                mmcatmap.conf
+do_all_cat             1       # if >0, will show an All category; if 0, skip it, just your cats.
+
+[filesystem]
+do_browser             1       # if >0, will allow filesystem browsing somehow
+tab_searchpaths                /media/*        # for each chunk in searchpath, show a tab (if not empty).
+# example:
+# /media -> show /media as a single tab
+# /media/* -> show a tab for each dir in /media
+# /media/mmcblk1p1:/media/mmcblk2p1 -> show 2 tabs, one for first partition on each SD
index 5f8734f..f5dae7a 100644 (file)
@@ -16,6 +16,7 @@ extern "C" {
  * overridespath may be NULL if you do not wish to search for pxml overrides
  */
 pnd_box_handle pnd_disco_search ( char *searchpath, char *overridespath );
+pnd_box_handle pnd_disco_file ( char *path, char *filename ); // should you wish to 'discover' one .pnd-file
 
 /* pnd_disco_t describes a given entry found by the discovery code; ie: the containers key is the path to
  * the PXML file (since this is relatively unique), with the fields below detailing the executable path,
@@ -44,6 +45,7 @@ typedef enum {
 // NOTE: We really need to rework disco-t so it can include non-english titles/desc; perhaps more info as optional,
 //   or a name/value pairing system so it can have extra data in it, without a complex structure.
 #define PND_DISCO_FLAG_OVR 1   // An ovr file was found for this app (not per subapp, just per .pnd)
+#define PND_DISCO_GENERATED 2  // This disco is 'faux', made up and not reflecting a real 'pnd file'
 typedef struct {
   // base
   unsigned char object_type;   // see enum above
index 1e41ead..07cde30 100644 (file)
@@ -420,3 +420,34 @@ pnd_box_handle pnd_disco_search ( char *searchpath, char *overridespath ) {
 
   return ( disco_box );
 }
+
+pnd_box_handle pnd_disco_file ( char *path, char *filename ) {
+  struct stat statbuf;
+
+  // set up container
+  disco_overrides = NULL;
+  disco_box = pnd_box_new ( "discovery" );
+
+  // path
+  char fullpath [ PATH_MAX ];
+  sprintf ( fullpath, "%s/%s", path, filename );
+
+  // fake it
+  if ( stat ( fullpath, &statbuf ) < 0 ) {
+    return ( 0 );
+  }
+
+  struct FTW ftw;
+  ftw.base = strlen ( path );
+  ftw.level = 0;
+
+  pnd_disco_callback ( fullpath, &statbuf, FTW_F, &ftw );
+
+  // return whatever we found, or NULL if nada
+  if ( ! pnd_box_get_head ( disco_box ) ) {
+    pnd_box_delete ( disco_box );
+    disco_box = NULL;
+  }
+
+  return ( disco_box );
+}
index d8ba65e..c5682f2 100644 (file)
@@ -239,10 +239,15 @@ unsigned char cache_icon ( pnd_disco_t *app, unsigned char maxwidth, unsigned ch
     } // stat
   } // ovr?
 
-  // pull icon into buffer from .pnd
-  if ( ! iconbuf ) {
-    iconbuf = pnd_emit_icon_to_buffer ( app, &buflen );
-  }
+  // if this is a real pnd file (dir-app or pnd-file-app), then try to pull icon from there
+  if ( ! ( app -> object_flags & PND_DISCO_GENERATED ) ) {
+
+    // pull icon into buffer from .pnd if not already found an icon
+    if ( ! iconbuf ) {
+      iconbuf = pnd_emit_icon_to_buffer ( app, &buflen );
+    }
+
+  } // generated?
 
   if ( ! iconbuf ) {
     return ( 0 );
index 1c666ee..db8d986 100644 (file)
@@ -2,6 +2,10 @@
 #include <string.h>
 #include <strings.h>
 #include <stdlib.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #include "pnd_conf.h"
 #include "pnd_logger.h"
@@ -22,7 +26,7 @@ unsigned char g_catmapcount = 0;
 
 extern pnd_conf_handle g_conf;
 
-unsigned char category_push ( char *catname, pnd_disco_t *app, pnd_conf_handle ovrh ) {
+unsigned char category_push ( char *catname, pnd_disco_t *app, pnd_conf_handle ovrh, char *fspath ) {
   mm_category_t *c;
 
   // check category list; if found, append app to the end of it.
@@ -42,6 +46,11 @@ unsigned char category_push ( char *catname, pnd_disco_t *app, pnd_conf_handle o
     g_categories [ g_categorycount ].catname = strdup ( catname );
     g_categories [ g_categorycount ].refs = NULL;
     c = &(g_categories [ g_categorycount ]);
+
+    if ( fspath ) {
+      g_categories [ g_categorycount ].fspath = strdup ( fspath );;
+    }
+
     g_categorycount++;
   }
 
@@ -75,7 +84,7 @@ unsigned char category_push ( char *catname, pnd_disco_t *app, pnd_conf_handle o
     while ( iter ) {
 
       if ( iter -> ref -> title_en ) {
-       if ( strcmp ( ar -> ref -> title_en, iter -> ref -> title_en ) < 0 ) {
+       if ( cat_sort_score ( ar, iter ) < 0 ) {
          // new guy is smaller than the current guy!
          break;
        }
@@ -126,6 +135,41 @@ mm_category_t *category_query ( char *catname ) {
   return ( NULL );
 }
 
+int cat_sort_score ( mm_appref_t *s1, mm_appref_t *s2 ) {
+
+  extern mm_category_t g_categories [ MAX_CATS ];
+  extern unsigned char g_categorycount;
+  extern unsigned char ui_category;
+
+  // are we in a directory browser, or looking at pnd-files?
+  if ( g_categories [ ui_category ].fspath ) {
+
+    if ( s1 == s2 ) {
+      return ( 0 ); // equal
+
+    } else if ( s1 -> ref -> object_type == pnd_object_type_directory &&
+               s2 -> ref -> object_type == pnd_object_type_directory )
+    {
+      // both are directories, be nice
+      return ( strcmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
+    } else if ( s1 -> ref -> object_type == pnd_object_type_directory &&
+               s2 -> ref -> object_type != pnd_object_type_directory )
+    {
+      return ( -1 ); // dir on the left is earlier than file on the right
+    } else if ( s1 -> ref -> object_type != pnd_object_type_directory &&
+               s2 -> ref -> object_type == pnd_object_type_directory )
+    {
+      return ( 1 ); // dir on the right is earlier than file on the left
+    } else {
+      // file on file
+      return ( strcmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
+    }
+
+  }
+
+  return ( strcmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
+}
+
 void category_dump ( void ) {
   unsigned int i;
 
@@ -190,7 +234,7 @@ unsigned char category_map_setup ( void ) {
       {
        //pnd_log ( pndn_debug, "target(%s) from(%s)\n", k, buffer );
 
-       category_push ( k, NULL, 0 );
+       category_push ( k, NULL, 0, NULL /* fspath */ );
        g_catmaps [ g_catmapcount ].target = category_query ( k );
        g_catmaps [ g_catmapcount ].from = strdup ( buffer );
        g_catmapcount++;
@@ -228,19 +272,123 @@ unsigned char category_meta_push ( char *catname, pnd_disco_t *app, pnd_conf_han
     cat = category_map_query ( catname );
 
     if ( cat ) {
-      return ( category_push ( cat -> catname, app, ovrh ) );
+      return ( category_push ( cat -> catname, app, ovrh, NULL /* fspath */ ) );
     }
 
     // not mapped.. but default?
     if ( pnd_conf_get_as_int_d ( g_conf, "categories.map_default_on", 0 ) ) {
       char *def = pnd_conf_get_as_char ( g_conf, "categories.map_default_cat" );
       if ( def ) {
-       return ( category_push ( def, app, ovrh ) );
+       return ( category_push ( def, app, ovrh, NULL /* fspath */ ) );
       }
     }
 
   } // cat map is desired?
 
   // not default, just do it
-  return ( category_push ( catname, app, ovrh ) );
+  return ( category_push ( catname, app, ovrh, NULL /* fspath */ ) );
+}
+
+unsigned char category_fs_restock ( mm_category_t *cat ) {
+
+  if ( ! cat -> fspath ) {
+    return ( 1 ); // not a filesystem browser tab
+  }
+
+  // clear any existing baggage
+  //
+
+  // apprefs
+  mm_appref_t *iter = cat -> refs, *next;
+  while ( iter ) {
+    next = iter -> next;
+    free ( iter );
+    iter = next;
+  }
+  cat -> refs = NULL;
+
+  // discos
+  if ( cat -> disco ) {
+    pnd_disco_t *p = pnd_box_get_head ( cat -> disco );
+    pnd_disco_t *n;
+    while ( p ) {
+      n = pnd_box_get_next ( p );
+      pnd_disco_destroy ( p );
+      p = n;
+    }
+    pnd_box_delete ( cat -> disco );
+  }
+
+  // rescan the filesystem
+  //
+
+  //pnd_log ( pndn_debug, "Restocking cat %s with path %s\n", cat -> catname, cat -> fspath );
+  DIR *d;
+
+  if ( ( d = opendir ( cat -> fspath ) ) ) {
+    struct dirent *de = readdir ( d );
+
+    pnd_disco_t *disco;
+    char uid [ 100 ];
+
+    cat -> disco = pnd_box_new ( cat -> catname );
+
+    while ( de ) {
+
+      struct stat buffy;
+      char fullpath [ PATH_MAX ];
+      sprintf ( fullpath, "%s/%s", cat -> fspath, de -> d_name );
+      int statret = stat ( fullpath, &buffy );
+
+      // if file is executable somehow or another
+      if ( statret == 0 &&
+          buffy.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)
+        )
+      {
+       // determine unique-id
+       sprintf ( uid, "%d", (int) de -> d_ino );
+       disco = NULL;
+
+       switch ( de -> d_type ) {
+
+       case DT_DIR:
+         if ( strcmp ( de -> d_name, "." ) == 0 ) {
+           // ignore ".", but ".." is fine
+         } else {
+           disco = pnd_box_allocinsert ( cat -> disco, uid, sizeof(pnd_disco_t) );
+           disco -> object_type = pnd_object_type_directory; // suggest to Grid that its a dir
+         }
+         break;
+       case DT_UNKNOWN:
+       case DT_REG:
+         disco = pnd_box_allocinsert ( cat -> disco, uid, sizeof(pnd_disco_t) );
+         disco -> object_type = pnd_object_type_unknown; // suggest to Grid that its a file
+         break;
+
+       } // switch
+
+       // found a directory or executable?
+       if ( disco ) {
+         // register with this category
+         disco -> unique_id = strdup ( uid );
+         disco -> title_en = strdup ( de -> d_name );
+         disco -> object_flags = PND_DISCO_GENERATED;
+         disco -> object_path = strdup ( cat -> fspath );
+         disco -> object_filename = strdup ( de -> d_name );
+         category_push ( cat -> catname, disco, 0, NULL /* fspath already set */ );
+         // if a override icon exists, cache it up
+         cache_icon ( disco, pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_width", 50 ),
+                      pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_height", 50 ) );
+       }
+
+      } // stat
+
+      // next
+      de = readdir ( d );
+    }
+
+    closedir ( d );
+  }
+
+  return ( 1 );
 }
index 0c679cb..db3c638 100644 (file)
@@ -11,8 +11,12 @@ typedef struct _mm_appref_t {
 
 typedef struct {
   char *catname;          // name of the category
+  // current applications
   mm_appref_t *refs;      // apps (from g_active_apps) that are in this category
   unsigned int refcount;  // how many apps in this category
+  // if a directory browser category, additional info is needed
+  char *fspath;           // NULL if a pnd-category (not a filesystem category)
+  pnd_box_handle disco;   // faux-applications generated from filesystem
 } mm_category_t;
 
 #define MAX_CATS 100
@@ -20,10 +24,11 @@ typedef struct {
 #define CATEGORY_ALL "All"
 
 // try to populate as many cats as necessary
-unsigned char category_push ( char *catname, pnd_disco_t *app, pnd_conf_handle ovrh ); // catname is not pulled from app, so we can make them up on the fly (ie: "All")
+unsigned char category_push ( char *catname, pnd_disco_t *app, pnd_conf_handle ovrh, char *fspath ); // catname is not pulled from app, so we can make them up on the fly (ie: "All");
 mm_category_t *category_query ( char *catname );
 void category_dump ( void ); // sort the apprefs
 void category_freeall ( void );
+int cat_sort_score ( mm_appref_t *s1, mm_appref_t *s2 ); // like strcmp, but used to sort apps by title
 
 // category mapping hack
 typedef struct {
@@ -35,4 +40,7 @@ unsigned char category_map_setup ( void ); // set up the mappings
 mm_category_t *category_map_query ( char *cat );
 unsigned char category_meta_push ( char *catname, pnd_disco_t *app, pnd_conf_handle ovrh );
 
+// filesystem browser
+unsigned char category_fs_restock ( mm_category_t *cat );
+
 #endif
diff --git a/minimenu/mmcatmap.conf b/minimenu/mmcatmap.conf
new file mode 100644 (file)
index 0000000..416bef6
--- /dev/null
@@ -0,0 +1,21 @@
+
+#
+# mmcatmap.conf -- allows merging or renaming or mapping of categories from one name to another, for minimenu.
+#
+
+[categories]
+# Normally for mmenu, an encountered category is just used as is. 5 cats exist, you get 5 tabs.
+# If map_on is >0, then category transforms will occur
+#   @NEWCAT oldcat1:oldcat2     <- means oldcat1, if found, will map to NEWCAT. "@" is discarded.
+#   NOTE: FreeDesktop rules do not allow categories with spaces in the name; if needed, I can add it with quoting.
+# If map_default_on is set (>0), then any unmapped categories will be forced into the default category bucket (map_default_cat.)
+#   If map_default_on is off (=0), then unmapped categories will become their own categories as normal.
+#   Should probably still have an @ line to create the default category, since creating the cats comes before assigning defaults
+# NOTE: Individual app overrides occur at the time of app scanning, so before this category mapping occurs and thus is effected
+map_on                 0               # if >0, will do category mapping at all; if 0, don't do any of this.
+map_default_on         0               # if >0, any unmapped category will get forced to map_default_cat; set to 0 to leave unmapped cats alone
+map_default_cat                Spam            # see map_default_on
+# NOTE: List the categories in reverse order to how you wish them in the tab list; last one shows up as first tab
+@Woogle                        Audio
+@Jimmy                 Game
+@Spam
index d0f1280..95a8b4b 100644 (file)
@@ -35,6 +35,7 @@
 #include <strings.h>
 #include <ctype.h>
 #include <sys/wait.h>
+#include <dirent.h>
 
 #include "pnd_logger.h"
 #include "pnd_pxml.h"
@@ -45,6 +46,7 @@
 #include "pnd_locate.h"
 #include "pnd_device.h"
 #include "pnd_pndfiles.h"
+#include "../lib/pnd_pathiter.h"
 
 #include "mmenu.h"
 #include "mmwrapcmd.h"
@@ -171,6 +173,25 @@ int main ( int argc, char *argv[] ) {
     emit_and_quit ( MM_QUIT );
   }
 
+  /* category conf file
+   */
+  {
+    char *locater = pnd_locate_filename ( pnd_conf_get_as_char ( g_conf, "categories.catmap_searchpath" ),
+                                         pnd_conf_get_as_char ( g_conf, "categories.catmap_confname" ) );
+
+    if ( locater ) {
+      pnd_log ( pndn_rem, "Found category conf at '%s'\n", locater );
+      pnd_conf_handle h = pnd_conf_fetch_by_path ( locater );
+      if ( h ) {
+       // lets just merge the skin conf onto the regular conf, so it just magicly works
+       pnd_box_append ( g_conf, h );
+      }
+    } else {
+      pnd_log ( pndn_debug, "No additional category conf file found.\n" );
+    }
+
+  } // cat conf
+
   // redo log filter
   pnd_log_set_filter ( pnd_conf_get_as_int_d ( g_conf, "minimenu.loglevel", pndn_error ) );
 
@@ -288,7 +309,7 @@ int main ( int argc, char *argv[] ) {
 
   // create all cat
   if ( pnd_conf_get_as_int_d ( g_conf, "categories.do_all_cat", 1 ) ) {
-    category_push ( g_x11_present ? CATEGORY_ALL "    (X11)" : CATEGORY_ALL "   (No X11)", NULL, 0 );
+    category_push ( g_x11_present ? CATEGORY_ALL "    (X11)" : CATEGORY_ALL "   (No X11)", NULL /*app*/, 0, NULL /* fspath */ );
   }
 
   // set up category mappings
@@ -325,6 +346,34 @@ void emit_and_quit ( char *s ) {
   exit ( 0 );
 }
 
+static unsigned int is_dir_empty ( char *fullpath ) {
+  DIR *d = opendir ( fullpath );
+
+  if ( ! d ) {
+    return ( 0 ); // not empty, since we don't know
+  }
+
+  struct dirent *de = readdir ( d );
+
+  while ( de ) {
+
+    if ( strcmp ( de -> d_name, "." ) == 0 ) {
+      // irrelevent
+    } else if ( strcmp ( de -> d_name, ".." ) == 0 ) {
+      // irrelevent
+    } else {
+      // something else came in, so dir must not be empty
+      return ( 0 ); 
+    }
+
+    de = readdir ( d );
+  }
+
+  closedir ( d );
+
+  return ( 1 ); // dir is empty
+}
+
 void applications_free ( void ) {
 
   // free up all our category apprefs, but keep the preview and icon cache's..
@@ -470,7 +519,7 @@ void applications_scan ( void ) {
       // push to All category
       // we do this first, so first category is always All
       if ( pnd_conf_get_as_int_d ( g_conf, "categories.do_all_cat", 1 ) ) {
-       if ( ! category_push ( g_x11_present ? CATEGORY_ALL "    (X11)" : CATEGORY_ALL "   (No X11)", iter, ovrh ) ) {
+       if ( ! category_push ( g_x11_present ? CATEGORY_ALL "    (X11)" : CATEGORY_ALL "   (No X11)", iter, ovrh, NULL /* fspath */ ) ) {
          pnd_log ( pndn_warning, "  Couldn't categorize to All: '%s'\n", IFNULL(iter -> title_en, "No Name") );
        }
       } // all?
@@ -520,6 +569,30 @@ void applications_scan ( void ) {
     itercount++;
   } // while
 
+  // set up filesystem browser tabs
+  if ( pnd_conf_get_as_int_d ( g_conf, "filesystem.do_browser", 0 ) ) {
+    char *searchpath = pnd_conf_get_as_char ( g_conf, "filesystem.tab_searchpaths" );
+
+    SEARCHPATH_PRE
+    {
+      char *c, *tabname;
+      c = strrchr ( buffer, '/' );
+      if ( c && (*(c+1)!='\0') ) {
+       tabname = c;
+      } else {
+       tabname = buffer;
+      }
+
+      // check if dir is empty; if so, skip it.
+      if ( ! is_dir_empty ( buffer ) ) {
+       category_push ( tabname /* tab name */, NULL /* app */, 0 /* override */, buffer /* fspath */ );
+      }
+
+    }
+    SEARCHPATH_POST
+
+  } // set up fs browser tabs
+
   // dump categories
   //category_dump();
 
index a4937d7..df6e06e 100644 (file)
@@ -58,19 +58,14 @@ cache_path          /pandora/appdata/mmenu.pvwcache              # where to write cached ima
 cache_findpath         /media/mmcblk[12]p?/pandora/appdata/mmenu.pvwcache           # where to look for cached preview pics
 
 [categories]
-do_all_cat             1               # if >0, will show an All category; if 0, skip it, just your cats.
-# Normally for mmenu, an encountered category is just used as is. 5 cats exist, you get 5 tabs.
-# If map_on is >0, then category transforms will occur
-#   @NEWCAT oldcat1:oldcat2     <- means oldcat1, if found, will map to NEWCAT. "@" is discarded.
-#   NOTE: FreeDesktop rules do not allow categories with spaces in the name; if needed, I can add it with quoting.
-# If map_default_on is set (>0), then any unmapped categories will be forced into the default category bucket (map_default_cat.)
-#   If map_default_on is off (=0), then unmapped categories will become their own categories as normal.
-#   Should probably still have an @ line to create the default category, since creating the cats comes before assigning defaults
-# NOTE: Individual app overrides occur at the time of app scanning, so before this category mapping occurs and thus is effected
-map_on                 0               # if >0, will do category mapping at all; if 0, don't do any of this.
-map_default_on         0               # if >0, any unmapped category will get forced to map_default_cat; set to 0 to leave unmapped cats alone
-map_default_cat                Spam            # see map_default_on
-# NOTE: List the categories in reverse order to how you wish them in the tab list; last one shows up as first tab
-@Woogle                        Audio
-@Jimmy                 Game
-@Spam
+catmap_searchpath      /media/*/pandora/mmenu:/etc/pandora/mmenu:./minimenu
+catmap_confname                mmcatmap.conf
+do_all_cat             1       # if >0, will show an All category; if 0, skip it, just your cats.
+
+[filesystem]
+do_browser             1       # if >0, will allow filesystem browsing somehow
+tab_searchpaths                /media/*        # for each chunk in searchpath, show a tab (if not empty).
+# example:
+# /media -> show /media as a single tab
+# /media/* -> show a tab for each dir in /media
+# /media/mmcblk1p1:/media/mmcblk2p1 -> show 2 tabs, one for first partition on each SD
index 954e50e..b922800 100644 (file)
@@ -1,9 +1,14 @@
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
+#include <stdio.h> /* for FILE etc */
+#include <stdlib.h> /* for malloc */
+#include <unistd.h> /* for unlink */
+#include <limits.h> /* for PATH_MAX */
+#include <sys/types.h>
+#include <sys/stat.h>
+#define __USE_GNU /* for strcasestr */
+#include <string.h> /* for making ftw.h happy */
 #include <time.h>
-#include <unistd.h>
+#include <ftw.h>
 #include "SDL.h"
 #include "SDL_audio.h"
 #include "SDL_image.h"
@@ -11,7 +16,6 @@
 #include "SDL_gfxPrimitives.h"
 #include "SDL_rotozoom.h"
 #include "SDL_thread.h"
-#include <sys/types.h>
 #include <dirent.h>
 
 #include "pnd_conf.h"
@@ -23,6 +27,7 @@
 #include "pnd_device.h"
 #include "../lib/pnd_pathiter.h"
 #include "pnd_utility.h"
+#include "pnd_pndfiles.h"
 
 #include "mmenu.h"
 #include "mmcat.h"
@@ -210,6 +215,8 @@ mm_imgcache_t g_imagecache [ IMG_TRUEMAX ] = {
   { IMG_ARROW_DOWN,           "graphics.IMG_ARROW_DOWN", },
   { IMG_ARROW_SCROLLBAR,      "graphics.IMG_ARROW_SCROLLBAR", },
   { IMG_HOURGLASS,            "graphics.IMG_HOURGLASS", },
+  { IMG_FOLDER,               "graphics.IMG_FOLDER", },
+  { IMG_EXECBIN,              "graphics.IMG_EXECBIN", },
   { IMG_MAX,                  NULL },
 };
 
@@ -365,8 +372,6 @@ void ui_render ( void ) {
     int topleft = col_max * ui_rows_scrolled_down;
     int botright = ( col_max * ( ui_rows_scrolled_down + row_max ) - 1 );
 
-    pnd_log ( PND_LOG_DEFAULT, "index %u tl %u br %u\n", index, topleft, botright );
-
     if ( index < topleft ) {
       ui_rows_scrolled_down -= pnd_conf_get_as_int_d ( g_conf, "grid.scroll_increment", 1 );
       render_jobs_b |= R_ALL;
@@ -411,7 +416,7 @@ void ui_render ( void ) {
   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_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" );
@@ -668,8 +673,22 @@ void ui_render ( void ) {
              iconsurface = ic -> i;
            } else {
              //pnd_log ( pndn_warning, "WARNING: TBD: Need Missin-icon icon for '%s'\n", IFNULL(appiter -> ref -> title_en,"No Name") );
-             iconsurface = g_imagecache [ IMG_ICON_MISSING ].i;
+
+             // 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") );
 
@@ -1511,7 +1530,88 @@ void ui_push_down ( void ) {
 
 void ui_push_exec ( void ) {
 
-  if ( ui_selected ) {
+  if ( ! ui_selected ) {
+    return;
+  }
+
+  // was this icon generated from filesystem, or from pnd-file?
+  if ( ui_selected -> ref -> object_flags & PND_DISCO_GENERATED ) {
+
+    if ( ! ui_selected -> ref -> title_en ) {
+      return; // no filename
+    }
+
+    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;
+
+       // 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' ) {
+         strcpy ( g_categories [ ui_category].fspath, "/" );
+       }
+
+      } 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 );
+
+      // 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 {
+      // just run it arbitrarily?
+
+      // if this a pnd-file, or just some executable?
+      if ( strcasestr ( ui_selected -> ref -> object_filename, PND_PACKAGE_FILEEXT ) ) {
+       // looks like a pnd, now what do we do..
+       pnd_box_handle h = pnd_disco_file ( ui_selected -> ref -> object_path, ui_selected -> ref -> object_filename );
+
+       if ( h ) {
+         pnd_disco_t *d = pnd_box_get_head ( h );
+         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 );
+       }
+
+      } else {
+       // random bin file
+#if 1
+       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 );
+       chdir ( cwd );
+#else
+       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 );
+#endif
+      } // pnd or bin?
+
+    } // dir or file?
+
+  } else {
     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() );
@@ -1526,9 +1626,11 @@ void ui_push_ltrigger ( void ) {
 
   if ( ui_category > 0 ) {
     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;
+      category_fs_restock ( &(g_categories [ ui_category ]) );
     }
   }
 
@@ -1558,9 +1660,11 @@ void ui_push_rtrigger ( void ) {
 
   if ( ui_category < ( g_categorycount - 1 ) ) {
     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;
+      category_fs_restock ( &(g_categories [ ui_category ]) );
     }
   }
 
index d662b50..6afb734 100644 (file)
@@ -32,6 +32,8 @@ typedef enum {
   IMG_ARROW_DOWN,
   IMG_ARROW_SCROLLBAR,
   IMG_HOURGLASS,
+  IMG_FOLDER,
+  IMG_EXECBIN,
   IMG_MAX, // before this point is loaded; after is generated
   IMG_TRUEMAX
 } mm_imgcache_e;
index 7e19220..796f465 100644 (file)
@@ -106,3 +106,5 @@ IMG_ARROW_UP                 arrowup.png
 IMG_ARROW_DOWN          arrowdown.png
 IMG_ARROW_SCROLLBAR     arrowscroller.png
 IMG_HOURGLASS           hourglass.png
+IMG_FOLDER              /usr/share/icons/gnome/32x32/places/folder.png # in dirbrowser mode
+IMG_EXECBIN             /usr/share/icons/gnome/32x32/categories/applications-other.png # in dirbrowser mode