diff options
Diffstat (limited to 'libmenu/desktop-entries.c')
-rw-r--r-- | libmenu/desktop-entries.c | 816 |
1 files changed, 816 insertions, 0 deletions
diff --git a/libmenu/desktop-entries.c b/libmenu/desktop-entries.c new file mode 100644 index 0000000..9488bde --- /dev/null +++ b/libmenu/desktop-entries.c @@ -0,0 +1,816 @@ +/* + * Copyright (C) 2002 - 2004 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <config.h> + +#include "desktop-entries.h" + +#include <string.h> + +#include "menu-util.h" + +#define DESKTOP_ENTRY_GROUP "Desktop Entry" +#define KDE_DESKTOP_ENTRY_GROUP "KDE Desktop Entry" + +enum { + DESKTOP_ENTRY_NO_DISPLAY = 1 << 0, + DESKTOP_ENTRY_HIDDEN = 1 << 1, + DESKTOP_ENTRY_SHOW_IN_MATE = 1 << 2, + DESKTOP_ENTRY_TRYEXEC_FAILED = 1 << 3 +}; + +struct DesktopEntry { + char* path; + char* basename; + + GQuark* categories; + + char* name; + char* generic_name; + char* full_name; + char* comment; + char* icon; + char* exec; + gboolean terminal; + + guint type: 2; + guint flags: 4; + guint refcount: 24; +}; + +struct DesktopEntrySet { + int refcount; + GHashTable* hash; +}; + +/* + * Desktop entries + */ + +static guint get_flags_from_key_file(DesktopEntry* entry, GKeyFile* key_file, const char* desktop_entry_group) +{ + GError *error; + char **strv; + gboolean no_display; + gboolean hidden; + gboolean show_in_mate; + gboolean tryexec_failed; + char *tryexec; + guint flags; + int i; + + error = NULL; + no_display = g_key_file_get_boolean (key_file, + desktop_entry_group, + "NoDisplay", + &error); + if (error) + { + no_display = FALSE; + g_error_free (error); + } + + error = NULL; + hidden = g_key_file_get_boolean (key_file, + desktop_entry_group, + "Hidden", + &error); + if (error) + { + hidden = FALSE; + g_error_free (error); + } + + show_in_mate = TRUE; + strv = g_key_file_get_string_list (key_file, + desktop_entry_group, + "OnlyShowIn", + NULL, + NULL); + if (strv) + { + show_in_mate = FALSE; + for (i = 0; strv[i]; i++) + { + if (!strcmp (strv[i], "MATE")) + { + show_in_mate = TRUE; + break; + } + } + } + else + { + strv = g_key_file_get_string_list (key_file, + desktop_entry_group, + "NotShowIn", + NULL, + NULL); + if (strv) + { + show_in_mate = TRUE; + for (i = 0; strv[i]; i++) + { + if (!strcmp (strv[i], "MATE")) + { + show_in_mate = FALSE; + } + } + } + } + g_strfreev (strv); + + tryexec_failed = FALSE; + tryexec = g_key_file_get_string (key_file, + desktop_entry_group, + "TryExec", + NULL); + if (tryexec) + { + char *path; + + path = g_find_program_in_path (g_strstrip (tryexec)); + + tryexec_failed = (path == NULL); + + g_free (path); + g_free (tryexec); + } + + flags = 0; + if (no_display) + flags |= DESKTOP_ENTRY_NO_DISPLAY; + if (hidden) + flags |= DESKTOP_ENTRY_HIDDEN; + if (show_in_mate) + flags |= DESKTOP_ENTRY_SHOW_IN_MATE; + if (tryexec_failed) + flags |= DESKTOP_ENTRY_TRYEXEC_FAILED; + + return flags; +} + +static GQuark* get_categories_from_key_file (DesktopEntry* entry, GKeyFile* key_file, const char* desktop_entry_group) +{ + GQuark *retval; + char **strv; + gsize len; + int i; + + strv = g_key_file_get_string_list (key_file, + desktop_entry_group, + "Categories", + &len, + NULL); + if (!strv) + return NULL; + + retval = g_new0 (GQuark, len + 1); + + for (i = 0; strv[i]; i++) + retval[i] = g_quark_from_string (strv[i]); + + g_strfreev (strv); + + return retval; +} + +static DesktopEntry* desktop_entry_load(DesktopEntry* entry) +{ + DesktopEntry *retval = NULL; + GKeyFile *key_file; + GError *error; + const char *desktop_entry_group; + char *name_str; + char *type_str; + + key_file = g_key_file_new (); + + error = NULL; + if (!g_key_file_load_from_file (key_file, entry->path, 0, &error)) + { + menu_verbose ("Failed to load \"%s\": %s\n", + entry->path, error->message); + g_error_free (error); + goto out; + } + + if (g_key_file_has_group (key_file, DESKTOP_ENTRY_GROUP)) + { + desktop_entry_group = DESKTOP_ENTRY_GROUP; + } + else + { + menu_verbose ("\"%s\" contains no \"" DESKTOP_ENTRY_GROUP "\" group\n", + entry->path); + + if (g_key_file_has_group (key_file, KDE_DESKTOP_ENTRY_GROUP)) + { + desktop_entry_group = KDE_DESKTOP_ENTRY_GROUP; + menu_verbose ("\"%s\" contains deprecated \"" KDE_DESKTOP_ENTRY_GROUP "\" group\n", + entry->path); + } + else + { + goto out; + } + } + + if (!g_key_file_has_key (key_file, desktop_entry_group, "Name", NULL)) + { + menu_verbose ("\"%s\" contains no \"Name\" key\n", entry->path); + goto out; + } + + name_str = g_key_file_get_locale_string (key_file, desktop_entry_group, "Name", NULL, NULL); + if (!name_str) + { + menu_verbose ("\"%s\" contains an invalid \"Name\" key\n", entry->path); + goto out; + } + + g_free (name_str); + + type_str = g_key_file_get_string (key_file, desktop_entry_group, "Type", NULL); + if (!type_str) + { + menu_verbose ("\"%s\" contains no \"Type\" key\n", entry->path); + goto out; + } + + if ((entry->type == DESKTOP_ENTRY_DESKTOP && strcmp (type_str, "Application") != 0) || + (entry->type == DESKTOP_ENTRY_DIRECTORY && strcmp (type_str, "Directory") != 0)) + { + menu_verbose ("\"%s\" does not contain the correct \"Type\" value\n", entry->path); + g_free (type_str); + goto out; + } + + g_free (type_str); + + if (entry->type == DESKTOP_ENTRY_DESKTOP && + !g_key_file_has_key (key_file, desktop_entry_group, "Exec", NULL)) + { + menu_verbose ("\"%s\" does not contain an \"Exec\" key\n", entry->path); + goto out; + } + + retval = entry; + +#define GET_LOCALE_STRING(n) g_key_file_get_locale_string (key_file, desktop_entry_group, (n), NULL, NULL) + + retval->name = GET_LOCALE_STRING ("Name"); + retval->generic_name = GET_LOCALE_STRING ("GenericName"); + retval->full_name = GET_LOCALE_STRING ("X-MATE-FullName"); + retval->comment = GET_LOCALE_STRING ("Comment"); + retval->icon = GET_LOCALE_STRING ("Icon"); + retval->flags = get_flags_from_key_file (retval, key_file, desktop_entry_group); + retval->categories = get_categories_from_key_file (retval, key_file, desktop_entry_group); + + if (entry->type == DESKTOP_ENTRY_DESKTOP) + { + retval->exec = g_key_file_get_string (key_file, desktop_entry_group, "Exec", NULL); + retval->terminal = g_key_file_get_boolean (key_file, desktop_entry_group, "Terminal", NULL); + } + +#undef GET_LOCALE_STRING + + menu_verbose ("Desktop entry \"%s\" (%s, %s, %s, %s, %s) flags: NoDisplay=%s, Hidden=%s, ShowInMATE=%s, TryExecFailed=%s\n", + retval->basename, + retval->name, + retval->generic_name ? retval->generic_name : "(null)", + retval->full_name ? retval->full_name : "(null)", + retval->comment ? retval->comment : "(null)", + retval->icon ? retval->icon : "(null)", + retval->flags & DESKTOP_ENTRY_NO_DISPLAY ? "(true)" : "(false)", + retval->flags & DESKTOP_ENTRY_HIDDEN ? "(true)" : "(false)", + retval->flags & DESKTOP_ENTRY_SHOW_IN_MATE ? "(true)" : "(false)", + retval->flags & DESKTOP_ENTRY_TRYEXEC_FAILED ? "(true)" : "(false)"); + + out: + g_key_file_free (key_file); + + if (!retval) + desktop_entry_unref (entry); + + return retval; +} + +DesktopEntry* desktop_entry_new(const char* path) +{ + DesktopEntryType type; + DesktopEntry *retval; + + menu_verbose ("Loading desktop entry \"%s\"\n", path); + + if (g_str_has_suffix (path, ".desktop")) + { + type = DESKTOP_ENTRY_DESKTOP; + } + else if (g_str_has_suffix (path, ".directory")) + { + type = DESKTOP_ENTRY_DIRECTORY; + } + else + { + menu_verbose ("Unknown desktop entry suffix in \"%s\"\n", + path); + return NULL; + } + + retval = g_new0 (DesktopEntry, 1); + + retval->refcount = 1; + retval->type = type; + retval->basename = g_path_get_basename (path); + retval->path = g_strdup (path); + + return desktop_entry_load (retval); +} + +DesktopEntry* desktop_entry_reload(DesktopEntry* entry) +{ + g_return_val_if_fail (entry != NULL, NULL); + + menu_verbose ("Re-loading desktop entry \"%s\"\n", entry->path); + + g_free (entry->categories); + entry->categories = NULL; + + g_free (entry->name); + entry->name = NULL; + + g_free (entry->generic_name); + entry->generic_name = NULL; + + g_free (entry->full_name); + entry->full_name = NULL; + + g_free (entry->comment); + entry->comment = NULL; + + g_free (entry->icon); + entry->icon = NULL; + + g_free (entry->exec); + entry->exec = NULL; + + entry->terminal = 0; + entry->flags = 0; + + return desktop_entry_load (entry); +} + +DesktopEntry* desktop_entry_ref(DesktopEntry* entry) +{ + g_return_val_if_fail (entry != NULL, NULL); + g_return_val_if_fail (entry->refcount > 0, NULL); + + entry->refcount += 1; + + return entry; +} + +DesktopEntry* desktop_entry_copy(DesktopEntry* entry) +{ + DesktopEntry *retval; + int i; + + menu_verbose ("Copying desktop entry \"%s\"\n", + entry->basename); + + retval = g_new0 (DesktopEntry, 1); + + retval->refcount = 1; + retval->type = entry->type; + retval->basename = g_strdup (entry->basename); + retval->path = g_strdup (entry->path); + retval->name = g_strdup (entry->name); + retval->generic_name = g_strdup (entry->generic_name); + retval->full_name = g_strdup (entry->full_name); + retval->comment = g_strdup (entry->comment); + retval->icon = g_strdup (entry->icon); + retval->exec = g_strdup (entry->exec); + retval->terminal = entry->terminal; + retval->flags = entry->flags; + + i = 0; + if (entry->categories != NULL) + { + for (; entry->categories[i]; i++); + } + + retval->categories = g_new0 (GQuark, i + 1); + + i = 0; + if (entry->categories != NULL) + { + for (; entry->categories[i]; i++) + retval->categories[i] = entry->categories[i]; + } + + return retval; +} + +void desktop_entry_unref(DesktopEntry* entry) +{ + g_return_if_fail (entry != NULL); + g_return_if_fail (entry->refcount > 0); + + entry->refcount -= 1; + if (entry->refcount == 0) + { + g_free (entry->categories); + entry->categories = NULL; + + g_free (entry->name); + entry->name = NULL; + + g_free (entry->generic_name); + entry->generic_name = NULL; + + g_free (entry->full_name); + entry->full_name = NULL; + + g_free (entry->comment); + entry->comment = NULL; + + g_free (entry->icon); + entry->icon = NULL; + + g_free (entry->exec); + entry->exec = NULL; + + g_free (entry->basename); + entry->basename = NULL; + + g_free (entry->path); + entry->path = NULL; + + g_free (entry); + } +} + +DesktopEntryType desktop_entry_get_type(DesktopEntry* entry) +{ + return entry->type; +} + +const char* desktop_entry_get_path(DesktopEntry* entry) +{ + return entry->path; +} + +const char * +desktop_entry_get_basename (DesktopEntry *entry) +{ + return entry->basename; +} + +const char* desktop_entry_get_name(DesktopEntry* entry) +{ + return entry->name; +} + +const char* desktop_entry_get_generic_name(DesktopEntry* entry) +{ + return entry->generic_name; +} + +const char* desktop_entry_get_full_name(DesktopEntry* entry) +{ + return entry->full_name; +} + +const char* desktop_entry_get_comment(DesktopEntry* entry) +{ + return entry->comment; +} + +const char* desktop_entry_get_icon(DesktopEntry* entry) +{ + return entry->icon; +} + +const char* desktop_entry_get_exec(DesktopEntry* entry) +{ + return entry->exec; +} + +gboolean desktop_entry_get_launch_in_terminal(DesktopEntry* entry) +{ + return entry->terminal; +} + +gboolean desktop_entry_get_hidden(DesktopEntry* entry) +{ + return (entry->flags & DESKTOP_ENTRY_HIDDEN) != 0; +} + +gboolean desktop_entry_get_no_display(DesktopEntry* entry) +{ + return (entry->flags & DESKTOP_ENTRY_NO_DISPLAY) != 0; +} + +gboolean desktop_entry_get_show_in_mate(DesktopEntry* entry) +{ + return (entry->flags & DESKTOP_ENTRY_SHOW_IN_MATE) != 0; +} + +gboolean desktop_entry_get_tryexec_failed(DesktopEntry* entry) +{ + return (entry->flags & DESKTOP_ENTRY_TRYEXEC_FAILED) != 0; +} + +gboolean desktop_entry_has_categories(DesktopEntry* entry) +{ + return (entry->categories != NULL && entry->categories[0] != 0); +} + +gboolean desktop_entry_has_category(DesktopEntry* entry, const char* category) +{ + GQuark quark; + int i; + + if (entry->categories == NULL) + return FALSE; + + if (!(quark = g_quark_try_string (category))) + return FALSE; + + for (i = 0; entry->categories[i]; i++) + { + if (quark == entry->categories[i]) + return TRUE; + } + + return FALSE; +} + +void desktop_entry_add_legacy_category(DesktopEntry* entry) +{ + GQuark *categories; + int i; + + menu_verbose ("Adding Legacy category to \"%s\"\n", + entry->basename); + + i = 0; + if (entry->categories != NULL) + { + for (; entry->categories[i]; i++); + } + + categories = g_new0 (GQuark, i + 2); + + i = 0; + if (entry->categories != NULL) + { + for (; entry->categories[i]; i++) + categories[i] = entry->categories[i]; + } + + categories[i] = g_quark_from_string ("Legacy"); + + g_free (entry->categories); + entry->categories = categories; +} + +/* + * Entry sets + */ + +DesktopEntrySet* desktop_entry_set_new(void) +{ + DesktopEntrySet *set; + + set = g_new0 (DesktopEntrySet, 1); + set->refcount = 1; + + menu_verbose (" New entry set %p\n", set); + + return set; +} + +DesktopEntrySet* desktop_entry_set_ref(DesktopEntrySet* set) +{ + g_return_val_if_fail (set != NULL, NULL); + g_return_val_if_fail (set->refcount > 0, NULL); + + set->refcount += 1; + + return set; +} + +void desktop_entry_set_unref(DesktopEntrySet* set) +{ + g_return_if_fail (set != NULL); + g_return_if_fail (set->refcount > 0); + + set->refcount -= 1; + if (set->refcount == 0) + { + menu_verbose (" Deleting entry set %p\n", set); + + if (set->hash) + g_hash_table_destroy (set->hash); + set->hash = NULL; + + g_free (set); + } +} + +void desktop_entry_set_add_entry(DesktopEntrySet* set, DesktopEntry* entry, const char* file_id) +{ + menu_verbose (" Adding to set %p entry %s\n", set, file_id); + + if (set->hash == NULL) + { + set->hash = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify) desktop_entry_unref); + } + + g_hash_table_replace (set->hash, + g_strdup (file_id), + desktop_entry_ref (entry)); +} + +DesktopEntry* desktop_entry_set_lookup(DesktopEntrySet* set, const char* file_id) +{ + if (set->hash == NULL) + return NULL; + + return g_hash_table_lookup (set->hash, file_id); +} + +typedef struct { + DesktopEntrySetForeachFunc func; + gpointer user_data; +} EntryHashForeachData; + +static void entry_hash_foreach(const char* file_id, DesktopEntry* entry, EntryHashForeachData* fd) +{ + fd->func(file_id, entry, fd->user_data); +} + +void desktop_entry_set_foreach(DesktopEntrySet* set, DesktopEntrySetForeachFunc func, gpointer user_data) +{ + g_return_if_fail (set != NULL); + g_return_if_fail (func != NULL); + + if (set->hash != NULL) + { + EntryHashForeachData fd; + + fd.func = func; + fd.user_data = user_data; + + g_hash_table_foreach (set->hash, + (GHFunc) entry_hash_foreach, + &fd); + } +} + +static void desktop_entry_set_clear(DesktopEntrySet* set) +{ + menu_verbose (" Clearing set %p\n", set); + + if (set->hash != NULL) + { + g_hash_table_destroy (set->hash); + set->hash = NULL; + } +} + +int desktop_entry_set_get_count(DesktopEntrySet* set) +{ + if (set->hash == NULL) + return 0; + + return g_hash_table_size (set->hash); +} + +static void union_foreach(const char* file_id, DesktopEntry* entry, DesktopEntrySet* set) +{ + /* we are iterating over "with" adding anything not + * already in "set". We unconditionally overwrite + * the stuff in "set" because we can assume + * two entries with the same name are equivalent. + */ + desktop_entry_set_add_entry(set, entry, file_id); +} + +void desktop_entry_set_union(DesktopEntrySet* set, DesktopEntrySet* with) +{ + menu_verbose (" Union of %p and %p\n", set, with); + + if (desktop_entry_set_get_count (with) == 0) + return; /* A fast simple case */ + + g_hash_table_foreach (with->hash, + (GHFunc) union_foreach, + set); +} + +typedef struct { + DesktopEntrySet *set; + DesktopEntrySet *with; +} IntersectData; + +static gboolean intersect_foreach_remove(const char* file_id, DesktopEntry* entry, IntersectData* id) +{ + /* Remove everything in "set" which is not in "with" */ + + if (g_hash_table_lookup (id->with->hash, file_id) != NULL) + return FALSE; + + menu_verbose (" Removing from %p entry %s\n", id->set, file_id); + + return TRUE; /* return TRUE to remove */ +} + +void desktop_entry_set_intersection(DesktopEntrySet* set, DesktopEntrySet* with) +{ + IntersectData id; + + menu_verbose (" Intersection of %p and %p\n", set, with); + + if (desktop_entry_set_get_count (set) == 0 || + desktop_entry_set_get_count (with) == 0) + { + /* A fast simple case */ + desktop_entry_set_clear (set); + return; + } + + id.set = set; + id.with = with; + + g_hash_table_foreach_remove (set->hash, + (GHRFunc) intersect_foreach_remove, + &id); +} + +typedef struct { + DesktopEntrySet *set; + DesktopEntrySet *other; +} SubtractData; + +static gboolean subtract_foreach_remove(const char* file_id, DesktopEntry* entry, SubtractData* sd) +{ + /* Remove everything in "set" which is not in "other" */ + + if (g_hash_table_lookup (sd->other->hash, file_id) == NULL) + return FALSE; + + menu_verbose (" Removing from %p entry %s\n", sd->set, file_id); + + return TRUE; /* return TRUE to remove */ +} + +void desktop_entry_set_subtract(DesktopEntrySet* set, DesktopEntrySet* other) +{ + SubtractData sd; + + menu_verbose (" Subtract from %p set %p\n", set, other); + + if (desktop_entry_set_get_count (set) == 0 || + desktop_entry_set_get_count (other) == 0) + return; /* A fast simple case */ + + sd.set = set; + sd.other = other; + + g_hash_table_foreach_remove (set->hash, + (GHRFunc) subtract_foreach_remove, + &sd); +} + +void desktop_entry_set_swap_contents(DesktopEntrySet* a, DesktopEntrySet* b) +{ + GHashTable *tmp; + + menu_verbose (" Swap contents of %p and %p\n", a, b); + + tmp = a->hash; + a->hash = b->hash; + b->hash = tmp; +} |