1 /* GIO - GLib Input, Output and Streaming Library
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 * Copyright © 2007 Ryan Lortie
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General
17 * Public License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
19 * Boston, MA 02111-1307, USA.
21 * Author: Alexander Larsson <alexl@redhat.com>
31 #ifdef HAVE_CRT_EXTERNS_H
32 #include <crt_externs.h>
35 #include "gcontenttypeprivate.h"
36 #include "gdesktopappinfo.h"
39 #include "gthemedicon.h"
40 #include "gfileicon.h"
41 #include <glib/gstdio.h>
43 #include "giomodule-priv.h"
49 * SECTION:gdesktopappinfo
50 * @short_description: Application information from desktop files
51 * @include: gio/gdesktopappinfo.h
53 * #GDesktopAppInfo is an implementation of #GAppInfo based on
58 #define DEFAULT_APPLICATIONS_GROUP "Default Applications"
59 #define ADDED_ASSOCIATIONS_GROUP "Added Associations"
60 #define REMOVED_ASSOCIATIONS_GROUP "Removed Associations"
61 #define MIME_CACHE_GROUP "MIME Cache"
63 static void g_desktop_app_info_iface_init (GAppInfoIface *iface);
64 static GList * get_all_desktop_entries_for_mime_type (const char *base_mime_type,
66 static void mime_info_cache_reload (const char *dir);
67 static gboolean g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
73 * Information about an installed application from a desktop file.
75 struct _GDesktopAppInfo
77 GObject parent_instance;
83 /* FIXME: what about GenericName ? */
97 guint startup_notify : 1;
98 /* FIXME: what about StartupWMClass ? */
101 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT,
102 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
103 g_desktop_app_info_iface_init))
106 search_path_init (gpointer data)
109 const char * const *data_dirs;
110 const char *user_data_dir;
113 data_dirs = g_get_system_data_dirs ();
114 length = g_strv_length ((char **) data_dirs);
116 args = g_new (char *, length + 2);
119 user_data_dir = g_get_user_data_dir ();
120 args[j++] = g_build_filename (user_data_dir, "applications", NULL);
121 for (i = 0; i < length; i++)
122 args[j++] = g_build_filename (data_dirs[i],
123 "applications", NULL);
129 static const char * const *
130 get_applications_search_path (void)
132 static GOnce once_init = G_ONCE_INIT;
133 return g_once (&once_init, search_path_init, NULL);
137 g_desktop_app_info_finalize (GObject *object)
139 GDesktopAppInfo *info;
141 info = G_DESKTOP_APP_INFO (object);
143 g_free (info->desktop_id);
144 g_free (info->filename);
146 g_free (info->comment);
147 g_free (info->icon_name);
149 g_object_unref (info->icon);
150 g_strfreev (info->only_show_in);
151 g_strfreev (info->not_show_in);
152 g_free (info->try_exec);
154 g_free (info->binary);
157 G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
161 g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
163 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
165 gobject_class->finalize = g_desktop_app_info_finalize;
169 g_desktop_app_info_init (GDesktopAppInfo *local)
174 binary_from_exec (const char *exec)
176 const char *p, *start;
182 while (*p != ' ' && *p != 0)
185 return g_strndup (start, p - start);
190 * g_desktop_app_info_new_from_keyfile:
191 * @key_file: an opened #GKeyFile
193 * Creates a new #GDesktopAppInfo.
195 * Returns: a new #GDesktopAppInfo or %NULL on error.
200 g_desktop_app_info_new_from_keyfile (GKeyFile *key_file)
202 GDesktopAppInfo *info;
207 start_group = g_key_file_get_start_group (key_file);
208 if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
210 g_free (start_group);
213 g_free (start_group);
215 type = g_key_file_get_string (key_file,
216 G_KEY_FILE_DESKTOP_GROUP,
217 G_KEY_FILE_DESKTOP_KEY_TYPE,
219 if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
226 try_exec = g_key_file_get_string (key_file,
227 G_KEY_FILE_DESKTOP_GROUP,
228 G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
230 if (try_exec && try_exec[0] != '\0')
233 t = g_find_program_in_path (try_exec);
242 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
243 info->filename = NULL;
245 info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
246 info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
247 info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
248 info->icon_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
249 info->only_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL, NULL);
250 info->not_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL, NULL);
251 info->try_exec = try_exec;
252 info->exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
253 info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
254 info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
255 info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
256 info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
261 if (g_path_is_absolute (info->icon_name))
265 file = g_file_new_for_path (info->icon_name);
266 info->icon = g_file_icon_new (file);
267 g_object_unref (file);
273 /* Work around a common mistake in desktop files */
274 if ((p = strrchr (info->icon_name, '.')) != NULL &&
275 (strcmp (p, ".png") == 0 ||
276 strcmp (p, ".xpm") == 0 ||
277 strcmp (p, ".svg") == 0))
280 info->icon = g_themed_icon_new (info->icon_name);
285 info->binary = binary_from_exec (info->exec);
287 if (info->path && info->path[0] == '\0')
297 * g_desktop_app_info_new_from_filename:
298 * @filename: the path of a desktop file, in the GLib filename encoding
300 * Creates a new #GDesktopAppInfo.
302 * Returns: a new #GDesktopAppInfo or %NULL on error.
305 g_desktop_app_info_new_from_filename (const char *filename)
308 GDesktopAppInfo *info = NULL;
310 key_file = g_key_file_new ();
312 if (g_key_file_load_from_file (key_file,
317 info = g_desktop_app_info_new_from_keyfile (key_file);
319 info->filename = g_strdup (filename);
322 g_key_file_free (key_file);
328 * g_desktop_app_info_new:
329 * @desktop_id: the desktop file id
331 * Creates a new #GDesktopAppInfo.
333 * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
336 g_desktop_app_info_new (const char *desktop_id)
338 GDesktopAppInfo *appinfo;
339 const char * const *dirs;
343 dirs = get_applications_search_path ();
345 basename = g_strdup (desktop_id);
347 for (i = 0; dirs[i] != NULL; i++)
352 filename = g_build_filename (dirs[i], desktop_id, NULL);
353 appinfo = g_desktop_app_info_new_from_filename (filename);
359 while ((p = strchr (p, '-')) != NULL)
363 filename = g_build_filename (dirs[i], basename, NULL);
364 appinfo = g_desktop_app_info_new_from_filename (filename);
379 appinfo->desktop_id = g_strdup (desktop_id);
381 if (g_desktop_app_info_get_is_hidden (appinfo))
383 g_object_unref (appinfo);
391 g_desktop_app_info_dup (GAppInfo *appinfo)
393 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
394 GDesktopAppInfo *new_info;
396 new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
398 new_info->filename = g_strdup (info->filename);
399 new_info->desktop_id = g_strdup (info->desktop_id);
401 new_info->name = g_strdup (info->name);
402 new_info->comment = g_strdup (info->comment);
403 new_info->nodisplay = info->nodisplay;
404 new_info->icon_name = g_strdup (info->icon_name);
405 new_info->icon = g_object_ref (info->icon);
406 new_info->only_show_in = g_strdupv (info->only_show_in);
407 new_info->not_show_in = g_strdupv (info->not_show_in);
408 new_info->try_exec = g_strdup (info->try_exec);
409 new_info->exec = g_strdup (info->exec);
410 new_info->binary = g_strdup (info->binary);
411 new_info->path = g_strdup (info->path);
412 new_info->hidden = info->hidden;
413 new_info->terminal = info->terminal;
414 new_info->startup_notify = info->startup_notify;
416 return G_APP_INFO (new_info);
420 g_desktop_app_info_equal (GAppInfo *appinfo1,
423 GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
424 GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
426 if (info1->desktop_id == NULL ||
427 info2->desktop_id == NULL)
428 return info1 == info2;
430 return strcmp (info1->desktop_id, info2->desktop_id) == 0;
434 g_desktop_app_info_get_id (GAppInfo *appinfo)
436 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
438 return info->desktop_id;
442 g_desktop_app_info_get_name (GAppInfo *appinfo)
444 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
446 if (info->name == NULL)
452 * g_desktop_app_info_get_is_hidden:
453 * @info: a #GDesktopAppInfo.
455 * A desktop file is hidden if the Hidden key in it is
458 * Returns: %TRUE if hidden, %FALSE otherwise.
461 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
467 g_desktop_app_info_get_description (GAppInfo *appinfo)
469 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
471 return info->comment;
475 g_desktop_app_info_get_executable (GAppInfo *appinfo)
477 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
483 g_desktop_app_info_get_icon (GAppInfo *appinfo)
485 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
491 expand_macro_single (char macro, char *uri)
497 file = g_file_new_for_uri (uri);
498 path = g_file_get_path (file);
499 g_object_unref (file);
505 result = g_shell_quote (uri);
510 result = g_shell_quote (path);
516 name = g_path_get_dirname (path);
517 result = g_shell_quote (name);
525 name = g_path_get_basename (path);
526 result = g_shell_quote (name);
538 expand_macro (char macro,
540 GDesktopAppInfo *info,
543 GList *uris = *uri_list;
545 gboolean force_file_uri;
546 char force_file_uri_macro;
548 g_return_if_fail (exec != NULL);
550 /* On %u and %U, pass POSIX file path pointing to the URI via
551 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
552 * running or the URI doesn't have a POSIX file path via FUSE
553 * we'll just pass the URI.
558 force_file_uri_macro = 'f';
559 force_file_uri = TRUE;
562 force_file_uri_macro = 'F';
563 force_file_uri = TRUE;
566 force_file_uri_macro = macro;
567 force_file_uri = FALSE;
581 expanded = expand_macro_single (macro, uris->data);
585 expanded = expand_macro_single (force_file_uri_macro, uris->data);
586 if (expanded == NULL)
587 expanded = expand_macro_single (macro, uris->data);
592 g_string_append (exec, expanded);
608 expanded = expand_macro_single (macro, uris->data);
612 expanded = expand_macro_single (force_file_uri_macro, uris->data);
613 if (expanded == NULL)
614 expanded = expand_macro_single (macro, uris->data);
619 g_string_append (exec, expanded);
625 if (uris != NULL && expanded)
626 g_string_append_c (exec, ' ');
634 g_string_append (exec, "--icon ");
635 g_string_append (exec, info->icon_name);
641 g_string_append (exec, info->name);
646 g_string_append (exec, info->filename);
649 case 'm': /* deprecated */
653 g_string_append_c (exec, '%');
661 expand_application_parameters (GDesktopAppInfo *info,
667 GList *uri_list = *uris;
668 const char *p = info->exec;
669 GString *expanded_exec = g_string_new (NULL);
672 if (info->exec == NULL)
674 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
675 _("Desktop file didn't specify Exec field"));
681 if (p[0] == '%' && p[1] != '\0')
683 expand_macro (p[1], expanded_exec, info, uris);
687 g_string_append_c (expanded_exec, *p);
692 /* No file substitutions */
693 if (uri_list == *uris && uri_list != NULL)
695 /* If there is no macro default to %f. This is also what KDE does */
696 g_string_append_c (expanded_exec, ' ');
697 expand_macro ('f', expanded_exec, info, uris);
700 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
701 g_string_free (expanded_exec, TRUE);
706 prepend_terminal_to_vector (int *argc,
713 char **term_argv = NULL;
718 g_return_val_if_fail (argc != NULL, FALSE);
719 g_return_val_if_fail (argv != NULL, FALSE);
727 /* compute size if not given */
730 for (i = 0; the_argv[i] != NULL; i++)
736 term_argv = g_new0 (char *, 3);
738 check = g_find_program_in_path ("gnome-terminal");
741 term_argv[0] = check;
742 /* Note that gnome-terminal takes -x and
743 * as -e in gnome-terminal is broken we use that. */
744 term_argv[1] = g_strdup ("-x");
749 check = g_find_program_in_path ("nxterm");
751 check = g_find_program_in_path ("color-xterm");
753 check = g_find_program_in_path ("rxvt");
755 check = g_find_program_in_path ("xterm");
757 check = g_find_program_in_path ("dtterm");
760 check = g_strdup ("xterm");
761 g_warning ("couldn't find a terminal, falling back to xterm");
763 term_argv[0] = check;
764 term_argv[1] = g_strdup ("-e");
767 real_argc = term_argc + *argc;
768 real_argv = g_new (char *, real_argc + 1);
770 for (i = 0; i < term_argc; i++)
771 real_argv[i] = term_argv[i];
773 for (j = 0; j < *argc; j++, i++)
774 real_argv[i] = (char *)the_argv[j];
782 /* we use g_free here as we sucked all the inner strings
783 * out from it into real_argv */
788 #endif /* G_OS_WIN32 */
791 /* '=' is the new '\0'.
792 * DO NOT CALL unless at least one string ends with '='
795 is_env (const char *a,
800 if (*a == 0 || *b == 0)
813 /* free with g_strfreev */
815 replace_env_var (char **old_environ,
817 const char *new_value)
819 int length, new_length;
820 int index, new_index;
824 /* do two things at once:
825 * - discover the length of the environment ('length')
826 * - find the location (if any) of the env var ('index')
829 for (length = 0; old_environ[length]; length++)
831 /* if we already have it in our environment, replace */
832 if (is_env (old_environ[length], env_var))
837 /* no current env var, no desired env value.
840 if (new_value == NULL && index == -1)
843 /* in all cases now, we will be using a modified environment.
844 * determine its length and allocated it.
847 * new_index = location to insert, if any
848 * new_length = length of the new array
849 * new_environ = the pointer array for the new environment
852 if (new_value == NULL && index >= 0)
854 /* in this case, we will be removing an entry */
855 new_length = length - 1;
858 else if (new_value != NULL && index < 0)
860 /* in this case, we will be adding an entry to the end */
861 new_length = length + 1;
865 /* in this case, we will be replacing the existing entry */
871 new_environ = g_malloc (sizeof (char *) * (new_length + 1));
872 new_environ[new_length] = NULL;
874 /* now we do the copying.
875 * for each entry in the new environment, we decide what to do
879 for (new_i = 0; new_i < new_length; new_i++)
881 if (new_i == new_index)
883 /* insert our new item */
884 new_environ[new_i] = g_strconcat (env_var,
889 /* if we had an old entry, skip it now */
895 /* if this is the old DESKTOP_STARTUP_ID, skip it */
899 /* copy an old item */
900 new_environ[new_i] = g_strdup (old_environ[i]);
905 g_strfreev (old_environ);
911 uri_list_segment_to_files (GList *start,
918 while (start != NULL && start != end)
920 file = g_file_new_for_uri ((char *)start->data);
921 res = g_list_prepend (res, file);
925 return g_list_reverse (res);
928 #ifdef HAVE__NSGETENVIRON
929 #define environ (*_NSGetEnviron())
930 #elif !defined(G_OS_WIN32)
932 /* According to the Single Unix Specification, environ is not in
933 * * any system header, although unistd.h often declares it.
935 extern char **environ;
939 g_desktop_app_info_launch_uris (GAppInfo *appinfo,
941 GAppLaunchContext *launch_context,
944 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
945 gboolean completed = FALSE;
947 GList *launched_files;
954 g_return_val_if_fail (appinfo != NULL, FALSE);
962 if (!expand_application_parameters (info, &uris,
963 &argc, &argv, error))
966 if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
968 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
969 _("Unable to find terminal required for application"));
976 launched_files = uri_list_segment_to_files (old_uris, uris);
978 display = g_app_launch_context_get_display (launch_context,
983 if (info->startup_notify)
984 sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
988 if (display || sn_id)
992 envp = g_new0 (char *, 1);
994 envp = g_strdupv (environ);
998 envp = replace_env_var (envp,
1003 envp = replace_env_var (envp,
1004 "DESKTOP_STARTUP_ID",
1010 g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
1011 g_list_free (launched_files);
1014 if (!g_spawn_async (info->path, /* working directory */
1017 G_SPAWN_SEARCH_PATH /* flags */,
1018 NULL /* child_setup */,
1020 NULL /* child_pid */,
1025 g_app_launch_context_launch_failed (launch_context, sn_id);
1039 while (uris != NULL);
1051 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
1053 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1055 return info->exec &&
1056 ((strstr (info->exec, "%u") != NULL) ||
1057 (strstr (info->exec, "%U") != NULL));
1061 g_desktop_app_info_supports_files (GAppInfo *appinfo)
1063 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1065 return info->exec &&
1066 ((strstr (info->exec, "%f") != NULL) ||
1067 (strstr (info->exec, "%F") != NULL));
1071 g_desktop_app_info_launch (GAppInfo *appinfo,
1073 GAppLaunchContext *launch_context,
1083 uri = g_file_get_uri (files->data);
1084 uris = g_list_prepend (uris, uri);
1085 files = files->next;
1088 uris = g_list_reverse (uris);
1090 res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
1092 g_list_foreach (uris, (GFunc)g_free, NULL);
1098 G_LOCK_DEFINE_STATIC (g_desktop_env);
1099 static gchar *g_desktop_env = NULL;
1102 * g_desktop_app_info_set_desktop_env:
1103 * @desktop_env: a string specifying what desktop this is
1105 * Sets the name of the desktop that the application is running in.
1106 * This is used by g_app_info_should_show() to evaluate the
1107 * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
1108 * desktop entry fields.
1110 * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop
1111 * Menu specification</ulink> recognizes the following:
1113 * <member>GNOME</member>
1114 * <member>KDE</member>
1115 * <member>ROX</member>
1116 * <member>XFCE</member>
1117 * <member>Old</member>
1120 * Should be called only once; subsequent calls are ignored.
1123 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
1125 G_LOCK (g_desktop_env);
1127 g_desktop_env = g_strdup (desktop_env);
1128 G_UNLOCK (g_desktop_env);
1132 g_desktop_app_info_should_show (GAppInfo *appinfo)
1134 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1136 const gchar *desktop_env;
1139 if (info->nodisplay)
1142 G_LOCK (g_desktop_env);
1143 desktop_env = g_desktop_env;
1144 G_UNLOCK (g_desktop_env);
1146 if (info->only_show_in)
1148 if (desktop_env == NULL)
1152 for (i = 0; info->only_show_in[i] != NULL; i++)
1154 if (strcmp (info->only_show_in[i], desktop_env) == 0)
1164 if (info->not_show_in && desktop_env)
1166 for (i = 0; info->not_show_in[i] != NULL; i++)
1168 if (strcmp (info->not_show_in[i], desktop_env) == 0)
1182 ensure_dir (DirType type,
1185 char *path, *display_name;
1188 if (type == APP_DIR)
1189 path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
1191 path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
1194 if (g_mkdir_with_parents (path, 0700) == 0)
1198 display_name = g_filename_display_name (path);
1199 if (type == APP_DIR)
1200 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1201 _("Can't create user application configuration folder %s: %s"),
1202 display_name, g_strerror (errsv));
1204 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1205 _("Can't create user MIME configuration folder %s: %s"),
1206 display_name, g_strerror (errsv));
1208 g_free (display_name);
1215 update_mimeapps_list (const char *desktop_id,
1216 const char *content_type,
1217 gboolean add_as_default,
1218 gboolean add_non_default,
1222 char *dirname, *filename;
1224 gboolean load_succeeded, res;
1225 char **old_list, **list;
1226 GList *system_list, *l;
1227 gsize length, data_size;
1230 char **content_types;
1232 /* Don't add both at start and end */
1233 g_assert (!(add_as_default && add_non_default));
1235 dirname = ensure_dir (APP_DIR, error);
1239 filename = g_build_filename (dirname, "mimeapps.list", NULL);
1242 key_file = g_key_file_new ();
1243 load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
1244 if (!load_succeeded || !g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP))
1246 g_key_file_free (key_file);
1247 key_file = g_key_file_new ();
1252 content_types = g_new (char *, 2);
1253 content_types[0] = g_strdup (content_type);
1254 content_types[1] = NULL;
1258 content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
1261 for (k = 0; content_types && content_types[k]; k++)
1263 /* Add to the right place in the list */
1266 old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
1267 content_types[k], &length, NULL);
1269 list = g_new (char *, 1 + length + 1);
1273 list[i++] = g_strdup (desktop_id);
1276 for (j = 0; old_list[j] != NULL; j++)
1278 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1279 list[i++] = g_strdup (old_list[j]);
1280 else if (add_non_default)
1282 /* If adding as non-default, and its already in,
1283 don't change order of desktop ids */
1284 add_non_default = FALSE;
1285 list[i++] = g_strdup (old_list[j]);
1290 if (add_non_default)
1292 /* We're adding as non-default, and it wasn't already in the list,
1293 so we add at the end. But to avoid listing the app before the
1294 current system default (thus changing the default) we have to
1295 add the current list of (not yet listed) apps before it. */
1297 list[i] = NULL; /* Terminate current list so we can use it */
1298 system_list = get_all_desktop_entries_for_mime_type (content_type, list);
1300 list = g_renew (char *, list, 1 + length + g_list_length (system_list) + 1);
1302 for (l = system_list; l != NULL; l = l->next)
1304 list[i++] = l->data; /* no strdup, taking ownership */
1305 if (g_strcmp0 (l->data, desktop_id) == 0)
1306 add_non_default = FALSE;
1308 g_list_free (system_list);
1310 if (add_non_default)
1311 list[i++] = g_strdup (desktop_id);
1316 g_strfreev (old_list);
1318 if (list[0] == NULL || desktop_id == NULL)
1319 g_key_file_remove_key (key_file,
1320 ADDED_ASSOCIATIONS_GROUP,
1324 g_key_file_set_string_list (key_file,
1325 ADDED_ASSOCIATIONS_GROUP,
1327 (const char * const *)list, i);
1334 /* reuse the list from above */
1338 g_strfreev (content_types);
1339 content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
1342 for (k = 0; content_types && content_types[k]; k++)
1344 /* Remove from removed associations group (unless remove) */
1347 old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
1348 content_types[k], &length, NULL);
1350 list = g_new (char *, 1 + length + 1);
1354 list[i++] = g_strdup (desktop_id);
1357 for (j = 0; old_list[j] != NULL; j++)
1359 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1360 list[i++] = g_strdup (old_list[j]);
1365 g_strfreev (old_list);
1367 if (list[0] == NULL || desktop_id == NULL)
1368 g_key_file_remove_key (key_file,
1369 REMOVED_ASSOCIATIONS_GROUP,
1373 g_key_file_set_string_list (key_file,
1374 REMOVED_ASSOCIATIONS_GROUP,
1376 (const char * const *)list, i);
1381 g_strfreev (content_types);
1383 data = g_key_file_to_data (key_file, &data_size, error);
1384 g_key_file_free (key_file);
1386 res = g_file_set_contents (filename, data, data_size, error);
1388 mime_info_cache_reload (NULL);
1397 g_desktop_app_info_set_as_default_for_type (GAppInfo *appinfo,
1398 const char *content_type,
1401 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1403 if (!g_desktop_app_info_ensure_saved (info, error))
1406 return update_mimeapps_list (info->desktop_id, content_type, TRUE, FALSE, FALSE, error);
1410 update_program_done (GPid pid,
1414 /* Did the application exit correctly */
1415 if (WIFEXITED (status) &&
1416 WEXITSTATUS (status) == 0)
1418 /* Here we could clean out any caches in use */
1423 run_update_command (char *command,
1432 GError *error = NULL;
1435 argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
1437 if (g_spawn_async ("/", argv,
1439 G_SPAWN_SEARCH_PATH |
1440 G_SPAWN_STDOUT_TO_DEV_NULL |
1441 G_SPAWN_STDERR_TO_DEV_NULL |
1442 G_SPAWN_DO_NOT_REAP_CHILD,
1443 NULL, NULL, /* No setup function */
1446 g_child_watch_add (pid, update_program_done, NULL);
1449 /* If we get an error at this point, it's quite likely the user doesn't
1450 * have an installed copy of either 'update-mime-database' or
1451 * 'update-desktop-database'. I don't think we want to popup an error
1452 * dialog at this point, so we just do a g_warning to give the user a
1453 * chance of debugging it.
1455 g_warning ("%s", error->message);
1462 g_desktop_app_info_set_as_default_for_extension (GAppInfo *appinfo,
1463 const char *extension,
1466 char *filename, *basename, *mimetype;
1470 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
1473 dirname = ensure_dir (MIMETYPE_DIR, error);
1477 basename = g_strdup_printf ("user-extension-%s.xml", extension);
1478 filename = g_build_filename (dirname, basename, NULL);
1482 mimetype = g_strdup_printf ("application/x-extension-%s", extension);
1484 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1489 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1490 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
1491 " <mime-type type=\"%s\">\n"
1492 " <comment>%s document</comment>\n"
1493 " <glob pattern=\"*.%s\"/>\n"
1495 "</mime-info>\n", mimetype, extension, extension);
1497 g_file_set_contents (filename, contents, -1, NULL);
1500 run_update_command ("update-mime-database", "mime");
1504 res = g_desktop_app_info_set_as_default_for_type (appinfo,
1514 g_desktop_app_info_add_supports_type (GAppInfo *appinfo,
1515 const char *content_type,
1518 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1520 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1523 return update_mimeapps_list (info->desktop_id, content_type, FALSE, TRUE, FALSE, error);
1527 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
1533 g_desktop_app_info_remove_supports_type (GAppInfo *appinfo,
1534 const char *content_type,
1537 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1539 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1542 return update_mimeapps_list (info->desktop_id, content_type, FALSE, FALSE, TRUE, error);
1546 g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
1552 char *data, *desktop_id;
1557 if (info->filename != NULL)
1560 /* This is only used for object created with
1561 * g_app_info_create_from_commandline. All other
1562 * object should have a filename
1565 dirname = ensure_dir (APP_DIR, error);
1569 key_file = g_key_file_new ();
1571 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1572 "Encoding", "UTF-8");
1573 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1574 G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
1575 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1576 G_KEY_FILE_DESKTOP_KEY_TYPE,
1577 G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
1579 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1580 G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
1582 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1583 G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
1585 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1586 G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
1588 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1589 G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
1591 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1592 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
1594 data = g_key_file_to_data (key_file, &data_size, NULL);
1595 g_key_file_free (key_file);
1597 desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
1598 filename = g_build_filename (dirname, desktop_id, NULL);
1599 g_free (desktop_id);
1602 fd = g_mkstemp (filename);
1607 display_name = g_filename_display_name (filename);
1608 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1609 _("Can't create user desktop file %s"), display_name);
1610 g_free (display_name);
1616 desktop_id = g_path_get_basename (filename);
1620 res = g_file_set_contents (filename, data, data_size, error);
1623 g_free (desktop_id);
1628 info->filename = filename;
1629 info->desktop_id = desktop_id;
1631 run_update_command ("update-desktop-database", "applications");
1637 g_desktop_app_info_can_delete (GAppInfo *appinfo)
1639 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1643 if (strstr (info->filename, "/userapp-"))
1644 return g_access (info->filename, W_OK) == 0;
1651 g_desktop_app_info_delete (GAppInfo *appinfo)
1653 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1657 if (g_remove (info->filename) == 0)
1659 update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL);
1661 g_free (info->filename);
1662 info->filename = NULL;
1663 g_free (info->desktop_id);
1664 info->desktop_id = NULL;
1674 * g_app_info_create_from_commandline:
1675 * @commandline: the commandline to use
1676 * @application_name: the application name, or %NULL to use @commandline
1677 * @flags: flags that can specify details of the created #GAppInfo
1678 * @error: a #GError location to store the error occuring, %NULL to ignore.
1680 * Creates a new #GAppInfo from the given information.
1682 * Returns: new #GAppInfo for given command.
1685 g_app_info_create_from_commandline (const char *commandline,
1686 const char *application_name,
1687 GAppInfoCreateFlags flags,
1692 GDesktopAppInfo *info;
1694 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1696 info->filename = NULL;
1697 info->desktop_id = NULL;
1699 info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL;
1700 info->startup_notify = FALSE;
1701 info->hidden = FALSE;
1702 if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS)
1703 info->exec = g_strconcat (commandline, " %u", NULL);
1705 info->exec = g_strconcat (commandline, " %f", NULL);
1706 info->nodisplay = TRUE;
1707 info->binary = binary_from_exec (info->exec);
1709 if (application_name)
1710 info->name = g_strdup (application_name);
1713 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
1714 split = g_strsplit (commandline, " ", 2);
1715 basename = g_path_get_basename (split[0]);
1717 info->name = basename;
1718 if (info->name == NULL)
1719 info->name = g_strdup ("custom");
1721 info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
1723 return G_APP_INFO (info);
1727 g_desktop_app_info_iface_init (GAppInfoIface *iface)
1729 iface->dup = g_desktop_app_info_dup;
1730 iface->equal = g_desktop_app_info_equal;
1731 iface->get_id = g_desktop_app_info_get_id;
1732 iface->get_name = g_desktop_app_info_get_name;
1733 iface->get_description = g_desktop_app_info_get_description;
1734 iface->get_executable = g_desktop_app_info_get_executable;
1735 iface->get_icon = g_desktop_app_info_get_icon;
1736 iface->launch = g_desktop_app_info_launch;
1737 iface->supports_uris = g_desktop_app_info_supports_uris;
1738 iface->supports_files = g_desktop_app_info_supports_files;
1739 iface->launch_uris = g_desktop_app_info_launch_uris;
1740 iface->should_show = g_desktop_app_info_should_show;
1741 iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
1742 iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
1743 iface->add_supports_type = g_desktop_app_info_add_supports_type;
1744 iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
1745 iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
1746 iface->can_delete = g_desktop_app_info_can_delete;
1747 iface->do_delete = g_desktop_app_info_delete;
1751 app_info_in_list (GAppInfo *info,
1754 while (list != NULL)
1756 if (g_app_info_equal (info, list->data))
1765 * g_app_info_get_all_for_type:
1766 * @content_type: the content type to find a #GAppInfo for
1768 * Gets a list of all #GAppInfo s for a given content type.
1770 * Returns: #GList of #GAppInfo s for given @content_type
1771 * or %NULL on error.
1774 g_app_info_get_all_for_type (const char *content_type)
1776 GList *desktop_entries, *l;
1778 GDesktopAppInfo *info;
1780 g_return_val_if_fail (content_type != NULL, NULL);
1782 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1785 for (l = desktop_entries; l != NULL; l = l->next)
1787 char *desktop_entry = l->data;
1789 info = g_desktop_app_info_new (desktop_entry);
1792 if (app_info_in_list (G_APP_INFO (info), infos))
1793 g_object_unref (info);
1795 infos = g_list_prepend (infos, info);
1797 g_free (desktop_entry);
1800 g_list_free (desktop_entries);
1802 return g_list_reverse (infos);
1806 * g_app_info_reset_type_associations:
1807 * @content_type: a content type
1809 * Removes all changes to the type associations done by
1810 * g_app_info_set_as_default_for_type(),
1811 * g_app_info_set_as_default_for_extension(),
1812 * g_app_info_add_supports_type() of g_app_info_remove_supports_type().
1817 g_app_info_reset_type_associations (const char *content_type)
1819 update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
1823 * g_app_info_get_default_for_type:
1824 * @content_type: the content type to find a #GAppInfo for
1825 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
1828 * Gets the #GAppInfo that correspond to a given content type.
1830 * Returns: #GAppInfo for given @content_type or %NULL on error.
1833 g_app_info_get_default_for_type (const char *content_type,
1834 gboolean must_support_uris)
1836 GList *desktop_entries, *l;
1839 g_return_val_if_fail (content_type != NULL, NULL);
1841 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1844 for (l = desktop_entries; l != NULL; l = l->next)
1846 char *desktop_entry = l->data;
1848 info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
1851 if (must_support_uris && !g_app_info_supports_uris (info))
1853 g_object_unref (info);
1861 g_list_foreach (desktop_entries, (GFunc)g_free, NULL);
1862 g_list_free (desktop_entries);
1868 * g_app_info_get_default_for_uri_scheme:
1869 * @uri_scheme: a string containing a URI scheme.
1871 * Gets the default application for launching applications
1872 * using this URI scheme. A URI scheme is the initial part
1873 * of the URI, up to but not including the ':', e.g. "http",
1876 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
1879 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
1881 static gsize lookup = 0;
1883 if (g_once_init_enter (&lookup))
1885 gsize setup_value = 1;
1886 GDesktopAppInfoLookup *lookup_instance;
1887 const char *use_this;
1888 GIOExtensionPoint *ep;
1889 GIOExtension *extension;
1892 use_this = g_getenv ("GIO_USE_URI_ASSOCIATION");
1894 /* Ensure vfs in modules loaded */
1895 _g_io_modules_ensure_loaded ();
1897 ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME);
1899 lookup_instance = NULL;
1902 extension = g_io_extension_point_get_extension_by_name (ep, use_this);
1904 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1907 if (lookup_instance == NULL)
1909 for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
1911 extension = l->data;
1912 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1913 if (lookup_instance != NULL)
1918 if (lookup_instance != NULL)
1919 setup_value = (gsize)lookup_instance;
1921 g_once_init_leave (&lookup, setup_value);
1927 return g_desktop_app_info_lookup_get_default_for_uri_scheme (G_DESKTOP_APP_INFO_LOOKUP (lookup),
1933 get_apps_from_dir (GHashTable *apps,
1934 const char *dirname,
1938 const char *basename;
1939 char *filename, *subprefix, *desktop_id;
1941 GDesktopAppInfo *appinfo;
1943 dir = g_dir_open (dirname, 0, NULL);
1946 while ((basename = g_dir_read_name (dir)) != NULL)
1948 filename = g_build_filename (dirname, basename, NULL);
1949 if (g_str_has_suffix (basename, ".desktop"))
1951 desktop_id = g_strconcat (prefix, basename, NULL);
1953 /* Use _extended so we catch NULLs too (hidden) */
1954 if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
1956 appinfo = g_desktop_app_info_new_from_filename (filename);
1958 if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
1960 g_object_unref (appinfo);
1965 if (appinfo || hidden)
1967 g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
1971 /* Reuse instead of strdup here */
1972 appinfo->desktop_id = desktop_id;
1977 g_free (desktop_id);
1981 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
1983 subprefix = g_strconcat (prefix, basename, "-", NULL);
1984 get_apps_from_dir (apps, filename, subprefix);
1996 * g_app_info_get_all:
1998 * Gets a list of all of the applications currently registered
2001 * For desktop files, this includes applications that have
2002 * <literal>NoDisplay=true</literal> set or are excluded from
2003 * display by means of <literal>OnlyShowIn</literal> or
2004 * <literal>NotShowIn</literal>. See g_app_info_should_show().
2005 * The returned list does not include applications which have
2006 * the <literal>Hidden</literal> key set.
2008 * Returns: a newly allocated #GList of references to #GAppInfo<!---->s.
2011 g_app_info_get_all (void)
2013 const char * const *dirs;
2015 GHashTableIter iter;
2020 dirs = get_applications_search_path ();
2022 apps = g_hash_table_new_full (g_str_hash, g_str_equal,
2026 for (i = 0; dirs[i] != NULL; i++)
2027 get_apps_from_dir (apps, dirs[i], "");
2031 g_hash_table_iter_init (&iter, apps);
2032 while (g_hash_table_iter_next (&iter, NULL, &value))
2035 infos = g_list_prepend (infos, value);
2038 g_hash_table_destroy (apps);
2040 return g_list_reverse (infos);
2043 /* Cacheing of mimeinfo.cache and defaults.list files */
2047 GHashTable *mime_info_cache_map;
2048 GHashTable *defaults_list_map;
2049 GHashTable *mimeapps_list_added_map;
2050 GHashTable *mimeapps_list_removed_map;
2051 time_t mime_info_cache_timestamp;
2052 time_t defaults_list_timestamp;
2053 time_t mimeapps_list_timestamp;
2057 GList *dirs; /* mimeinfo.cache and defaults.list */
2058 GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
2059 time_t last_stat_time;
2060 guint should_ping_mime_monitor : 1;
2063 static MimeInfoCache *mime_info_cache = NULL;
2064 G_LOCK_DEFINE_STATIC (mime_info_cache);
2066 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2067 const char *mime_type,
2068 char **new_desktop_file_ids);
2070 static MimeInfoCache * mime_info_cache_new (void);
2073 destroy_info_cache_value (gpointer key,
2077 g_list_foreach (value, (GFunc)g_free, NULL);
2078 g_list_free (value);
2082 destroy_info_cache_map (GHashTable *info_cache_map)
2084 g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
2085 g_hash_table_destroy (info_cache_map);
2089 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
2090 const char *cache_file,
2096 filename = g_build_filename (dir->path, cache_file, NULL);
2098 if (g_stat (filename, &buf) < 0)
2105 if (buf.st_mtime != *timestamp)
2111 /* Call with lock held */
2113 remove_all (gpointer key,
2122 mime_info_cache_blow_global_cache (void)
2124 g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
2129 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
2133 gchar *filename, **mime_types;
2140 if (dir->mime_info_cache_map != NULL &&
2141 !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
2142 &dir->mime_info_cache_timestamp))
2145 if (dir->mime_info_cache_map != NULL)
2146 destroy_info_cache_map (dir->mime_info_cache_map);
2148 dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2149 (GDestroyNotify) g_free,
2152 key_file = g_key_file_new ();
2154 filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
2156 if (g_stat (filename, &buf) < 0)
2159 if (dir->mime_info_cache_timestamp > 0)
2160 mime_info_cache->should_ping_mime_monitor = TRUE;
2162 dir->mime_info_cache_timestamp = buf.st_mtime;
2164 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2169 if (load_error != NULL)
2172 mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
2175 if (load_error != NULL)
2178 for (i = 0; mime_types[i] != NULL; i++)
2180 gchar **desktop_file_ids;
2181 char *unaliased_type;
2182 desktop_file_ids = g_key_file_get_string_list (key_file,
2188 if (desktop_file_ids == NULL)
2191 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2192 mime_info_cache_dir_add_desktop_entries (dir,
2195 g_free (unaliased_type);
2197 g_strfreev (desktop_file_ids);
2200 g_strfreev (mime_types);
2201 g_key_file_free (key_file);
2206 g_key_file_free (key_file);
2208 if (mime_types != NULL)
2209 g_strfreev (mime_types);
2212 g_error_free (load_error);
2216 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
2220 gchar *filename, **mime_types;
2221 char *unaliased_type;
2222 char **desktop_file_ids;
2229 if (dir->defaults_list_map != NULL &&
2230 !mime_info_cache_dir_out_of_date (dir, "defaults.list",
2231 &dir->defaults_list_timestamp))
2234 if (dir->defaults_list_map != NULL)
2235 g_hash_table_destroy (dir->defaults_list_map);
2236 dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2237 g_free, (GDestroyNotify)g_strfreev);
2240 key_file = g_key_file_new ();
2242 filename = g_build_filename (dir->path, "defaults.list", NULL);
2243 if (g_stat (filename, &buf) < 0)
2246 if (dir->defaults_list_timestamp > 0)
2247 mime_info_cache->should_ping_mime_monitor = TRUE;
2249 dir->defaults_list_timestamp = buf.st_mtime;
2251 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2255 if (load_error != NULL)
2258 mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
2260 if (mime_types != NULL)
2262 for (i = 0; mime_types[i] != NULL; i++)
2264 desktop_file_ids = g_key_file_get_string_list (key_file,
2265 DEFAULT_APPLICATIONS_GROUP,
2269 if (desktop_file_ids == NULL)
2272 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2273 g_hash_table_replace (dir->defaults_list_map,
2278 g_strfreev (mime_types);
2281 g_key_file_free (key_file);
2286 g_key_file_free (key_file);
2288 if (mime_types != NULL)
2289 g_strfreev (mime_types);
2292 g_error_free (load_error);
2296 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
2300 gchar *filename, **mime_types;
2301 char *unaliased_type;
2302 char **desktop_file_ids;
2309 if (dir->mimeapps_list_added_map != NULL &&
2310 !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
2311 &dir->mimeapps_list_timestamp))
2314 if (dir->mimeapps_list_added_map != NULL)
2315 g_hash_table_destroy (dir->mimeapps_list_added_map);
2316 dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2317 g_free, (GDestroyNotify)g_strfreev);
2319 if (dir->mimeapps_list_removed_map != NULL)
2320 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2321 dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2322 g_free, (GDestroyNotify)g_strfreev);
2324 key_file = g_key_file_new ();
2326 filename = g_build_filename (dir->path, "mimeapps.list", NULL);
2327 if (g_stat (filename, &buf) < 0)
2330 if (dir->mimeapps_list_timestamp > 0)
2331 mime_info_cache->should_ping_mime_monitor = TRUE;
2333 dir->mimeapps_list_timestamp = buf.st_mtime;
2335 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2339 if (load_error != NULL)
2342 mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
2344 if (mime_types != NULL)
2346 for (i = 0; mime_types[i] != NULL; i++)
2348 desktop_file_ids = g_key_file_get_string_list (key_file,
2349 ADDED_ASSOCIATIONS_GROUP,
2353 if (desktop_file_ids == NULL)
2356 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2357 g_hash_table_replace (dir->mimeapps_list_added_map,
2362 g_strfreev (mime_types);
2365 mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
2367 if (mime_types != NULL)
2369 for (i = 0; mime_types[i] != NULL; i++)
2371 desktop_file_ids = g_key_file_get_string_list (key_file,
2372 REMOVED_ASSOCIATIONS_GROUP,
2376 if (desktop_file_ids == NULL)
2379 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2380 g_hash_table_replace (dir->mimeapps_list_removed_map,
2385 g_strfreev (mime_types);
2388 g_key_file_free (key_file);
2393 g_key_file_free (key_file);
2395 if (mime_types != NULL)
2396 g_strfreev (mime_types);
2399 g_error_free (load_error);
2402 static MimeInfoCacheDir *
2403 mime_info_cache_dir_new (const char *path)
2405 MimeInfoCacheDir *dir;
2407 dir = g_new0 (MimeInfoCacheDir, 1);
2408 dir->path = g_strdup (path);
2414 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
2419 if (dir->mime_info_cache_map != NULL)
2421 destroy_info_cache_map (dir->mime_info_cache_map);
2422 dir->mime_info_cache_map = NULL;
2426 if (dir->defaults_list_map != NULL)
2428 g_hash_table_destroy (dir->defaults_list_map);
2429 dir->defaults_list_map = NULL;
2432 if (dir->mimeapps_list_added_map != NULL)
2434 g_hash_table_destroy (dir->mimeapps_list_added_map);
2435 dir->mimeapps_list_added_map = NULL;
2438 if (dir->mimeapps_list_removed_map != NULL)
2440 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2441 dir->mimeapps_list_removed_map = NULL;
2448 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2449 const char *mime_type,
2450 char **new_desktop_file_ids)
2452 GList *desktop_file_ids;
2455 desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
2458 for (i = 0; new_desktop_file_ids[i] != NULL; i++)
2460 if (!g_list_find (desktop_file_ids, new_desktop_file_ids[i]))
2461 desktop_file_ids = g_list_append (desktop_file_ids,
2462 g_strdup (new_desktop_file_ids[i]));
2465 g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
2469 mime_info_cache_init_dir_lists (void)
2471 const char * const *dirs;
2474 mime_info_cache = mime_info_cache_new ();
2476 dirs = get_applications_search_path ();
2478 for (i = 0; dirs[i] != NULL; i++)
2480 MimeInfoCacheDir *dir;
2482 dir = mime_info_cache_dir_new (dirs[i]);
2486 mime_info_cache_dir_init (dir);
2487 mime_info_cache_dir_init_defaults_list (dir);
2488 mime_info_cache_dir_init_mimeapps_list (dir);
2490 mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
2496 mime_info_cache_update_dir_lists (void)
2500 tmp = mime_info_cache->dirs;
2504 MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
2506 /* No need to do this if we had file monitors... */
2507 mime_info_cache_blow_global_cache ();
2508 mime_info_cache_dir_init (dir);
2509 mime_info_cache_dir_init_defaults_list (dir);
2510 mime_info_cache_dir_init_mimeapps_list (dir);
2517 mime_info_cache_init (void)
2519 G_LOCK (mime_info_cache);
2520 if (mime_info_cache == NULL)
2521 mime_info_cache_init_dir_lists ();
2527 if (now >= mime_info_cache->last_stat_time + 10)
2529 mime_info_cache_update_dir_lists ();
2530 mime_info_cache->last_stat_time = now;
2534 if (mime_info_cache->should_ping_mime_monitor)
2536 /* g_idle_add (emit_mime_changed, NULL); */
2537 mime_info_cache->should_ping_mime_monitor = FALSE;
2540 G_UNLOCK (mime_info_cache);
2543 static MimeInfoCache *
2544 mime_info_cache_new (void)
2546 MimeInfoCache *cache;
2548 cache = g_new0 (MimeInfoCache, 1);
2550 cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2551 (GDestroyNotify) g_free,
2552 (GDestroyNotify) g_free);
2557 mime_info_cache_free (MimeInfoCache *cache)
2562 g_list_foreach (cache->dirs,
2563 (GFunc) mime_info_cache_dir_free,
2565 g_list_free (cache->dirs);
2566 g_hash_table_destroy (cache->global_defaults_cache);
2571 * mime_info_cache_reload:
2572 * @dir: directory path which needs reloading.
2574 * Reload the mime information for the @dir.
2577 mime_info_cache_reload (const char *dir)
2579 /* FIXME: just reload the dir that needs reloading,
2580 * don't blow the whole cache
2582 if (mime_info_cache != NULL)
2584 G_LOCK (mime_info_cache);
2585 mime_info_cache_free (mime_info_cache);
2586 mime_info_cache = NULL;
2587 G_UNLOCK (mime_info_cache);
2592 append_desktop_entry (GList *list,
2593 const char *desktop_entry,
2594 GList *removed_entries)
2596 /* Add if not already in list, and valid */
2597 if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
2598 !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
2599 list = g_list_prepend (list, g_strdup (desktop_entry));
2605 * get_all_desktop_entries_for_mime_type:
2606 * @mime_type: a mime type.
2607 * @except: NULL or a strv list
2609 * Returns all the desktop ids for @mime_type. The desktop files
2610 * are listed in an order so that default applications are listed before
2611 * non-default ones, and handlers for inherited mimetypes are listed
2612 * after the base ones.
2614 * Optionally doesn't list the desktop ids given in the @except
2616 * Return value: a #GList containing the desktop ids which claim
2617 * to handle @mime_type.
2620 get_all_desktop_entries_for_mime_type (const char *base_mime_type,
2621 const char **except)
2623 GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
2624 MimeInfoCacheDir *dir;
2627 char **default_entries;
2628 char **removed_associations;
2633 mime_info_cache_init ();
2635 /* collect all ancestors */
2636 mime_types = _g_unix_content_type_get_parents (base_mime_type);
2637 array = g_ptr_array_new ();
2638 for (i = 0; mime_types[i]; i++)
2639 g_ptr_array_add (array, mime_types[i]);
2640 g_free (mime_types);
2641 for (i = 0; i < array->len; i++)
2643 anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
2644 for (j = 0; anc[j]; j++)
2646 for (k = 0; k < array->len; k++)
2648 if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
2651 if (k == array->len) /* not found */
2652 g_ptr_array_add (array, g_strdup (anc[j]));
2656 g_ptr_array_add (array, NULL);
2657 mime_types = (char **)g_ptr_array_free (array, FALSE);
2659 G_LOCK (mime_info_cache);
2661 removed_entries = NULL;
2662 desktop_entries = NULL;
2664 for (i = 0; except != NULL && except[i] != NULL; i++)
2665 removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
2667 for (i = 0; mime_types[i] != NULL; i++)
2669 mime_type = mime_types[i];
2671 /* Go through all apps listed as defaults */
2672 for (dir_list = mime_info_cache->dirs;
2674 dir_list = dir_list->next)
2676 dir = dir_list->data;
2678 /* First added associations from mimeapps.list */
2679 default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
2680 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2681 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2683 /* Then removed associations from mimeapps.list */
2684 removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
2685 for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
2686 removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
2688 /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
2689 default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
2690 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2691 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2694 /* Go through all entries that support the mimetype */
2695 for (dir_list = mime_info_cache->dirs;
2697 dir_list = dir_list->next)
2699 dir = dir_list->data;
2701 list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
2702 for (tmp = list; tmp != NULL; tmp = tmp->next)
2703 desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
2707 G_UNLOCK (mime_info_cache);
2709 g_strfreev (mime_types);
2711 g_list_foreach (removed_entries, (GFunc)g_free, NULL);
2712 g_list_free (removed_entries);
2714 desktop_entries = g_list_reverse (desktop_entries);
2716 return desktop_entries;
2719 /* GDesktopAppInfoLookup interface: */
2721 static void g_desktop_app_info_lookup_base_init (gpointer g_class);
2722 static void g_desktop_app_info_lookup_class_init (gpointer g_class,
2723 gpointer class_data);
2726 g_desktop_app_info_lookup_get_type (void)
2728 static volatile gsize g_define_type_id__volatile = 0;
2730 if (g_once_init_enter (&g_define_type_id__volatile))
2732 const GTypeInfo desktop_app_info_lookup_info =
2734 sizeof (GDesktopAppInfoLookupIface), /* class_size */
2735 g_desktop_app_info_lookup_base_init, /* base_init */
2736 NULL, /* base_finalize */
2737 g_desktop_app_info_lookup_class_init,
2738 NULL, /* class_finalize */
2739 NULL, /* class_data */
2741 0, /* n_preallocs */
2744 GType g_define_type_id =
2745 g_type_register_static (G_TYPE_INTERFACE, I_("GDesktopAppInfoLookup"),
2746 &desktop_app_info_lookup_info, 0);
2748 g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
2750 g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
2753 return g_define_type_id__volatile;
2757 g_desktop_app_info_lookup_class_init (gpointer g_class,
2758 gpointer class_data)
2763 g_desktop_app_info_lookup_base_init (gpointer g_class)
2768 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
2769 * @lookup: a #GDesktopAppInfoLookup
2770 * @uri_scheme: a string containing a URI scheme.
2772 * Gets the default application for launching applications
2773 * using this URI scheme for a particular GDesktopAppInfoLookup
2776 * The GDesktopAppInfoLookup interface and this function is used
2777 * to implement g_app_info_get_default_for_uri_scheme() backends
2778 * in a GIO module. There is no reason for applications to use it
2779 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
2781 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
2784 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
2785 const char *uri_scheme)
2787 GDesktopAppInfoLookupIface *iface;
2789 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
2791 iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
2793 return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
2796 #define __G_DESKTOP_APP_INFO_C__
2797 #include "gioaliasdef.c"