Store all links in the linkbase grouped by their main category.
authorDana Jansens <danakj@orodu.net>
Tue, 25 Jan 2011 22:38:47 +0000 (17:38 -0500)
committerDana Jansens <danakj@orodu.net>
Sun, 16 Oct 2011 22:54:04 +0000 (18:54 -0400)
obt/link.c
obt/link.h
obt/linkbase.c

index de665f073e52d9cf5f29fc74fe85f6cc8c9ac3f4..b434ea37f845741759031d1b50f3a5a29b431c00 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
 
    obt/link.c for the Openbox window manager
-   Copyright (c) 2009        Dana Jansens
+   Copyright (c) 2009-2011   Dana Jansens
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 */
 
 #include "obt/link.h"
+#include "obt/bsearch.h"
 #include "obt/ddparse.h"
 #include "obt/paths.h"
+#include "obt/util.h"
 #include <glib.h>
 
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
 struct _ObtLink {
     guint ref;
 
@@ -49,6 +55,7 @@ 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 */
@@ -64,6 +71,30 @@ 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,
@@ -74,6 +105,8 @@ 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 */
@@ -177,13 +210,27 @@ 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) {
-                link->d.app.categories[i] =
+                cat = 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 */
             }
         }
@@ -233,6 +280,21 @@ void obt_link_unref(ObtLink *dd)
     }
 }
 
+ObtLinkType obt_link_type (ObtLink *e)
+{
+    g_return_val_if_fail(e != NULL, 0);
+
+    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 973f1132c4648fa62200b5d13ddee125bd01dc26..922030cdc8a490622640a5c5bedf634911d93349 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
  
    obt/link.h for the Openbox window manager
-   Copyright (c) 2009        Dana Jansens
+   Copyright (c) 2009-2011   Dana Jansens
  
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -117,8 +117,16 @@ const gchar*  obt_link_app_executable      (ObtLink *e);
 /*! Returns the path in which the application should be run */
 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);
+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);
+
 /*! Returns a combination of values in the ObtLinkAppOpen enum,
     specifying if the application can be launched to open one or more files
     and URLs. */
index 6ab02887e63826356edd6ace36d1f5d551a8a1ba..f96ffbfc08b4112a4cdfbdae425d6fbeb4870fdf 100644 (file)
@@ -55,6 +55,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
+      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;
+
     ObtLinkBaseUpdateFunc update_func;
     gpointer update_data;
 };
@@ -98,6 +104,27 @@ static GSList* find_base_entry_priority(GSList *list, gint priority)
     return it;
 }
 
+static void main_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);
+}
+
+static void main_category_remove(ObtLinkBase *lb, GQuark cat, ObtLink *link)
+{
+    GSList *list, *it;
+
+    list = g_hash_table_lookup(lb->main_categories, &cat);
+    it = list;
+    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);
+}
+
 /*! Called when a change happens in the filesystem. */
 static void update(ObtWatch *w, const gchar *base_path,
                    const gchar *sub_path,
@@ -125,6 +152,13 @@ static void update(ObtWatch *w, const gchar *base_path,
         if (it) {
             /* it may be false if the link was skipped during the add because
                it did not want to be displayed */
+
+            ObtLink *link = it->data;
+
+            if (obt_link_type(link) == OBT_LINK_TYPE_APPLICATION)
+                main_category_remove(
+                    self, obt_link_app_main_category(link), link);
+
             list = g_slist_delete_link(list, it);
             base_entry_free(it->data);
 
@@ -145,6 +179,13 @@ static void update(ObtWatch *w, const gchar *base_path,
         if (it) {
             /* it may be false if the link was skipped during the add because
                it did not want to be displayed */
+
+            ObtLink *link = it->data;
+
+            if (obt_link_type(link) == OBT_LINK_TYPE_APPLICATION)
+                main_category_remove(
+                    self, obt_link_app_main_category(link), link);
+
             list = g_slist_delete_link(list, it);
             base_entry_free(it->data);
             /* this will put the modified list into the hash table */
@@ -185,6 +226,10 @@ static void update(ObtWatch *w, const gchar *base_path,
                 /* this will free 'id' */
                 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);
             }
         }
     }
@@ -209,6 +254,7 @@ ObtLinkBase* obt_linkbase_new(ObtPaths *paths, const gchar *locale,
     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->paths = paths;
     obt_paths_ref(paths);
 
@@ -298,6 +344,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->path_to_priority);
         g_hash_table_unref(self->base);
         obt_paths_unref(self->paths);