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_commandline (GAppInfo *appinfo)
485 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
491 g_desktop_app_info_get_icon (GAppInfo *appinfo)
493 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
499 expand_macro_single (char macro, char *uri)
505 file = g_file_new_for_uri (uri);
506 path = g_file_get_path (file);
507 g_object_unref (file);
513 result = g_shell_quote (uri);
518 result = g_shell_quote (path);
524 name = g_path_get_dirname (path);
525 result = g_shell_quote (name);
533 name = g_path_get_basename (path);
534 result = g_shell_quote (name);
546 expand_macro (char macro,
548 GDesktopAppInfo *info,
551 GList *uris = *uri_list;
553 gboolean force_file_uri;
554 char force_file_uri_macro;
556 g_return_if_fail (exec != NULL);
558 /* On %u and %U, pass POSIX file path pointing to the URI via
559 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
560 * running or the URI doesn't have a POSIX file path via FUSE
561 * we'll just pass the URI.
566 force_file_uri_macro = 'f';
567 force_file_uri = TRUE;
570 force_file_uri_macro = 'F';
571 force_file_uri = TRUE;
574 force_file_uri_macro = macro;
575 force_file_uri = FALSE;
589 expanded = expand_macro_single (macro, uris->data);
593 expanded = expand_macro_single (force_file_uri_macro, uris->data);
594 if (expanded == NULL)
595 expanded = expand_macro_single (macro, uris->data);
600 g_string_append (exec, expanded);
616 expanded = expand_macro_single (macro, uris->data);
620 expanded = expand_macro_single (force_file_uri_macro, uris->data);
621 if (expanded == NULL)
622 expanded = expand_macro_single (macro, uris->data);
627 g_string_append (exec, expanded);
633 if (uris != NULL && expanded)
634 g_string_append_c (exec, ' ');
642 g_string_append (exec, "--icon ");
643 g_string_append (exec, info->icon_name);
649 g_string_append (exec, info->name);
654 g_string_append (exec, info->filename);
657 case 'm': /* deprecated */
661 g_string_append_c (exec, '%');
669 expand_application_parameters (GDesktopAppInfo *info,
675 GList *uri_list = *uris;
676 const char *p = info->exec;
677 GString *expanded_exec = g_string_new (NULL);
680 if (info->exec == NULL)
682 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
683 _("Desktop file didn't specify Exec field"));
689 if (p[0] == '%' && p[1] != '\0')
691 expand_macro (p[1], expanded_exec, info, uris);
695 g_string_append_c (expanded_exec, *p);
700 /* No file substitutions */
701 if (uri_list == *uris && uri_list != NULL)
703 /* If there is no macro default to %f. This is also what KDE does */
704 g_string_append_c (expanded_exec, ' ');
705 expand_macro ('f', expanded_exec, info, uris);
708 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
709 g_string_free (expanded_exec, TRUE);
714 prepend_terminal_to_vector (int *argc,
721 char **term_argv = NULL;
726 g_return_val_if_fail (argc != NULL, FALSE);
727 g_return_val_if_fail (argv != NULL, FALSE);
735 /* compute size if not given */
738 for (i = 0; the_argv[i] != NULL; i++)
744 term_argv = g_new0 (char *, 3);
746 check = g_find_program_in_path ("gnome-terminal");
749 term_argv[0] = check;
750 /* Note that gnome-terminal takes -x and
751 * as -e in gnome-terminal is broken we use that. */
752 term_argv[1] = g_strdup ("-x");
757 check = g_find_program_in_path ("nxterm");
759 check = g_find_program_in_path ("color-xterm");
761 check = g_find_program_in_path ("rxvt");
763 check = g_find_program_in_path ("xterm");
765 check = g_find_program_in_path ("dtterm");
768 check = g_strdup ("xterm");
769 g_warning ("couldn't find a terminal, falling back to xterm");
771 term_argv[0] = check;
772 term_argv[1] = g_strdup ("-e");
775 real_argc = term_argc + *argc;
776 real_argv = g_new (char *, real_argc + 1);
778 for (i = 0; i < term_argc; i++)
779 real_argv[i] = term_argv[i];
781 for (j = 0; j < *argc; j++, i++)
782 real_argv[i] = (char *)the_argv[j];
790 /* we use g_free here as we sucked all the inner strings
791 * out from it into real_argv */
796 #endif /* G_OS_WIN32 */
799 /* '=' is the new '\0'.
800 * DO NOT CALL unless at least one string ends with '='
803 is_env (const char *a,
808 if (*a == 0 || *b == 0)
821 /* free with g_strfreev */
823 replace_env_var (char **old_environ,
825 const char *new_value)
827 int length, new_length;
828 int index, new_index;
832 /* do two things at once:
833 * - discover the length of the environment ('length')
834 * - find the location (if any) of the env var ('index')
837 for (length = 0; old_environ[length]; length++)
839 /* if we already have it in our environment, replace */
840 if (is_env (old_environ[length], env_var))
845 /* no current env var, no desired env value.
848 if (new_value == NULL && index == -1)
851 /* in all cases now, we will be using a modified environment.
852 * determine its length and allocated it.
855 * new_index = location to insert, if any
856 * new_length = length of the new array
857 * new_environ = the pointer array for the new environment
860 if (new_value == NULL && index >= 0)
862 /* in this case, we will be removing an entry */
863 new_length = length - 1;
866 else if (new_value != NULL && index < 0)
868 /* in this case, we will be adding an entry to the end */
869 new_length = length + 1;
873 /* in this case, we will be replacing the existing entry */
879 new_environ = g_malloc (sizeof (char *) * (new_length + 1));
880 new_environ[new_length] = NULL;
882 /* now we do the copying.
883 * for each entry in the new environment, we decide what to do
887 for (new_i = 0; new_i < new_length; new_i++)
889 if (new_i == new_index)
891 /* insert our new item */
892 new_environ[new_i] = g_strconcat (env_var,
897 /* if we had an old entry, skip it now */
903 /* if this is the old DESKTOP_STARTUP_ID, skip it */
907 /* copy an old item */
908 new_environ[new_i] = g_strdup (old_environ[i]);
913 g_strfreev (old_environ);
919 uri_list_segment_to_files (GList *start,
926 while (start != NULL && start != end)
928 file = g_file_new_for_uri ((char *)start->data);
929 res = g_list_prepend (res, file);
933 return g_list_reverse (res);
936 #ifdef HAVE__NSGETENVIRON
937 #define environ (*_NSGetEnviron())
938 #elif !defined(G_OS_WIN32)
940 /* According to the Single Unix Specification, environ is not in
941 * * any system header, although unistd.h often declares it.
943 extern char **environ;
947 g_desktop_app_info_launch_uris (GAppInfo *appinfo,
949 GAppLaunchContext *launch_context,
952 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
953 gboolean completed = FALSE;
955 GList *launched_files;
962 g_return_val_if_fail (appinfo != NULL, FALSE);
970 if (!expand_application_parameters (info, &uris,
971 &argc, &argv, error))
974 if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
976 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
977 _("Unable to find terminal required for application"));
984 launched_files = uri_list_segment_to_files (old_uris, uris);
986 display = g_app_launch_context_get_display (launch_context,
991 if (info->startup_notify)
992 sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
996 if (display || sn_id)
1000 envp = g_new0 (char *, 1);
1002 envp = g_strdupv (environ);
1006 envp = replace_env_var (envp,
1011 envp = replace_env_var (envp,
1012 "DESKTOP_STARTUP_ID",
1018 g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
1019 g_list_free (launched_files);
1022 if (!g_spawn_async (info->path, /* working directory */
1025 G_SPAWN_SEARCH_PATH /* flags */,
1026 NULL /* child_setup */,
1028 NULL /* child_pid */,
1033 g_app_launch_context_launch_failed (launch_context, sn_id);
1047 while (uris != NULL);
1059 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
1061 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1063 return info->exec &&
1064 ((strstr (info->exec, "%u") != NULL) ||
1065 (strstr (info->exec, "%U") != NULL));
1069 g_desktop_app_info_supports_files (GAppInfo *appinfo)
1071 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1073 return info->exec &&
1074 ((strstr (info->exec, "%f") != NULL) ||
1075 (strstr (info->exec, "%F") != NULL));
1079 g_desktop_app_info_launch (GAppInfo *appinfo,
1081 GAppLaunchContext *launch_context,
1091 uri = g_file_get_uri (files->data);
1092 uris = g_list_prepend (uris, uri);
1093 files = files->next;
1096 uris = g_list_reverse (uris);
1098 res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
1100 g_list_foreach (uris, (GFunc)g_free, NULL);
1106 G_LOCK_DEFINE_STATIC (g_desktop_env);
1107 static gchar *g_desktop_env = NULL;
1110 * g_desktop_app_info_set_desktop_env:
1111 * @desktop_env: a string specifying what desktop this is
1113 * Sets the name of the desktop that the application is running in.
1114 * This is used by g_app_info_should_show() to evaluate the
1115 * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
1116 * desktop entry fields.
1118 * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop
1119 * Menu specification</ulink> recognizes the following:
1121 * <member>GNOME</member>
1122 * <member>KDE</member>
1123 * <member>ROX</member>
1124 * <member>XFCE</member>
1125 * <member>Old</member>
1128 * Should be called only once; subsequent calls are ignored.
1131 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
1133 G_LOCK (g_desktop_env);
1135 g_desktop_env = g_strdup (desktop_env);
1136 G_UNLOCK (g_desktop_env);
1140 g_desktop_app_info_should_show (GAppInfo *appinfo)
1142 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1144 const gchar *desktop_env;
1147 if (info->nodisplay)
1150 G_LOCK (g_desktop_env);
1151 desktop_env = g_desktop_env;
1152 G_UNLOCK (g_desktop_env);
1154 if (info->only_show_in)
1156 if (desktop_env == NULL)
1160 for (i = 0; info->only_show_in[i] != NULL; i++)
1162 if (strcmp (info->only_show_in[i], desktop_env) == 0)
1172 if (info->not_show_in && desktop_env)
1174 for (i = 0; info->not_show_in[i] != NULL; i++)
1176 if (strcmp (info->not_show_in[i], desktop_env) == 0)
1190 ensure_dir (DirType type,
1193 char *path, *display_name;
1196 if (type == APP_DIR)
1197 path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
1199 path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
1202 if (g_mkdir_with_parents (path, 0700) == 0)
1206 display_name = g_filename_display_name (path);
1207 if (type == APP_DIR)
1208 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1209 _("Can't create user application configuration folder %s: %s"),
1210 display_name, g_strerror (errsv));
1212 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1213 _("Can't create user MIME configuration folder %s: %s"),
1214 display_name, g_strerror (errsv));
1216 g_free (display_name);
1223 update_mimeapps_list (const char *desktop_id,
1224 const char *content_type,
1225 gboolean add_as_default,
1226 gboolean add_non_default,
1230 char *dirname, *filename;
1232 gboolean load_succeeded, res;
1233 char **old_list, **list;
1234 GList *system_list, *l;
1235 gsize length, data_size;
1238 char **content_types;
1240 /* Don't add both at start and end */
1241 g_assert (!(add_as_default && add_non_default));
1243 dirname = ensure_dir (APP_DIR, error);
1247 filename = g_build_filename (dirname, "mimeapps.list", NULL);
1250 key_file = g_key_file_new ();
1251 load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
1252 if (!load_succeeded || !g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP))
1254 g_key_file_free (key_file);
1255 key_file = g_key_file_new ();
1260 content_types = g_new (char *, 2);
1261 content_types[0] = g_strdup (content_type);
1262 content_types[1] = NULL;
1266 content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
1269 for (k = 0; content_types && content_types[k]; k++)
1271 /* Add to the right place in the list */
1274 old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
1275 content_types[k], &length, NULL);
1277 list = g_new (char *, 1 + length + 1);
1281 list[i++] = g_strdup (desktop_id);
1284 for (j = 0; old_list[j] != NULL; j++)
1286 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1287 list[i++] = g_strdup (old_list[j]);
1288 else if (add_non_default)
1290 /* If adding as non-default, and its already in,
1291 don't change order of desktop ids */
1292 add_non_default = FALSE;
1293 list[i++] = g_strdup (old_list[j]);
1298 if (add_non_default)
1300 /* We're adding as non-default, and it wasn't already in the list,
1301 so we add at the end. But to avoid listing the app before the
1302 current system default (thus changing the default) we have to
1303 add the current list of (not yet listed) apps before it. */
1305 list[i] = NULL; /* Terminate current list so we can use it */
1306 system_list = get_all_desktop_entries_for_mime_type (content_type, list);
1308 list = g_renew (char *, list, 1 + length + g_list_length (system_list) + 1);
1310 for (l = system_list; l != NULL; l = l->next)
1312 list[i++] = l->data; /* no strdup, taking ownership */
1313 if (g_strcmp0 (l->data, desktop_id) == 0)
1314 add_non_default = FALSE;
1316 g_list_free (system_list);
1318 if (add_non_default)
1319 list[i++] = g_strdup (desktop_id);
1324 g_strfreev (old_list);
1326 if (list[0] == NULL || desktop_id == NULL)
1327 g_key_file_remove_key (key_file,
1328 ADDED_ASSOCIATIONS_GROUP,
1332 g_key_file_set_string_list (key_file,
1333 ADDED_ASSOCIATIONS_GROUP,
1335 (const char * const *)list, i);
1342 /* reuse the list from above */
1346 g_strfreev (content_types);
1347 content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
1350 for (k = 0; content_types && content_types[k]; k++)
1352 /* Remove from removed associations group (unless remove) */
1355 old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
1356 content_types[k], &length, NULL);
1358 list = g_new (char *, 1 + length + 1);
1362 list[i++] = g_strdup (desktop_id);
1365 for (j = 0; old_list[j] != NULL; j++)
1367 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1368 list[i++] = g_strdup (old_list[j]);
1373 g_strfreev (old_list);
1375 if (list[0] == NULL || desktop_id == NULL)
1376 g_key_file_remove_key (key_file,
1377 REMOVED_ASSOCIATIONS_GROUP,
1381 g_key_file_set_string_list (key_file,
1382 REMOVED_ASSOCIATIONS_GROUP,
1384 (const char * const *)list, i);
1389 g_strfreev (content_types);
1391 data = g_key_file_to_data (key_file, &data_size, error);
1392 g_key_file_free (key_file);
1394 res = g_file_set_contents (filename, data, data_size, error);
1396 mime_info_cache_reload (NULL);
1405 g_desktop_app_info_set_as_default_for_type (GAppInfo *appinfo,
1406 const char *content_type,
1409 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1411 if (!g_desktop_app_info_ensure_saved (info, error))
1414 return update_mimeapps_list (info->desktop_id, content_type, TRUE, FALSE, FALSE, error);
1418 update_program_done (GPid pid,
1422 /* Did the application exit correctly */
1423 if (WIFEXITED (status) &&
1424 WEXITSTATUS (status) == 0)
1426 /* Here we could clean out any caches in use */
1431 run_update_command (char *command,
1440 GError *error = NULL;
1443 argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
1445 if (g_spawn_async ("/", argv,
1447 G_SPAWN_SEARCH_PATH |
1448 G_SPAWN_STDOUT_TO_DEV_NULL |
1449 G_SPAWN_STDERR_TO_DEV_NULL |
1450 G_SPAWN_DO_NOT_REAP_CHILD,
1451 NULL, NULL, /* No setup function */
1454 g_child_watch_add (pid, update_program_done, NULL);
1457 /* If we get an error at this point, it's quite likely the user doesn't
1458 * have an installed copy of either 'update-mime-database' or
1459 * 'update-desktop-database'. I don't think we want to popup an error
1460 * dialog at this point, so we just do a g_warning to give the user a
1461 * chance of debugging it.
1463 g_warning ("%s", error->message);
1470 g_desktop_app_info_set_as_default_for_extension (GAppInfo *appinfo,
1471 const char *extension,
1474 char *filename, *basename, *mimetype;
1478 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
1481 dirname = ensure_dir (MIMETYPE_DIR, error);
1485 basename = g_strdup_printf ("user-extension-%s.xml", extension);
1486 filename = g_build_filename (dirname, basename, NULL);
1490 mimetype = g_strdup_printf ("application/x-extension-%s", extension);
1492 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1497 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1498 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
1499 " <mime-type type=\"%s\">\n"
1500 " <comment>%s document</comment>\n"
1501 " <glob pattern=\"*.%s\"/>\n"
1503 "</mime-info>\n", mimetype, extension, extension);
1505 g_file_set_contents (filename, contents, -1, NULL);
1508 run_update_command ("update-mime-database", "mime");
1512 res = g_desktop_app_info_set_as_default_for_type (appinfo,
1522 g_desktop_app_info_add_supports_type (GAppInfo *appinfo,
1523 const char *content_type,
1526 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1528 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1531 return update_mimeapps_list (info->desktop_id, content_type, FALSE, TRUE, FALSE, error);
1535 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
1541 g_desktop_app_info_remove_supports_type (GAppInfo *appinfo,
1542 const char *content_type,
1545 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1547 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1550 return update_mimeapps_list (info->desktop_id, content_type, FALSE, FALSE, TRUE, error);
1554 g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
1560 char *data, *desktop_id;
1565 if (info->filename != NULL)
1568 /* This is only used for object created with
1569 * g_app_info_create_from_commandline. All other
1570 * object should have a filename
1573 dirname = ensure_dir (APP_DIR, error);
1577 key_file = g_key_file_new ();
1579 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1580 "Encoding", "UTF-8");
1581 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1582 G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
1583 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1584 G_KEY_FILE_DESKTOP_KEY_TYPE,
1585 G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
1587 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1588 G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
1590 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1591 G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
1593 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1594 G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
1596 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1597 G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
1599 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1600 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
1602 data = g_key_file_to_data (key_file, &data_size, NULL);
1603 g_key_file_free (key_file);
1605 desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
1606 filename = g_build_filename (dirname, desktop_id, NULL);
1607 g_free (desktop_id);
1610 fd = g_mkstemp (filename);
1615 display_name = g_filename_display_name (filename);
1616 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1617 _("Can't create user desktop file %s"), display_name);
1618 g_free (display_name);
1624 desktop_id = g_path_get_basename (filename);
1628 res = g_file_set_contents (filename, data, data_size, error);
1631 g_free (desktop_id);
1636 info->filename = filename;
1637 info->desktop_id = desktop_id;
1639 run_update_command ("update-desktop-database", "applications");
1645 g_desktop_app_info_can_delete (GAppInfo *appinfo)
1647 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1651 if (strstr (info->filename, "/userapp-"))
1652 return g_access (info->filename, W_OK) == 0;
1659 g_desktop_app_info_delete (GAppInfo *appinfo)
1661 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1665 if (g_remove (info->filename) == 0)
1667 update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL);
1669 g_free (info->filename);
1670 info->filename = NULL;
1671 g_free (info->desktop_id);
1672 info->desktop_id = NULL;
1682 * g_app_info_create_from_commandline:
1683 * @commandline: the commandline to use
1684 * @application_name: the application name, or %NULL to use @commandline
1685 * @flags: flags that can specify details of the created #GAppInfo
1686 * @error: a #GError location to store the error occuring, %NULL to ignore.
1688 * Creates a new #GAppInfo from the given information.
1690 * Returns: new #GAppInfo for given command.
1693 g_app_info_create_from_commandline (const char *commandline,
1694 const char *application_name,
1695 GAppInfoCreateFlags flags,
1700 GDesktopAppInfo *info;
1702 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1704 info->filename = NULL;
1705 info->desktop_id = NULL;
1707 info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL;
1708 info->startup_notify = FALSE;
1709 info->hidden = FALSE;
1710 if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS)
1711 info->exec = g_strconcat (commandline, " %u", NULL);
1713 info->exec = g_strconcat (commandline, " %f", NULL);
1714 info->nodisplay = TRUE;
1715 info->binary = binary_from_exec (info->exec);
1717 if (application_name)
1718 info->name = g_strdup (application_name);
1721 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
1722 split = g_strsplit (commandline, " ", 2);
1723 basename = g_path_get_basename (split[0]);
1725 info->name = basename;
1726 if (info->name == NULL)
1727 info->name = g_strdup ("custom");
1729 info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
1731 return G_APP_INFO (info);
1735 g_desktop_app_info_iface_init (GAppInfoIface *iface)
1737 iface->dup = g_desktop_app_info_dup;
1738 iface->equal = g_desktop_app_info_equal;
1739 iface->get_id = g_desktop_app_info_get_id;
1740 iface->get_name = g_desktop_app_info_get_name;
1741 iface->get_description = g_desktop_app_info_get_description;
1742 iface->get_executable = g_desktop_app_info_get_executable;
1743 iface->get_icon = g_desktop_app_info_get_icon;
1744 iface->launch = g_desktop_app_info_launch;
1745 iface->supports_uris = g_desktop_app_info_supports_uris;
1746 iface->supports_files = g_desktop_app_info_supports_files;
1747 iface->launch_uris = g_desktop_app_info_launch_uris;
1748 iface->should_show = g_desktop_app_info_should_show;
1749 iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
1750 iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
1751 iface->add_supports_type = g_desktop_app_info_add_supports_type;
1752 iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
1753 iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
1754 iface->can_delete = g_desktop_app_info_can_delete;
1755 iface->do_delete = g_desktop_app_info_delete;
1756 iface->get_commandline = g_desktop_app_info_get_commandline;
1760 app_info_in_list (GAppInfo *info,
1763 while (list != NULL)
1765 if (g_app_info_equal (info, list->data))
1774 * g_app_info_get_all_for_type:
1775 * @content_type: the content type to find a #GAppInfo for
1777 * Gets a list of all #GAppInfo s for a given content type.
1779 * Returns: #GList of #GAppInfo s for given @content_type
1780 * or %NULL on error.
1783 g_app_info_get_all_for_type (const char *content_type)
1785 GList *desktop_entries, *l;
1787 GDesktopAppInfo *info;
1789 g_return_val_if_fail (content_type != NULL, NULL);
1791 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1794 for (l = desktop_entries; l != NULL; l = l->next)
1796 char *desktop_entry = l->data;
1798 info = g_desktop_app_info_new (desktop_entry);
1801 if (app_info_in_list (G_APP_INFO (info), infos))
1802 g_object_unref (info);
1804 infos = g_list_prepend (infos, info);
1806 g_free (desktop_entry);
1809 g_list_free (desktop_entries);
1811 return g_list_reverse (infos);
1815 * g_app_info_reset_type_associations:
1816 * @content_type: a content type
1818 * Removes all changes to the type associations done by
1819 * g_app_info_set_as_default_for_type(),
1820 * g_app_info_set_as_default_for_extension(),
1821 * g_app_info_add_supports_type() of g_app_info_remove_supports_type().
1826 g_app_info_reset_type_associations (const char *content_type)
1828 update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
1832 * g_app_info_get_default_for_type:
1833 * @content_type: the content type to find a #GAppInfo for
1834 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
1837 * Gets the #GAppInfo that correspond to a given content type.
1839 * Returns: #GAppInfo for given @content_type or %NULL on error.
1842 g_app_info_get_default_for_type (const char *content_type,
1843 gboolean must_support_uris)
1845 GList *desktop_entries, *l;
1848 g_return_val_if_fail (content_type != NULL, NULL);
1850 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1853 for (l = desktop_entries; l != NULL; l = l->next)
1855 char *desktop_entry = l->data;
1857 info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
1860 if (must_support_uris && !g_app_info_supports_uris (info))
1862 g_object_unref (info);
1870 g_list_foreach (desktop_entries, (GFunc)g_free, NULL);
1871 g_list_free (desktop_entries);
1877 * g_app_info_get_default_for_uri_scheme:
1878 * @uri_scheme: a string containing a URI scheme.
1880 * Gets the default application for launching applications
1881 * using this URI scheme. A URI scheme is the initial part
1882 * of the URI, up to but not including the ':', e.g. "http",
1885 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
1888 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
1890 static gsize lookup = 0;
1892 if (g_once_init_enter (&lookup))
1894 gsize setup_value = 1;
1895 GDesktopAppInfoLookup *lookup_instance;
1896 const char *use_this;
1897 GIOExtensionPoint *ep;
1898 GIOExtension *extension;
1901 use_this = g_getenv ("GIO_USE_URI_ASSOCIATION");
1903 /* Ensure vfs in modules loaded */
1904 _g_io_modules_ensure_loaded ();
1906 ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME);
1908 lookup_instance = NULL;
1911 extension = g_io_extension_point_get_extension_by_name (ep, use_this);
1913 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1916 if (lookup_instance == NULL)
1918 for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
1920 extension = l->data;
1921 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1922 if (lookup_instance != NULL)
1927 if (lookup_instance != NULL)
1928 setup_value = (gsize)lookup_instance;
1930 g_once_init_leave (&lookup, setup_value);
1936 return g_desktop_app_info_lookup_get_default_for_uri_scheme (G_DESKTOP_APP_INFO_LOOKUP (lookup),
1942 get_apps_from_dir (GHashTable *apps,
1943 const char *dirname,
1947 const char *basename;
1948 char *filename, *subprefix, *desktop_id;
1950 GDesktopAppInfo *appinfo;
1952 dir = g_dir_open (dirname, 0, NULL);
1955 while ((basename = g_dir_read_name (dir)) != NULL)
1957 filename = g_build_filename (dirname, basename, NULL);
1958 if (g_str_has_suffix (basename, ".desktop"))
1960 desktop_id = g_strconcat (prefix, basename, NULL);
1962 /* Use _extended so we catch NULLs too (hidden) */
1963 if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
1965 appinfo = g_desktop_app_info_new_from_filename (filename);
1967 if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
1969 g_object_unref (appinfo);
1974 if (appinfo || hidden)
1976 g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
1980 /* Reuse instead of strdup here */
1981 appinfo->desktop_id = desktop_id;
1986 g_free (desktop_id);
1990 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
1992 subprefix = g_strconcat (prefix, basename, "-", NULL);
1993 get_apps_from_dir (apps, filename, subprefix);
2005 * g_app_info_get_all:
2007 * Gets a list of all of the applications currently registered
2010 * For desktop files, this includes applications that have
2011 * <literal>NoDisplay=true</literal> set or are excluded from
2012 * display by means of <literal>OnlyShowIn</literal> or
2013 * <literal>NotShowIn</literal>. See g_app_info_should_show().
2014 * The returned list does not include applications which have
2015 * the <literal>Hidden</literal> key set.
2017 * Returns: a newly allocated #GList of references to #GAppInfo<!---->s.
2020 g_app_info_get_all (void)
2022 const char * const *dirs;
2024 GHashTableIter iter;
2029 dirs = get_applications_search_path ();
2031 apps = g_hash_table_new_full (g_str_hash, g_str_equal,
2035 for (i = 0; dirs[i] != NULL; i++)
2036 get_apps_from_dir (apps, dirs[i], "");
2040 g_hash_table_iter_init (&iter, apps);
2041 while (g_hash_table_iter_next (&iter, NULL, &value))
2044 infos = g_list_prepend (infos, value);
2047 g_hash_table_destroy (apps);
2049 return g_list_reverse (infos);
2052 /* Cacheing of mimeinfo.cache and defaults.list files */
2056 GHashTable *mime_info_cache_map;
2057 GHashTable *defaults_list_map;
2058 GHashTable *mimeapps_list_added_map;
2059 GHashTable *mimeapps_list_removed_map;
2060 time_t mime_info_cache_timestamp;
2061 time_t defaults_list_timestamp;
2062 time_t mimeapps_list_timestamp;
2066 GList *dirs; /* mimeinfo.cache and defaults.list */
2067 GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
2068 time_t last_stat_time;
2069 guint should_ping_mime_monitor : 1;
2072 static MimeInfoCache *mime_info_cache = NULL;
2073 G_LOCK_DEFINE_STATIC (mime_info_cache);
2075 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2076 const char *mime_type,
2077 char **new_desktop_file_ids);
2079 static MimeInfoCache * mime_info_cache_new (void);
2082 destroy_info_cache_value (gpointer key,
2086 g_list_foreach (value, (GFunc)g_free, NULL);
2087 g_list_free (value);
2091 destroy_info_cache_map (GHashTable *info_cache_map)
2093 g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
2094 g_hash_table_destroy (info_cache_map);
2098 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
2099 const char *cache_file,
2105 filename = g_build_filename (dir->path, cache_file, NULL);
2107 if (g_stat (filename, &buf) < 0)
2114 if (buf.st_mtime != *timestamp)
2120 /* Call with lock held */
2122 remove_all (gpointer key,
2131 mime_info_cache_blow_global_cache (void)
2133 g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
2138 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
2142 gchar *filename, **mime_types;
2149 if (dir->mime_info_cache_map != NULL &&
2150 !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
2151 &dir->mime_info_cache_timestamp))
2154 if (dir->mime_info_cache_map != NULL)
2155 destroy_info_cache_map (dir->mime_info_cache_map);
2157 dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2158 (GDestroyNotify) g_free,
2161 key_file = g_key_file_new ();
2163 filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
2165 if (g_stat (filename, &buf) < 0)
2168 if (dir->mime_info_cache_timestamp > 0)
2169 mime_info_cache->should_ping_mime_monitor = TRUE;
2171 dir->mime_info_cache_timestamp = buf.st_mtime;
2173 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2178 if (load_error != NULL)
2181 mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
2184 if (load_error != NULL)
2187 for (i = 0; mime_types[i] != NULL; i++)
2189 gchar **desktop_file_ids;
2190 char *unaliased_type;
2191 desktop_file_ids = g_key_file_get_string_list (key_file,
2197 if (desktop_file_ids == NULL)
2200 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2201 mime_info_cache_dir_add_desktop_entries (dir,
2204 g_free (unaliased_type);
2206 g_strfreev (desktop_file_ids);
2209 g_strfreev (mime_types);
2210 g_key_file_free (key_file);
2215 g_key_file_free (key_file);
2217 if (mime_types != NULL)
2218 g_strfreev (mime_types);
2221 g_error_free (load_error);
2225 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
2229 gchar *filename, **mime_types;
2230 char *unaliased_type;
2231 char **desktop_file_ids;
2238 if (dir->defaults_list_map != NULL &&
2239 !mime_info_cache_dir_out_of_date (dir, "defaults.list",
2240 &dir->defaults_list_timestamp))
2243 if (dir->defaults_list_map != NULL)
2244 g_hash_table_destroy (dir->defaults_list_map);
2245 dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2246 g_free, (GDestroyNotify)g_strfreev);
2249 key_file = g_key_file_new ();
2251 filename = g_build_filename (dir->path, "defaults.list", NULL);
2252 if (g_stat (filename, &buf) < 0)
2255 if (dir->defaults_list_timestamp > 0)
2256 mime_info_cache->should_ping_mime_monitor = TRUE;
2258 dir->defaults_list_timestamp = buf.st_mtime;
2260 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2264 if (load_error != NULL)
2267 mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
2269 if (mime_types != NULL)
2271 for (i = 0; mime_types[i] != NULL; i++)
2273 desktop_file_ids = g_key_file_get_string_list (key_file,
2274 DEFAULT_APPLICATIONS_GROUP,
2278 if (desktop_file_ids == NULL)
2281 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2282 g_hash_table_replace (dir->defaults_list_map,
2287 g_strfreev (mime_types);
2290 g_key_file_free (key_file);
2295 g_key_file_free (key_file);
2297 if (mime_types != NULL)
2298 g_strfreev (mime_types);
2301 g_error_free (load_error);
2305 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
2309 gchar *filename, **mime_types;
2310 char *unaliased_type;
2311 char **desktop_file_ids;
2318 if (dir->mimeapps_list_added_map != NULL &&
2319 !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
2320 &dir->mimeapps_list_timestamp))
2323 if (dir->mimeapps_list_added_map != NULL)
2324 g_hash_table_destroy (dir->mimeapps_list_added_map);
2325 dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2326 g_free, (GDestroyNotify)g_strfreev);
2328 if (dir->mimeapps_list_removed_map != NULL)
2329 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2330 dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2331 g_free, (GDestroyNotify)g_strfreev);
2333 key_file = g_key_file_new ();
2335 filename = g_build_filename (dir->path, "mimeapps.list", NULL);
2336 if (g_stat (filename, &buf) < 0)
2339 if (dir->mimeapps_list_timestamp > 0)
2340 mime_info_cache->should_ping_mime_monitor = TRUE;
2342 dir->mimeapps_list_timestamp = buf.st_mtime;
2344 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2348 if (load_error != NULL)
2351 mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
2353 if (mime_types != NULL)
2355 for (i = 0; mime_types[i] != NULL; i++)
2357 desktop_file_ids = g_key_file_get_string_list (key_file,
2358 ADDED_ASSOCIATIONS_GROUP,
2362 if (desktop_file_ids == NULL)
2365 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2366 g_hash_table_replace (dir->mimeapps_list_added_map,
2371 g_strfreev (mime_types);
2374 mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
2376 if (mime_types != NULL)
2378 for (i = 0; mime_types[i] != NULL; i++)
2380 desktop_file_ids = g_key_file_get_string_list (key_file,
2381 REMOVED_ASSOCIATIONS_GROUP,
2385 if (desktop_file_ids == NULL)
2388 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2389 g_hash_table_replace (dir->mimeapps_list_removed_map,
2394 g_strfreev (mime_types);
2397 g_key_file_free (key_file);
2402 g_key_file_free (key_file);
2404 if (mime_types != NULL)
2405 g_strfreev (mime_types);
2408 g_error_free (load_error);
2411 static MimeInfoCacheDir *
2412 mime_info_cache_dir_new (const char *path)
2414 MimeInfoCacheDir *dir;
2416 dir = g_new0 (MimeInfoCacheDir, 1);
2417 dir->path = g_strdup (path);
2423 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
2428 if (dir->mime_info_cache_map != NULL)
2430 destroy_info_cache_map (dir->mime_info_cache_map);
2431 dir->mime_info_cache_map = NULL;
2435 if (dir->defaults_list_map != NULL)
2437 g_hash_table_destroy (dir->defaults_list_map);
2438 dir->defaults_list_map = NULL;
2441 if (dir->mimeapps_list_added_map != NULL)
2443 g_hash_table_destroy (dir->mimeapps_list_added_map);
2444 dir->mimeapps_list_added_map = NULL;
2447 if (dir->mimeapps_list_removed_map != NULL)
2449 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2450 dir->mimeapps_list_removed_map = NULL;
2457 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2458 const char *mime_type,
2459 char **new_desktop_file_ids)
2461 GList *desktop_file_ids;
2464 desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
2467 for (i = 0; new_desktop_file_ids[i] != NULL; i++)
2469 if (!g_list_find (desktop_file_ids, new_desktop_file_ids[i]))
2470 desktop_file_ids = g_list_append (desktop_file_ids,
2471 g_strdup (new_desktop_file_ids[i]));
2474 g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
2478 mime_info_cache_init_dir_lists (void)
2480 const char * const *dirs;
2483 mime_info_cache = mime_info_cache_new ();
2485 dirs = get_applications_search_path ();
2487 for (i = 0; dirs[i] != NULL; i++)
2489 MimeInfoCacheDir *dir;
2491 dir = mime_info_cache_dir_new (dirs[i]);
2495 mime_info_cache_dir_init (dir);
2496 mime_info_cache_dir_init_defaults_list (dir);
2497 mime_info_cache_dir_init_mimeapps_list (dir);
2499 mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
2505 mime_info_cache_update_dir_lists (void)
2509 tmp = mime_info_cache->dirs;
2513 MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
2515 /* No need to do this if we had file monitors... */
2516 mime_info_cache_blow_global_cache ();
2517 mime_info_cache_dir_init (dir);
2518 mime_info_cache_dir_init_defaults_list (dir);
2519 mime_info_cache_dir_init_mimeapps_list (dir);
2526 mime_info_cache_init (void)
2528 G_LOCK (mime_info_cache);
2529 if (mime_info_cache == NULL)
2530 mime_info_cache_init_dir_lists ();
2536 if (now >= mime_info_cache->last_stat_time + 10)
2538 mime_info_cache_update_dir_lists ();
2539 mime_info_cache->last_stat_time = now;
2543 if (mime_info_cache->should_ping_mime_monitor)
2545 /* g_idle_add (emit_mime_changed, NULL); */
2546 mime_info_cache->should_ping_mime_monitor = FALSE;
2549 G_UNLOCK (mime_info_cache);
2552 static MimeInfoCache *
2553 mime_info_cache_new (void)
2555 MimeInfoCache *cache;
2557 cache = g_new0 (MimeInfoCache, 1);
2559 cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2560 (GDestroyNotify) g_free,
2561 (GDestroyNotify) g_free);
2566 mime_info_cache_free (MimeInfoCache *cache)
2571 g_list_foreach (cache->dirs,
2572 (GFunc) mime_info_cache_dir_free,
2574 g_list_free (cache->dirs);
2575 g_hash_table_destroy (cache->global_defaults_cache);
2580 * mime_info_cache_reload:
2581 * @dir: directory path which needs reloading.
2583 * Reload the mime information for the @dir.
2586 mime_info_cache_reload (const char *dir)
2588 /* FIXME: just reload the dir that needs reloading,
2589 * don't blow the whole cache
2591 if (mime_info_cache != NULL)
2593 G_LOCK (mime_info_cache);
2594 mime_info_cache_free (mime_info_cache);
2595 mime_info_cache = NULL;
2596 G_UNLOCK (mime_info_cache);
2601 append_desktop_entry (GList *list,
2602 const char *desktop_entry,
2603 GList *removed_entries)
2605 /* Add if not already in list, and valid */
2606 if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
2607 !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
2608 list = g_list_prepend (list, g_strdup (desktop_entry));
2614 * get_all_desktop_entries_for_mime_type:
2615 * @mime_type: a mime type.
2616 * @except: NULL or a strv list
2618 * Returns all the desktop ids for @mime_type. The desktop files
2619 * are listed in an order so that default applications are listed before
2620 * non-default ones, and handlers for inherited mimetypes are listed
2621 * after the base ones.
2623 * Optionally doesn't list the desktop ids given in the @except
2625 * Return value: a #GList containing the desktop ids which claim
2626 * to handle @mime_type.
2629 get_all_desktop_entries_for_mime_type (const char *base_mime_type,
2630 const char **except)
2632 GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
2633 MimeInfoCacheDir *dir;
2636 char **default_entries;
2637 char **removed_associations;
2642 mime_info_cache_init ();
2644 /* collect all ancestors */
2645 mime_types = _g_unix_content_type_get_parents (base_mime_type);
2646 array = g_ptr_array_new ();
2647 for (i = 0; mime_types[i]; i++)
2648 g_ptr_array_add (array, mime_types[i]);
2649 g_free (mime_types);
2650 for (i = 0; i < array->len; i++)
2652 anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
2653 for (j = 0; anc[j]; j++)
2655 for (k = 0; k < array->len; k++)
2657 if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
2660 if (k == array->len) /* not found */
2661 g_ptr_array_add (array, g_strdup (anc[j]));
2665 g_ptr_array_add (array, NULL);
2666 mime_types = (char **)g_ptr_array_free (array, FALSE);
2668 G_LOCK (mime_info_cache);
2670 removed_entries = NULL;
2671 desktop_entries = NULL;
2673 for (i = 0; except != NULL && except[i] != NULL; i++)
2674 removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
2676 for (i = 0; mime_types[i] != NULL; i++)
2678 mime_type = mime_types[i];
2680 /* Go through all apps listed as defaults */
2681 for (dir_list = mime_info_cache->dirs;
2683 dir_list = dir_list->next)
2685 dir = dir_list->data;
2687 /* First added associations from mimeapps.list */
2688 default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
2689 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2690 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2692 /* Then removed associations from mimeapps.list */
2693 removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
2694 for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
2695 removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
2697 /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
2698 default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
2699 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2700 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2703 /* Go through all entries that support the mimetype */
2704 for (dir_list = mime_info_cache->dirs;
2706 dir_list = dir_list->next)
2708 dir = dir_list->data;
2710 list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
2711 for (tmp = list; tmp != NULL; tmp = tmp->next)
2712 desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
2716 G_UNLOCK (mime_info_cache);
2718 g_strfreev (mime_types);
2720 g_list_foreach (removed_entries, (GFunc)g_free, NULL);
2721 g_list_free (removed_entries);
2723 desktop_entries = g_list_reverse (desktop_entries);
2725 return desktop_entries;
2728 /* GDesktopAppInfoLookup interface: */
2730 static void g_desktop_app_info_lookup_base_init (gpointer g_class);
2731 static void g_desktop_app_info_lookup_class_init (gpointer g_class,
2732 gpointer class_data);
2735 g_desktop_app_info_lookup_get_type (void)
2737 static volatile gsize g_define_type_id__volatile = 0;
2739 if (g_once_init_enter (&g_define_type_id__volatile))
2741 const GTypeInfo desktop_app_info_lookup_info =
2743 sizeof (GDesktopAppInfoLookupIface), /* class_size */
2744 g_desktop_app_info_lookup_base_init, /* base_init */
2745 NULL, /* base_finalize */
2746 g_desktop_app_info_lookup_class_init,
2747 NULL, /* class_finalize */
2748 NULL, /* class_data */
2750 0, /* n_preallocs */
2753 GType g_define_type_id =
2754 g_type_register_static (G_TYPE_INTERFACE, I_("GDesktopAppInfoLookup"),
2755 &desktop_app_info_lookup_info, 0);
2757 g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
2759 g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
2762 return g_define_type_id__volatile;
2766 g_desktop_app_info_lookup_class_init (gpointer g_class,
2767 gpointer class_data)
2772 g_desktop_app_info_lookup_base_init (gpointer g_class)
2777 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
2778 * @lookup: a #GDesktopAppInfoLookup
2779 * @uri_scheme: a string containing a URI scheme.
2781 * Gets the default application for launching applications
2782 * using this URI scheme for a particular GDesktopAppInfoLookup
2785 * The GDesktopAppInfoLookup interface and this function is used
2786 * to implement g_app_info_get_default_for_uri_scheme() backends
2787 * in a GIO module. There is no reason for applications to use it
2788 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
2790 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
2793 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
2794 const char *uri_scheme)
2796 GDesktopAppInfoLookupIface *iface;
2798 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
2800 iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
2802 return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
2805 #define __G_DESKTOP_APP_INFO_C__
2806 #include "gioaliasdef.c"