Show the list of categories in the apps menus as submenus.
authorDana Jansens <danakj@orodu.net>
Wed, 26 Jan 2011 23:11:22 +0000 (18:11 -0500)
committerDana Jansens <danakj@orodu.net>
Sun, 16 Oct 2011 22:54:04 +0000 (18:54 -0400)
The categories are not based on what you actually have yet but will be soon.

Removed the "main category" notion from the links themselves, they just
publish a list of categories instead.  Moved this notion out to the Apps menu
itself.  This should make it easy to possibly to customize your set of
visible categories if we should like to do that sometime.

obt/link.c
obt/link.h
obt/linkbase.c
openbox/apps_menu.c

index b434ea37f845741759031d1b50f3a5a29b431c00..ab215b50406e854cf0930f0a8818ac9cc7d72f46 100644 (file)
@@ -55,7 +55,6 @@ struct _ObtLink {
 
             gchar **mime; /*!< Mime types the app can open */
 
-            GQuark main_category; /*!< The first main category listed */
             GQuark *categories; /*!< Array of quarks representing the
                                   application's categories */
             gulong  n_categories; /*!< Number of categories for the app */
@@ -71,30 +70,6 @@ struct _ObtLink {
     } d;
 };
 
-#define NUM_MAIN_CATEGORIES 10
-static GQuark MAIN_CATEGORIES[NUM_MAIN_CATEGORIES];
-static gboolean setup = FALSE;
-
-static void startup()
-{
-    if (setup) return;
-
-    setup = TRUE;
-
-    /* From http://standards.freedesktop.org/menu-spec/latest/apa.html */
-    MAIN_CATEGORIES[0] = g_quark_from_static_string("AudioVideo");
-    MAIN_CATEGORIES[1] = g_quark_from_static_string("Development");
-    MAIN_CATEGORIES[2] = g_quark_from_static_string("Education");
-    MAIN_CATEGORIES[3] = g_quark_from_static_string("Game");
-    MAIN_CATEGORIES[4] = g_quark_from_static_string("Graphics");
-    MAIN_CATEGORIES[5] = g_quark_from_static_string("Network");
-    MAIN_CATEGORIES[6] = g_quark_from_static_string("Office");
-    MAIN_CATEGORIES[7] = g_quark_from_static_string("Settings");
-    MAIN_CATEGORIES[8] = g_quark_from_static_string("System");
-    MAIN_CATEGORIES[9] = g_quark_from_static_string("Utility");
-    qsort(MAIN_CATEGORIES, NUM_MAIN_CATEGORIES, sizeof(guint), obt_guint_cmp);
-}
-
 ObtLink* obt_link_from_ddfile(const gchar *path, ObtPaths *p,
                               const gchar *language,
                               const gchar *country,
@@ -105,8 +80,6 @@ ObtLink* obt_link_from_ddfile(const gchar *path, ObtPaths *p,
     ObtDDParseGroup *g;
     ObtDDParseValue *v;
 
-    startup();
-
     /* parse the file, and get a hash table of the groups */
     groups = obt_ddparse_file(path, language, country, modifier);
     if (!groups) return NULL; /* parsing failed */
@@ -209,29 +182,13 @@ ObtLink* obt_link_from_ddfile(const gchar *path, ObtPaths *p,
 
         if ((v = g_hash_table_lookup(keys, "Categories"))) {
             gulong i;
-            gchar *end;
-            gboolean found_main;
-            GQuark cat;
 
             link->d.app.categories = g_new(GQuark, v->value.strings.n);
             link->d.app.n_categories = v->value.strings.n;
 
-            found_main = FALSE;
             for (i = 0; i < v->value.strings.n; ++i) {
-                cat = link->d.app.categories[i] =
+                link->d.app.categories[i] =
                     g_quark_from_string(v->value.strings.a[i]);
-
-                if (!found_main) {
-                    BSEARCH_SETUP(guint);
-                    BSEARCH(guint, MAIN_CATEGORIES, 0, NUM_MAIN_CATEGORIES,
-                            cat);
-                    if (BSEARCH_FOUND()) {
-                        found_main = TRUE;
-                        link->d.app.main_category = cat;
-                    }
-                }
-
-                c = end = end+1; /* next */
             }
         }
 
@@ -287,14 +244,6 @@ ObtLinkType obt_link_type (ObtLink *e)
     return e->type;
 }
 
-GQuark obt_link_app_main_category          (ObtLink *e)
-{
-    g_return_val_if_fail(e != NULL, 0);
-    g_return_val_if_fail(e->type == OBT_LINK_TYPE_APPLICATION, 0);
-
-    return e->d.app.main_category;
-}
-
 const GQuark* obt_link_app_categories(ObtLink *e, gulong *n)
 {
     g_return_val_if_fail(e != NULL, NULL);
index 922030cdc8a490622640a5c5bedf634911d93349..6577d8a3877426154cb3189e8a62559e7fa4d610 100644 (file)
@@ -119,10 +119,6 @@ const gchar*  obt_link_app_path            (ObtLink *e);
 gboolean      obt_link_app_run_in_terminal (ObtLink *e);
 const gchar*const* obt_link_app_mime_types (ObtLink *e);
 
-/*! Returns the first main category listed by the link. This may be 0 if the
-  application does not list a category. */
-GQuark obt_link_app_main_category          (ObtLink *e);
-
 /*! Returns a list of categories listed by the link.  This may be empty if the
   application does not list a category. */
 const GQuark* obt_link_app_categories      (ObtLink *e, gulong *n);
index f96ffbfc08b4112a4cdfbdae425d6fbeb4870fdf..28f617f3ddb21d2818d3e37b29079a7a909e95f5 100644 (file)
@@ -25,7 +25,8 @@
 # include <string.h>
 #endif
 
-typedef struct _ObtLinkBaseEntry ObtLinkBaseEntry;
+typedef struct _ObtLinkBaseEntry    ObtLinkBaseEntry;
+typedef struct _ObtLinkBaseCategory ObtLinkBaseCategory;
 
 struct _ObtLinkBaseEntry {
     /*! Links come from a set of paths.  Links found in earlier paths get lower
@@ -35,6 +36,11 @@ struct _ObtLinkBaseEntry {
     ObtLink *link;
 };
 
+struct _ObtLinkBaseCategory {
+    GQuark cat;
+    GSList *links;
+};
+
 struct _ObtLinkBase {
     gint ref;
 
@@ -55,11 +61,12 @@ struct _ObtLinkBase {
       integer that is the priority of that directory. */
     GHashTable *path_to_priority;
 
-    /*! This maps GQuark main categories to GSLists of ObtLink objects found in
+    /*! This maps GQuark main categories to ObtLinkBaseCategory objects,
+      containing lists of ObtLink objects found in
       the category. The ObtLink objects are not reffed to be placed in this
       structure since they will always be in the base hash table as well. So
       they are not unreffed when they are removed. */
-    GHashTable *main_categories;
+    GHashTable *categories;
 
     ObtLinkBaseUpdateFunc update_func;
     gpointer update_data;
@@ -104,25 +111,39 @@ static GSList* find_base_entry_priority(GSList *list, gint priority)
     return it;
 }
 
-static void main_category_add(ObtLinkBase *lb, GQuark cat, ObtLink *link)
+static void category_add(ObtLinkBase *lb, GQuark cat, ObtLink *link)
 {
-    GSList *list;
-
-    list = g_hash_table_lookup(lb->main_categories, &cat);
-    list = g_slist_prepend(list, link);
-    g_hash_table_insert(lb->main_categories, &cat, list);
+    ObtLinkBaseCategory *lc;
+
+    lc = g_hash_table_lookup(lb->categories, &cat);
+    if (!lc) {
+        lc = g_slice_new(ObtLinkBaseCategory);
+        lc->cat = cat;
+        lc->links = NULL;
+        g_hash_table_insert(lb->categories, &lc->cat, lc);
+    }
+    lc->links = g_slist_prepend(lc->links, link);
 }
 
-static void main_category_remove(ObtLinkBase *lb, GQuark cat, ObtLink *link)
+static void category_remove(ObtLinkBase *lb, GQuark cat, ObtLink *link)
 {
-    GSList *list, *it;
+    ObtLinkBaseCategory *lc;
+    GSList *it;
 
-    list = g_hash_table_lookup(lb->main_categories, &cat);
-    it = list;
+    lc = g_hash_table_lookup(lb->categories, &cat);
+
+    it = lc->links;
     while (it->data != link)
         it = g_slist_next(it);
-    list = g_slist_delete_link(list, it);
-    g_hash_table_insert(lb->main_categories, &cat, list);
+    lc->links = g_slist_delete_link(lc->links, it);
+    if (!lc->links)
+        g_hash_table_remove(lb->categories, &cat);
+}
+
+static void category_free(ObtLinkBaseCategory *lc)
+{
+    g_slist_free(lc->links);
+    g_slice_free(ObtLinkBaseCategory, lc);
 }
 
 /*! Called when a change happens in the filesystem. */
@@ -155,9 +176,14 @@ static void update(ObtWatch *w, const gchar *base_path,
 
             ObtLink *link = it->data;
 
-            if (obt_link_type(link) == OBT_LINK_TYPE_APPLICATION)
-                main_category_remove(
-                    self, obt_link_app_main_category(link), link);
+            if (obt_link_type(link) == OBT_LINK_TYPE_APPLICATION) {
+                const GQuark *cats;
+                gulong i, n;
+
+                cats = obt_link_app_categories(link, &n);
+                for (i = 0; i < n; ++i)
+                    category_remove(self, cats[i], link);
+            }
 
             list = g_slist_delete_link(list, it);
             base_entry_free(it->data);
@@ -182,9 +208,14 @@ static void update(ObtWatch *w, const gchar *base_path,
 
             ObtLink *link = it->data;
 
-            if (obt_link_type(link) == OBT_LINK_TYPE_APPLICATION)
-                main_category_remove(
-                    self, obt_link_app_main_category(link), link);
+            if (obt_link_type(link) == OBT_LINK_TYPE_APPLICATION) {
+                const GQuark *cats;
+                gulong i, n;
+
+                cats = obt_link_app_categories(link, &n);
+                for (i = 0; i < n; ++i)
+                    category_remove(self, cats[i], link);
+            }
 
             list = g_slist_delete_link(list, it);
             base_entry_free(it->data);
@@ -227,9 +258,14 @@ static void update(ObtWatch *w, const gchar *base_path,
                 g_hash_table_insert(self->base, id, list);
                 id = NULL;
 
-                if (obt_link_type(link) == OBT_LINK_TYPE_APPLICATION)
-                    main_category_add(
-                        self, obt_link_app_main_category(link), link);
+                if (obt_link_type(link) == OBT_LINK_TYPE_APPLICATION) {
+                    const GQuark *cats;
+                    gulong i, n;
+
+                    cats = obt_link_app_categories(link, &n);
+                    for (i = 0; i < n; ++i)
+                        category_add(self, cats[i], link);
+                }
             }
         }
     }
@@ -252,9 +288,10 @@ ObtLinkBase* obt_linkbase_new(ObtPaths *paths, const gchar *locale,
     self->environments = environments;
     self->watch = obt_watch_new();
     self->base = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
-    self->path_to_priority = g_hash_table_new_full(g_str_hash, g_str_equal,
-                                                g_free, g_free);
-    self->main_categories = g_hash_table_new(g_int_hash, g_int_equal);
+    self->path_to_priority = g_hash_table_new_full(
+        g_str_hash, g_str_equal, g_free, g_free);
+    self->categories = g_hash_table_new_full(
+        g_int_hash, g_int_equal, NULL, (GDestroyNotify)category_free);
     self->paths = paths;
     obt_paths_ref(paths);
 
@@ -344,7 +381,7 @@ void obt_linkbase_unref(ObtLinkBase *self)
         g_hash_table_foreach(self->base, base_entry_list_free, NULL);
 
         obt_watch_unref(self->watch);
-        g_hash_table_unref(self->main_categories);
+        g_hash_table_unref(self->categories);
         g_hash_table_unref(self->path_to_priority);
         g_hash_table_unref(self->base);
         obt_paths_unref(self->paths);
index 7e5fb709ae8f416cc70e8359b0624c1b184e7eb3..364a1b2f4b9190bf135437cce0c189d8fdd6542b 100644 (file)
 
 #define MENU_NAME "apps-menu"
 
+typedef struct _ObAppsMenuCategory ObAppsMenuCategory;
+
+struct _ObAppsMenuCategory {
+    GQuark q;
+    gchar *friendly;
+    ObMenu *m;
+};
+
 static ObMenu *apps_menu;
 static ObtLinkBase *linkbase;
 static gboolean dirty;
 
-static void self_cleanup(ObMenu *menu, gpointer data)
+static ObAppsMenuCategory *categories;
+/*! An array of pointers to the categories array, sorted by their friendly
+  names. */
+static ObAppsMenuCategory **sorted_categories;
+static guint n_categories;
+
+static void self_destroy(ObMenu *menu, gpointer data)
 {
     menu_clear_entries(menu);
 }
@@ -44,9 +58,28 @@ static gboolean self_update(ObMenuFrame *frame, gpointer data)
 {
     ObMenu *menu = frame->menu;
     ObMenuEntry *e;
+    guint i;
+
+    g_print("UPDATE DIRTY %d\n", dirty);
 
     if (!dirty) return TRUE;  /* nothing has changed to be updated */
 
+    menu_clear_entries(menu);
+
+    for (i = 0; i < n_categories; ++i) {
+        menu_free(categories[i].m);
+        categories[i].m = NULL;
+    }
+
+
+    for (i = 0; i < n_categories; ++i) {
+        // XXX if not empty
+        ObAppsMenuCategory *cat = sorted_categories[i];
+        const gchar *label = g_quark_to_string(cat->q);
+        if (!cat->m)
+            cat->m = menu_new(label, cat->friendly, FALSE, NULL);
+        e = menu_add_submenu(menu, i, label);
+    }
 /*
     menu_add_separator(menu, SEPARATOR, screen_desktop_names[desktop]);
 
@@ -64,6 +97,8 @@ static gboolean self_update(ObMenuFrame *frame, gpointer data)
     e->data.normal.data = link;
 */
 
+
+    dirty = FALSE;
     return TRUE; /* always show the menu */
 }
 
@@ -84,34 +119,93 @@ static void menu_execute(ObMenuEntry *self, ObMenuFrame *f,
 #endif
 }
 
-void linkbase_update(ObtLinkBase *lb, gpointer data)
+static void linkbase_update(ObtLinkBase *lb, gpointer data)
 {
     dirty = TRUE;
 }
 
+static int cat_cmp(const void *a, const void *b)
+{
+    const ObAppsMenuCategory *ca = a, *cb = b;
+    return ca->q - cb->q;
+}
+
+static int cat_friendly_cmp(const void *a, const void *b)
+{
+    ObAppsMenuCategory *const *ca = a, *const *cb = b;
+    return strcmp((*ca)->friendly, (*cb)->friendly);
+}
+
 void apps_menu_startup(gboolean reconfig)
 {
     if (!reconfig) {
         ObtPaths *paths;
+        guint i;
 
         paths = obt_paths_new();
         /* XXX allow more environments, like GNOME or KDE, to be included */
         linkbase = obt_linkbase_new(paths, ob_locale_msg,
                                     OBT_LINK_ENV_OPENBOX);
         obt_paths_unref(paths);
+        obt_linkbase_set_update_func(linkbase, linkbase_update, NULL);
+
+        dirty = TRUE;
+
+        /* From http://standards.freedesktop.org/menu-spec/latest/apa.html */
+        n_categories = 10;
+        categories = g_new0(ObAppsMenuCategory, n_categories);
+        sorted_categories = g_new(ObAppsMenuCategory*, n_categories);
+
+        categories[0].q = g_quark_from_static_string("AudioVideo");
+        categories[0].friendly = _("Sound & Video");
+        categories[1].q = g_quark_from_static_string("Development");
+        categories[1].friendly = _("Programming");
+        categories[2].q = g_quark_from_static_string("Education");
+        categories[2].friendly = _("Education");
+        categories[3].q = g_quark_from_static_string("Game");
+        categories[3].friendly = _("Games");
+        categories[4].q = g_quark_from_static_string("Graphics");
+        categories[4].friendly = _("Graphics");
+        categories[5].q = g_quark_from_static_string("Network");
+        categories[5].friendly = _("Internet");
+        categories[6].q = g_quark_from_static_string("Office");
+        categories[6].friendly = _("Office");
+        categories[7].q = g_quark_from_static_string("Settings");
+        categories[7].friendly = _("Settings");
+        categories[8].q = g_quark_from_static_string("System");
+        categories[8].friendly = _("System");
+        categories[9].q = g_quark_from_static_string("Utility");
+        categories[9].friendly = _("Utility");
+        /* Sort them by their quark values */
+        qsort(categories, n_categories, sizeof(ObAppsMenuCategory), cat_cmp);
+
+        for (i = 0; i < n_categories; ++i)
+            sorted_categories[i] = &categories[i];
+        qsort(sorted_categories, n_categories, sizeof(void*),
+              cat_friendly_cmp);
     }
 
     apps_menu = menu_new(MENU_NAME, _("Applications"), TRUE, NULL);
     menu_set_update_func(apps_menu, self_update);
-    menu_set_cleanup_func(apps_menu, self_cleanup);
+    menu_set_destroy_func(apps_menu, self_destroy);
     menu_set_execute_func(apps_menu, menu_execute);
 }
 
 void apps_menu_shutdown(gboolean reconfig)
 {
     if (!reconfig) {
+        guint i;
+
         obt_linkbase_unref(linkbase);
         linkbase = NULL;
+
+        for (i = 0; i < n_categories; ++i)
+            if (categories[i].m)
+                menu_free(categories[i].m);
+        g_free(categories);
+        categories = NULL;
+        g_free(sorted_categories);
+        sorted_categories = NULL;
     }
 
     /* freed by the hash table */