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.
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 */
} 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,
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 */
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 */
}
}
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);
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);
# 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
ObtLink *link;
};
+struct _ObtLinkBaseCategory {
+ GQuark cat;
+ GSList *links;
+};
+
struct _ObtLinkBase {
gint ref;
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;
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. */
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);
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);
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);
+ }
}
}
}
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);
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);
#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);
}
{
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]);
e->data.normal.data = link;
*/
+
+ dirty = FALSE;
return TRUE; /* always show the menu */
}
#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 */