mmenu; nearly done support for custom categories; not quite done, but nearly.
authorskeezix <skeezix@flotsam-vm.(none)>
Wed, 9 Feb 2011 18:21:12 +0000 (13:21 -0500)
committerskeezix <skeezix@flotsam-vm.(none)>
Wed, 9 Feb 2011 18:21:12 +0000 (13:21 -0500)
Makefile
minimenu/mmcat.c
minimenu/mmcustom_cats.c [new file with mode: 0644]
minimenu/mmcustom_cats.h [new file with mode: 0644]
minimenu/mmui.c
minimenu/mmui.h

index 4220ac6..1f67e20 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -26,7 +26,7 @@ ALLOBJ = pnd_conf.o pnd_container.o pnd_discovery.o pnd_pxml.o pnd_notify.o pnd_
 all: ${SOLIB} ${LIB} conftest discotest evdevtest notifytest pndnotifyd rawpxmltest pndvalidator loggertest dbusnotifytest pnd_run pndevmapperd pnd_info evtest mmenu mmwrapper
 
 clean:
-       ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o evdevtest.o bin/evdevtest bin/discotest dbusnotifytest.o bin/dbusnotifytest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o pnd_info.o bin/pnd_info bin/pndevmapperd pndevmapperd.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/menu/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/bin/pnd_run deployment/usr/bin/pnd_info deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o deployment/usr/bin/pndevmapperd testdata/menuicons/* evtest.o bin/evtest bin/mmenu bin/mmwrapper mmenu.o mmwrapper.o deployment/usr/bin/mmenu deployment/usr/bin/mmwrapper mmcache.o mmui.o mmcat.o mmconf.o freedsktop_cats.o freedesktop_cats.o
+       ${RM} -f ${ALLOBJ} ${XMLOBJ} ${LIB} ${SOLIB1} locatetest.o bin/locatetest conftest.o bin/conftest discotest.o evdevtest.o bin/evdevtest bin/discotest dbusnotifytest.o bin/dbusnotifytest loggertest.o bin/loggertest bin/notifytest notifytest.o bin/rawpxmltest rawpxmltest.o bin/pnd_run pnd_run.o pnd_info.o bin/pnd_info bin/pndevmapperd pndevmapperd.o bin/pndnotifyd pndnotifyd.o ${SOLIB} testdata/dotdesktop/*.desktop testdata/menu/*.desktop testdata/apps/*.pnd testdata/dotdesktop/*.png deployment/usr/lib/libpnd* deployment/usr/bin/pndnotifyd deployment/usr/bin/pnd_run deployment/usr/bin/pnd_info deployment/usr/pandora/scripts/* deployment/etc/sudoers deployment/etc/init.d/pndnotifyd bin/pndvalidator pndvalidator.o deployment/usr/bin/pndevmapperd testdata/menuicons/* evtest.o bin/evtest bin/mmenu bin/mmwrapper mmenu.o mmwrapper.o deployment/usr/bin/mmenu deployment/usr/bin/mmwrapper mmcache.o mmui.o mmcat.o mmconf.o freedsktop_cats.o freedesktop_cats.o mmcustom_cats.o
        ${RM} -rf deployment/media deployment/etc/pandora/mmenu
        find . -name "*~*" -exec rm {} \; -print
 
@@ -60,8 +60,8 @@ pnd_info:     pnd_info.o ${SOLIB1}
 pndevmapperd:  pndevmapperd.o ${SOLIB1}
        ${CC} -lstdc++ -o bin/pndevmapperd pndevmapperd.o ${SOLIB1}
 
-mmenu: mmenu.o mmui.o mmcache.o mmcat.o mmconf.o freedesktop_cats.o ${SOLIB1}
-       ${CC} -lstdc++ -o bin/mmenu mmenu.o mmui.o mmcache.o mmcat.o mmconf.o freedesktop_cats.o ${SOLIB1} -L${PNDSTUFF}/usr/lib -lSDL -lSDL_image -lSDL_ttf -lSDL_gfx
+mmenu: mmenu.o mmui.o mmcache.o mmcat.o mmconf.o freedesktop_cats.o mmcustom_cats.o ${SOLIB1}
+       ${CC} -lstdc++ -o bin/mmenu mmenu.o mmui.o mmcache.o mmcat.o mmconf.o freedesktop_cats.o mmcustom_cats.o ${SOLIB1} -L${PNDSTUFF}/usr/lib -lSDL -lSDL_image -lSDL_ttf -lSDL_gfx
 mmwrapper:     mmwrapper.o ${SOLIB1}
        ${CC} -lstdc++ -o bin/mmwrapper mmwrapper.o ${SOLIB1}
 
index 2bf84e6..a8c40a6 100644 (file)
@@ -363,7 +363,7 @@ unsigned char category_meta_push ( char *catname, char *parentcatname, pnd_disco
   if ( ! fdcat ) {
     // requested cat is bad, send it to Other
     cat_is_clean = 0;
-    printf ( "PXML Fail %s: Cat request %s (parent %s) -> bad cat\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+    pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> bad cat\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
 
     // do the Other substitution right away, so remaining code has something to look at in fdcat
     fdcat = freedesktop_category_query ( BADCATNAME, NULL );
@@ -381,7 +381,7 @@ unsigned char category_meta_push ( char *catname, char *parentcatname, pnd_disco
     if ( ! fdpcat ) {
       // requested cat is bad, send it to Other
       cat_is_clean = 0;
-      printf ( "PXML Fail %s: Cat request %s (parent %s) -> parent bad cat\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+      pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> parent bad cat\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
       // fix immediately so code doesn't explode
       parentcatname = NULL;
     } else {
@@ -401,12 +401,12 @@ unsigned char category_meta_push ( char *catname, char *parentcatname, pnd_disco
     if ( fdcat -> parent_cat == NULL ) {
       // but wait, catname is actually a parent cat...
       cat_is_clean = 0;
-      printf ( "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be child, but FD says its a parent\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+      pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be child, but FD says its a parent\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
     }
     if ( fdpcat -> parent_cat ) {
       // but wait, parent cat is actually a subcat!
       cat_is_clean = 0;
-      printf ( "PXML Fail %s: Cat request %s (parent %s) -> parent cat, FD says its a child\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+      pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> parent cat, FD says its a child\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
     }
 
   } else {
@@ -415,7 +415,7 @@ unsigned char category_meta_push ( char *catname, char *parentcatname, pnd_disco
     if ( fdcat -> parent_cat ) {
       // but wait, cat actually has a parent!
       cat_is_clean = 0;
-      printf ( "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be parent, FD says its a child\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+      pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be parent, FD says its a child\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
     }
 
   }
@@ -427,11 +427,11 @@ unsigned char category_meta_push ( char *catname, char *parentcatname, pnd_disco
     { 
       // child cat points to a different parent than requested parent!
       cat_is_clean = 0;
-      printf ( "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be child of a cat which FD says is the wrong parent (1)\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+      pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be child of a cat which FD says is the wrong parent (1)\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
     } else if ( strcasecmp ( fdcat -> parent_cat, fdpcat -> cat ) != 0 ) {
       // child cat points to a different parent than requested parent!
       cat_is_clean = 0;
-      printf ( "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be child of a cat which FD says is the wrong parent (2)\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
+      pnd_log ( pndn_warning, "PXML Fail %s: Cat request %s (parent %s) -> cat wants to be child of a cat which FD says is the wrong parent (2)\n", app -> title_en ? app -> title_en : "no name?", catname, parentcatname ? parentcatname : "n/a" );
     }
   }
 
@@ -529,7 +529,7 @@ unsigned char category_meta_push ( char *catname, char *parentcatname, pnd_disco
   // is app already in the target cat? (ie: its being pushed twice due to cat mapping or Other'ing or something..)
   if ( app ) {
     if ( category_contains_app ( catname, parentcatname, app -> unique_id ) ) {
-      printf ( "App Fail: app (%s %s) is already in cat %s\n", app -> title_en ? app -> title_en : "no name?", app -> unique_id, catname );
+      pnd_log ( pndn_warning, "App Fail: app (%s %s) is already in cat %s\n", app -> title_en ? app -> title_en : "no name?", app -> unique_id, catname );
       return ( 1 ); // success, already there!
     }
   }
diff --git a/minimenu/mmcustom_cats.c b/minimenu/mmcustom_cats.c
new file mode 100644 (file)
index 0000000..7cb341a
--- /dev/null
@@ -0,0 +1,225 @@
+
+#include <stdio.h>
+#include <limits.h> /* for PATH_MAX */
+#define __USE_GNU /* for strndup */
+#include <string.h> /* for strdup */
+#include <stdlib.h> /* getenv */
+
+#include "pnd_container.h"
+#include "pnd_conf.h"
+#include "pnd_discovery.h"
+#include "pnd_notify.h"
+#include "pnd_dbusnotify.h"
+#include "pnd_logger.h"
+
+#include "mmenu.h"
+#include "mmcustom_cats.h"
+
+mmcustom_cat_t mmcustom_complete [ MMCUSTOM_CATS_MAX ];
+unsigned int mmcustom_count = 0;
+
+pnd_conf_handle g_custom_h = 0;
+
+char *mmcustom_determine_path ( void ) {
+  char *home = getenv ( "HOME" );
+  static char path [ PATH_MAX ];
+  bzero ( path, PATH_MAX );
+
+  snprintf ( path, PATH_MAX - 1, "%s/%s", home ? home : ".", MMCUSTOM_CATS_PREF_FILENAME );
+
+  return ( path );
+}
+
+static unsigned char loaded = 0;
+unsigned char mmcustom_setup ( void ) {
+
+  if ( ! loaded ) {
+    loaded = 1;
+
+    // inhale conf file
+    char *path = mmcustom_determine_path();
+
+    g_custom_h = pnd_conf_fetch_by_path ( path );
+
+    if ( ! g_custom_h ) {
+      pnd_log ( pndn_rem, "Custom category conf file %s not found; is okay.\n", path );
+      return ( 1 ); // file does not exist most likely
+    }
+
+    // find its head; if no head (empty file), bail
+    char *iter = pnd_box_get_head ( g_custom_h );
+
+    if ( ! iter ) {
+      pnd_log ( pndn_rem, "Custom category conf file %s is empty. Fine.\n", path );
+      return ( 1 );
+    }
+
+    // walk the conf, plucking out the values
+    while ( iter ) {
+      char *k = pnd_box_get_key ( iter );
+
+      // does this entry look like it is a custom category?
+      if ( strncmp ( k, MMCUSTOM_CATS_SECTION, strlen ( MMCUSTOM_CATS_SECTION ) ) == 0 ) {
+       // determine the actual category name part
+       k += ( strlen ( MMCUSTOM_CATS_SECTION ) + 1 );
+
+       mmcustom_complete [ mmcustom_count ].cat = strdup ( k );
+       if ( iter && strcmp ( iter, MMCUSTOM_CATS_NOCAT ) != 0 ) {
+         mmcustom_complete [ mmcustom_count ].parent_cat = strdup ( iter );
+       } else {
+         mmcustom_complete [ mmcustom_count ].parent_cat = NULL;
+       }
+
+       mmcustom_count += 1;
+
+      }
+
+      // next!
+      iter = pnd_box_get_next ( iter );
+
+    } // while
+
+    pnd_log ( pndn_rem, "Found %u custom categories.\n", mmcustom_count );
+
+  } // loaded already?
+
+  return ( 1 );
+}
+
+void mmcustom_shutdown ( void ) {
+
+  bzero ( mmcustom_complete, sizeof(mmcustom_cat_t) * MMCUSTOM_CATS_MAX );
+  mmcustom_count = 0;
+  loaded = 0;
+
+  return;
+}
+
+unsigned char mmcustom_write ( char *fullpath /* if NULL, uses canonical location */ ) {
+
+  if ( ! fullpath ) {
+    fullpath = mmcustom_determine_path();
+  }
+
+  FILE *f = fopen ( fullpath, "w" );
+
+  if ( ! f ) {
+    return ( 0 );
+  }
+
+  int i;
+  for ( i = 0; i < mmcustom_count; i++ ) {
+    if ( mmcustom_complete [ i ].cat ) {
+      fprintf ( f, "%s.%s\t%s\n", MMCUSTOM_CATS_SECTION, mmcustom_complete [ i ].cat, mmcustom_complete [ i ].parent_cat ? mmcustom_complete [ i ].parent_cat : MMCUSTOM_CATS_NOCAT );
+    }
+  }
+
+  fclose ( f );
+
+  return ( 1 );
+}
+
+mmcustom_cat_t *mmcustom_query ( char *name, char *parentcatname ) {
+  int i;
+
+  // search for the cat/parent combination
+  for ( i = 0; i < mmcustom_count; i++ ) {
+    mmcustom_cat_t *p = &(mmcustom_complete [ i ]);
+
+    if ( strcasecmp ( p -> cat, name ) == 0 ) {
+
+      if ( parentcatname == NULL && p -> parent_cat == NULL ) {
+       return ( p );
+      } else if ( parentcatname && p -> parent_cat && strcasecmp ( p -> parent_cat, parentcatname ) == 0 ) {
+       return ( p );
+      }
+
+    }
+
+  } // for
+
+  return ( NULL );
+}
+
+mmcustom_cat_t *mmcustom_register ( char *catname, char *parentcatname ) {
+  mmcustom_complete [ mmcustom_count ].cat = strdup ( catname );
+  if ( parentcatname ) {
+    mmcustom_complete [ mmcustom_count ].parent_cat = strdup ( parentcatname );
+  }
+  mmcustom_count += 1;
+  return ( &(mmcustom_complete [ mmcustom_count - 1 ]) );
+}
+
+unsigned int mmcustom_count_subcats ( char *catname ) {
+  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, catname ) == 0 ) {
+      counter++;
+    }
+
+  }
+
+  return ( counter );
+}
+
+void mmcustom_unregister ( char *catname, char *parentcatname ) {
+  int i;
+  int parent_index = -1;
+
+  if ( parentcatname ) {
+    pnd_log ( pndn_warning, "Goal: Remove subcat %s of %s\n", catname, parentcatname );
+  } else {
+    pnd_log ( pndn_warning, "Goal: Remove parent cat %s and descendants\n", catname );
+  }
+
+  for ( i = 0; i < mmcustom_count; i++ ) {
+
+    // killing a parent cat, or just a subcat?
+    if ( parentcatname ) {
+
+      // killing a subcat, so match cat+subcat to kill
+      if ( mmcustom_complete [ i ].cat && strcmp ( mmcustom_complete [ i ].cat, catname ) == 0 &&
+          mmcustom_complete [ i ].parent_cat && strcasecmp ( mmcustom_complete [ i ].parent_cat, parentcatname ) == 0 )
+      {
+       pnd_log ( pndn_warning, "  Removing subcat: %s of %s\n", catname, parentcatname );
+       free ( mmcustom_complete [ i ].cat );
+       mmcustom_complete [ i ].cat = NULL;
+       break;
+      }
+
+    } else {
+
+      // killing a parent cat, so kill it, and any children of it
+      if ( mmcustom_complete [ i ].parent_cat == NULL &&
+          mmcustom_complete [ i ].cat &&
+          strcmp ( mmcustom_complete [ i ].cat, catname ) == 0 )
+      {
+       // flag the prent for future death (we need its name for now, since the caller is using it)
+       parent_index = i;
+      }
+      else if ( mmcustom_complete [ i ].cat &&
+               mmcustom_complete [ i ].parent_cat &&
+               strcmp ( mmcustom_complete [ i ].parent_cat, catname ) == 0 )
+      {
+       // kill children of it
+       pnd_log ( pndn_warning, "  Removing cascading subcat: %s of %s\n", mmcustom_complete [ i ].cat, mmcustom_complete [ i ].parent_cat );
+       free ( mmcustom_complete [ i ].cat );
+       mmcustom_complete [ i ].cat = NULL;
+      }
+
+    }
+
+  } // for
+
+  // kill the actual cat itself
+  if ( i >= 0 ) {
+    pnd_log ( pndn_warning, "  Removing cat: %s\n", catname );
+    free ( mmcustom_complete [ i ].cat );
+    mmcustom_complete [ i ].cat = NULL;
+  }
+
+  return;
+}
diff --git a/minimenu/mmcustom_cats.h b/minimenu/mmcustom_cats.h
new file mode 100644 (file)
index 0000000..a003451
--- /dev/null
@@ -0,0 +1,32 @@
+
+#ifndef h_mmcustom_cats_h
+#define h_mmcustom_cats_h
+
+#define MMCUSTOM_CATS_MAX 255
+#define MMCUSTOM_CATS_PREF_FILENAME ".mmcats.conf"
+
+#define MMCUSTOM_CATS_SECTION "custom_categories"
+
+#define MMCUSTOM_CATS_NOCAT "*parent*"
+
+typedef struct {
+  char *cat;
+  char *parent_cat;
+} mmcustom_cat_t;
+
+extern mmcustom_cat_t mmcustom_complete[];
+extern unsigned int mmcustom_count;
+
+unsigned char mmcustom_setup ( void ); // load
+void mmcustom_shutdown ( void );       // unload
+unsigned char mmcustom_write ( char *fullpath /* if NULL, uses canonical location */ );   // save
+char *mmcustom_determine_path ( void );
+
+mmcustom_cat_t *mmcustom_query ( char *catname, char *parentcatname ); // parentcatname NULL for parents
+
+mmcustom_cat_t *mmcustom_register ( char *catname, char *parentcatname );
+void mmcustom_unregister ( char *catname, char *parentcatname );
+
+unsigned int mmcustom_count_subcats ( char *catname );
+
+#endif
index ceffeca..1770897 100644 (file)
@@ -41,6 +41,7 @@
 #include "mmconf.h"
 #include "mmui_context.h"
 #include "freedesktop_cats.h"
+#include "mmcustom_cats.h"
 
 #define CHANGED_NOTHING     (0)
 #define CHANGED_CATEGORY    (1<<0)  /* changed to different category */
@@ -1342,6 +1343,7 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) {
          "Reveal hidden category",
          "Shutdown Pandora",
          "Configure Minimenu",
+         "Manage custom app categories",
          "Rescan for applications",
          "Cache previews to SD now",
          "Run a terminal/console",
@@ -1350,7 +1352,7 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) {
          "Select a Minimenu skin",
          "About Minimenu"
        };
-       int sel = ui_modal_single_menu ( opts, 10, "Minimenu", "Enter to select; other to return." );
+       int sel = ui_modal_single_menu ( opts, 11, "Minimenu", "Enter to select; other to return." );
 
        char buffer [ 100 ];
        if ( sel == 0 ) {
@@ -1368,12 +1370,15 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) {
            emit_and_quit ( MM_RESTART );
          }
        } else if ( sel == 3 ) {
+         // manage custom categories
+         ui_manage_categories();
+       } else if ( sel == 4 ) {
          // rescan apps
          pnd_log ( pndn_debug, "Freeing up applications\n" );
          applications_free();
          pnd_log ( pndn_debug, "Rescanning applications\n" );
          applications_scan();
-       } else if ( sel == 4 ) {
+       } else if ( sel == 5 ) {
          // cache preview to SD now
          extern pnd_box_handle g_active_apps;
          pnd_box_handle h = g_active_apps;
@@ -1396,7 +1401,7 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) {
            iter = pnd_box_get_next ( iter );
          } // while
 
-       } else if ( sel == 5 ) {
+       } else if ( sel == 6 ) {
          // run terminal
          char *argv[5];
          argv [ 0 ] = pnd_conf_get_as_char ( g_conf, "utility.terminal" );
@@ -1406,18 +1411,18 @@ void ui_process_input ( pnd_dbusnotify_handle dbh, pnd_notify_handle nh ) {
            ui_forkexec ( argv );
          }
 
-       } else if ( sel == 6 ) {
+       } else if ( sel == 7 ) {
          char buffer [ PATH_MAX ];
          sprintf ( buffer, "%s %s\n", MM_RUN, "/usr/pandora/scripts/op_switchgui.sh" );
          emit_and_quit ( buffer );
-       } else if ( sel == 7 ) {
-         emit_and_quit ( MM_QUIT );
        } else if ( sel == 8 ) {
+         emit_and_quit ( MM_QUIT );
+       } else if ( sel == 9 ) {
          // select skin
          if ( ui_pick_skin() ) {
            emit_and_quit ( MM_RESTART );
          }
-       } else if ( sel == 9 ) {
+       } else if ( sel == 10 ) {
          // about
          char buffer [ PATH_MAX ];
          sprintf ( buffer, "%s/about.txt", g_skinpath );
@@ -3576,6 +3581,16 @@ void ui_menu_context ( mm_appref_t *a ) {
   return;
 }
 
+unsigned char ui_menu_oneby ( char *title, char *footer, char *one ) {
+  char *opts [ 2 ];
+  opts [ 0 ] = one;
+  int sel = ui_modal_single_menu ( opts, 1, title, footer );
+  if ( sel < 0 ) {
+    return ( 0 );
+  }
+  return ( sel + 1 );
+}
+
 unsigned char ui_menu_twoby ( char *title, char *footer, char *one, char *two ) {
   char *opts [ 3 ];
   opts [ 0 ] = one;
@@ -3696,6 +3711,10 @@ unsigned char ui_menu_get_text_line ( char *title, char *footer, char *initialva
            char *eol = strchr ( r_buffer, '\0' );
            *( eol - 1 ) = '\0';
          }
+
+       } else if ( event.key.keysym.sym == SDLK_UP ) {
+         r_buffer [ 0 ] = '\0'; // truncate!
+
        } else if ( event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_END ) { // return, or "B"
          return ( 1 );
 
@@ -3801,3 +3820,282 @@ unsigned char ovr_replace_or_add ( mm_appref_t *a, char *keybase, char *newvalue
 
   return ( 1 );
 }
+
+void ui_manage_categories ( void ) {
+  unsigned char require_app_scan = 0;
+
+  if ( ! mmcustom_setup() ) {
+    return; // error
+  }
+
+  char *opts [ 20 ] = {
+    "List custom categories",
+    "List custom subcategories",
+    "Register custom category",
+    "Register custom subcategory",
+    "Unregister custom category",
+    "Unregister custom subcategory",
+    "Done"
+  };
+
+  while ( 1 ) {
+
+    int sel = ui_modal_single_menu ( opts, 7, "Custom Categories", "B to select; other to cancel." );
+
+    switch ( sel ) {
+
+    case 0: // list custom
+      if ( mmcustom_count ) {
+       ui_pick_custom_category();
+      } else {
+       ui_menu_oneby ( "Warning", "B/Enter to accept", "There are none registered." );
+      }
+      break;
+
+    case 1: // list custom sub
+      if ( mmcustom_count ) {
+
+       int maincat = ui_pick_custom_category();
+
+       if ( maincat >= 0 ) {
+         unsigned int subcount = mmcustom_count_subcats ( mmcustom_complete [ maincat ].cat );
+         char titlebuf [ 201 ];
+
+         snprintf ( titlebuf, 200, "Category: %s", mmcustom_complete [ maincat ].cat );
+
+         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, mmcustom_complete [ maincat ].cat ) == 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 ) {
+
+         // 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 ( mmcustom_count ) {
+
+       int maincat = ui_pick_custom_category();
+
+       if ( maincat >= 0 ) {
+         char titlebuf [ 201 ];
+
+         snprintf ( titlebuf, 200, "Subcat of: %s", mmcustom_complete [ maincat ].cat );
+
+         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 ) {
+
+           // and if so, is it existant already or not?
+           if ( mmcustom_query ( namebuf, mmcustom_complete [ maincat ].cat ) ) {
+             ui_menu_oneby ( "Warning", "B/Enter to accept", "Already a subcategory." );
+           } else if ( freedesktop_category_query ( namebuf, mmcustom_complete [ maincat ].cat ) ) {
+             ui_menu_oneby ( "Warning", "B/Enter to accept", "Already a Standard subcategory." );
+           } else {
+
+             char confirm [ 1001 ];
+             snprintf ( confirm, 1000, "Confirm: %s [%s]", namebuf, mmcustom_complete [ maincat ].cat );
+
+             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, mmcustom_complete [ maincat ].cat );
+               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 ) {
+       int maincat = ui_pick_custom_category();
+
+       if ( maincat >= 0 ) {
+         char confirm [ 1001 ];
+         snprintf ( confirm, 1000, "Confirm remove: %s", mmcustom_complete [ maincat ].cat );
+
+         if ( ui_menu_twoby ( confirm, "B/enter; other to cancel", "Confirm unregister", "Do not unregister" ) == 1 ) {
+           // register, save, recycle the current list
+           mmcustom_unregister ( mmcustom_complete [ maincat ].cat, 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 ) {
+       int maincat = ui_pick_custom_category();
+
+       if ( maincat >= 0 ) {
+         unsigned int subcount = mmcustom_count_subcats ( mmcustom_complete [ maincat ].cat );
+         char titlebuf [ 201 ];
+
+         snprintf ( titlebuf, 200, "Category: %s", mmcustom_complete [ maincat ].cat );
+
+         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, mmcustom_complete [ maincat ].cat ) == 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 ], mmcustom_complete [ maincat ].cat );
+               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;
+}
+
+int ui_pick_custom_category ( void ) {
+  char **list = malloc ( mmcustom_count * sizeof(char*) );
+  int i;
+  unsigned int counter = 0;
+
+  for ( i = 0; i < mmcustom_count; i++ ) {
+    if ( mmcustom_complete [ i ].parent_cat == NULL ) {
+      list [ counter++ ] = mmcustom_complete [ i ].cat;
+    }
+  }
+
+  int sel = ui_modal_single_menu ( list, counter, "Custom Main Categories", "Any button to exit." );
+
+  counter = 0;
+  for ( i = 0; i < mmcustom_count; i++ ) {
+    if ( mmcustom_complete [ i ].parent_cat == NULL ) {
+      if ( counter == sel ) {
+       free ( list );
+       return ( i );
+      }
+      counter ++;
+    }
+  }
+
+  free ( list );
+
+  return ( -1 );
+}
index 7472294..65cdb71 100644 (file)
@@ -74,6 +74,8 @@ void ui_post_scan ( void );
 unsigned char ui_show_info ( char *pndrun, pnd_disco_t *p );
 void ui_aboutscreen ( char *textpath );
 void ui_revealscreen ( void );
+void ui_manage_categories ( void );
+int ui_pick_custom_category ( void );
 
 /* internal functions follow
  */
@@ -120,6 +122,7 @@ unsigned int ui_callback_f ( unsigned int t );
 
 // 'popup' 'context' menu
 void ui_menu_context ( mm_appref_t *a );
+unsigned char ui_menu_oneby ( char *title, char *footer, char *one ); // return 0 (nada) or 1 (one) for they actually picked it
 unsigned char ui_menu_twoby ( char *title, char *footer, char *one, char *two ); // return 0 (nada), 1 (one), 2 (two)
 unsigned char ui_menu_get_text_line ( char *title, char *footer, char *initialvalue,
                                      char *r_buffer, unsigned char maxlen, unsigned char numbersonlyp ); // populates r_buffer