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
56 * Note that <filename><gio/gdesktopappinfo.h></filename> belongs to
57 * the UNIX-specific GIO interfaces, thus you have to use the
58 * <filename>gio-unix-2.0.pc</filename> pkg-config file when using it.
61 #define DEFAULT_APPLICATIONS_GROUP "Default Applications"
62 #define ADDED_ASSOCIATIONS_GROUP "Added Associations"
63 #define REMOVED_ASSOCIATIONS_GROUP "Removed Associations"
64 #define MIME_CACHE_GROUP "MIME Cache"
66 static void g_desktop_app_info_iface_init (GAppInfoIface *iface);
67 static GList * get_all_desktop_entries_for_mime_type (const char *base_mime_type,
69 static void mime_info_cache_reload (const char *dir);
70 static gboolean g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
76 * Information about an installed application from a desktop file.
78 struct _GDesktopAppInfo
80 GObject parent_instance;
86 /* FIXME: what about GenericName ? */
100 guint startup_notify : 1;
102 /* FIXME: what about StartupWMClass ? */
105 G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT,
106 G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
107 g_desktop_app_info_iface_init))
110 search_path_init (gpointer data)
113 const char * const *data_dirs;
114 const char *user_data_dir;
117 data_dirs = g_get_system_data_dirs ();
118 length = g_strv_length ((char **) data_dirs);
120 args = g_new (char *, length + 2);
123 user_data_dir = g_get_user_data_dir ();
124 args[j++] = g_build_filename (user_data_dir, "applications", NULL);
125 for (i = 0; i < length; i++)
126 args[j++] = g_build_filename (data_dirs[i],
127 "applications", NULL);
133 static const char * const *
134 get_applications_search_path (void)
136 static GOnce once_init = G_ONCE_INIT;
137 return g_once (&once_init, search_path_init, NULL);
141 g_desktop_app_info_finalize (GObject *object)
143 GDesktopAppInfo *info;
145 info = G_DESKTOP_APP_INFO (object);
147 g_free (info->desktop_id);
148 g_free (info->filename);
150 g_free (info->comment);
151 g_free (info->icon_name);
153 g_object_unref (info->icon);
154 g_strfreev (info->only_show_in);
155 g_strfreev (info->not_show_in);
156 g_free (info->try_exec);
158 g_free (info->binary);
161 G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
165 g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
167 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
169 gobject_class->finalize = g_desktop_app_info_finalize;
173 g_desktop_app_info_init (GDesktopAppInfo *local)
178 binary_from_exec (const char *exec)
180 const char *p, *start;
186 while (*p != ' ' && *p != 0)
189 return g_strndup (start, p - start);
194 * g_desktop_app_info_new_from_keyfile:
195 * @key_file: an opened #GKeyFile
197 * Creates a new #GDesktopAppInfo.
199 * Returns: a new #GDesktopAppInfo or %NULL on error.
204 g_desktop_app_info_new_from_keyfile (GKeyFile *key_file)
206 GDesktopAppInfo *info;
211 start_group = g_key_file_get_start_group (key_file);
212 if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
214 g_free (start_group);
217 g_free (start_group);
219 type = g_key_file_get_string (key_file,
220 G_KEY_FILE_DESKTOP_GROUP,
221 G_KEY_FILE_DESKTOP_KEY_TYPE,
223 if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
230 try_exec = g_key_file_get_string (key_file,
231 G_KEY_FILE_DESKTOP_GROUP,
232 G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
234 if (try_exec && try_exec[0] != '\0')
237 t = g_find_program_in_path (try_exec);
246 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
247 info->filename = NULL;
249 info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
250 info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
251 info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
252 info->icon_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
253 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);
254 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);
255 info->try_exec = try_exec;
256 info->exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
257 info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
258 info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
259 info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
260 info->no_fuse = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GIO-NoFuse", NULL) != FALSE;
261 info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
266 if (g_path_is_absolute (info->icon_name))
270 file = g_file_new_for_path (info->icon_name);
271 info->icon = g_file_icon_new (file);
272 g_object_unref (file);
278 /* Work around a common mistake in desktop files */
279 if ((p = strrchr (info->icon_name, '.')) != NULL &&
280 (strcmp (p, ".png") == 0 ||
281 strcmp (p, ".xpm") == 0 ||
282 strcmp (p, ".svg") == 0))
285 info->icon = g_themed_icon_new (info->icon_name);
290 info->binary = binary_from_exec (info->exec);
292 if (info->path && info->path[0] == '\0')
302 * g_desktop_app_info_new_from_filename:
303 * @filename: the path of a desktop file, in the GLib filename encoding
305 * Creates a new #GDesktopAppInfo.
307 * Returns: a new #GDesktopAppInfo or %NULL on error.
310 g_desktop_app_info_new_from_filename (const char *filename)
313 GDesktopAppInfo *info = NULL;
315 key_file = g_key_file_new ();
317 if (g_key_file_load_from_file (key_file,
322 info = g_desktop_app_info_new_from_keyfile (key_file);
324 info->filename = g_strdup (filename);
327 g_key_file_free (key_file);
333 * g_desktop_app_info_new:
334 * @desktop_id: the desktop file id
336 * Creates a new #GDesktopAppInfo based on a desktop file id.
338 * A desktop file id is the basename of the desktop file, including the
339 * .desktop extension. GIO is looking for a desktop file with this name
340 * in the <filename>applications</filename> subdirectories of the XDG data
341 * directories (i.e. the directories specified in the
342 * <envar>XDG_DATA_HOME</envar> and <envar>XDG_DATA_DIRS</envar> environment
343 * variables). GIO also supports the prefix-to-subdirectory mapping that is
344 * described in the <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Menu Spec</ulink>
345 * (i.e. a desktop id of kde-foo.desktop will match
346 * <filename>/usr/share/applications/kde/foo.desktop</filename>).
348 * Returns: a new #GDesktopAppInfo, or %NULL if no desktop file with that id
351 g_desktop_app_info_new (const char *desktop_id)
353 GDesktopAppInfo *appinfo;
354 const char * const *dirs;
358 dirs = get_applications_search_path ();
360 basename = g_strdup (desktop_id);
362 for (i = 0; dirs[i] != NULL; i++)
367 filename = g_build_filename (dirs[i], desktop_id, NULL);
368 appinfo = g_desktop_app_info_new_from_filename (filename);
374 while ((p = strchr (p, '-')) != NULL)
378 filename = g_build_filename (dirs[i], basename, NULL);
379 appinfo = g_desktop_app_info_new_from_filename (filename);
394 appinfo->desktop_id = g_strdup (desktop_id);
396 if (g_desktop_app_info_get_is_hidden (appinfo))
398 g_object_unref (appinfo);
406 g_desktop_app_info_dup (GAppInfo *appinfo)
408 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
409 GDesktopAppInfo *new_info;
411 new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
413 new_info->filename = g_strdup (info->filename);
414 new_info->desktop_id = g_strdup (info->desktop_id);
416 new_info->name = g_strdup (info->name);
417 new_info->comment = g_strdup (info->comment);
418 new_info->nodisplay = info->nodisplay;
419 new_info->icon_name = g_strdup (info->icon_name);
420 new_info->icon = g_object_ref (info->icon);
421 new_info->only_show_in = g_strdupv (info->only_show_in);
422 new_info->not_show_in = g_strdupv (info->not_show_in);
423 new_info->try_exec = g_strdup (info->try_exec);
424 new_info->exec = g_strdup (info->exec);
425 new_info->binary = g_strdup (info->binary);
426 new_info->path = g_strdup (info->path);
427 new_info->hidden = info->hidden;
428 new_info->terminal = info->terminal;
429 new_info->startup_notify = info->startup_notify;
431 return G_APP_INFO (new_info);
435 g_desktop_app_info_equal (GAppInfo *appinfo1,
438 GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
439 GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
441 if (info1->desktop_id == NULL ||
442 info2->desktop_id == NULL)
443 return info1 == info2;
445 return strcmp (info1->desktop_id, info2->desktop_id) == 0;
449 g_desktop_app_info_get_id (GAppInfo *appinfo)
451 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
453 return info->desktop_id;
457 g_desktop_app_info_get_name (GAppInfo *appinfo)
459 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
461 if (info->name == NULL)
467 * g_desktop_app_info_get_is_hidden:
468 * @info: a #GDesktopAppInfo.
470 * A desktop file is hidden if the Hidden key in it is
473 * Returns: %TRUE if hidden, %FALSE otherwise.
476 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
482 g_desktop_app_info_get_description (GAppInfo *appinfo)
484 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
486 return info->comment;
490 g_desktop_app_info_get_executable (GAppInfo *appinfo)
492 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
498 g_desktop_app_info_get_commandline (GAppInfo *appinfo)
500 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
506 g_desktop_app_info_get_icon (GAppInfo *appinfo)
508 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
514 expand_macro_single (char macro, char *uri)
520 file = g_file_new_for_uri (uri);
521 path = g_file_get_path (file);
522 g_object_unref (file);
528 result = g_shell_quote (uri);
533 result = g_shell_quote (path);
539 name = g_path_get_dirname (path);
540 result = g_shell_quote (name);
548 name = g_path_get_basename (path);
549 result = g_shell_quote (name);
561 expand_macro (char macro,
563 GDesktopAppInfo *info,
566 GList *uris = *uri_list;
568 gboolean force_file_uri;
569 char force_file_uri_macro;
572 g_return_if_fail (exec != NULL);
574 /* On %u and %U, pass POSIX file path pointing to the URI via
575 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
576 * running or the URI doesn't have a POSIX file path via FUSE
577 * we'll just pass the URI.
579 force_file_uri_macro = macro;
580 force_file_uri = FALSE;
586 force_file_uri_macro = 'f';
587 force_file_uri = TRUE;
590 force_file_uri_macro = 'F';
591 force_file_uri = TRUE;
607 if (!force_file_uri ||
608 /* Pass URI if it contains an anchor */
609 strchr (uri, '#') != NULL)
611 expanded = expand_macro_single (macro, uri);
615 expanded = expand_macro_single (force_file_uri_macro, uri);
616 if (expanded == NULL)
617 expanded = expand_macro_single (macro, uri);
622 g_string_append (exec, expanded);
638 if (!force_file_uri ||
639 /* Pass URI if it contains an anchor */
640 strchr (uri, '#') != NULL)
642 expanded = expand_macro_single (macro, uri);
646 expanded = expand_macro_single (force_file_uri_macro, uri);
647 if (expanded == NULL)
648 expanded = expand_macro_single (macro, uri);
653 g_string_append (exec, expanded);
659 if (uris != NULL && expanded)
660 g_string_append_c (exec, ' ');
668 g_string_append (exec, "--icon ");
669 g_string_append (exec, info->icon_name);
675 g_string_append (exec, info->name);
680 g_string_append (exec, info->filename);
683 case 'm': /* deprecated */
687 g_string_append_c (exec, '%');
695 expand_application_parameters (GDesktopAppInfo *info,
701 GList *uri_list = *uris;
702 const char *p = info->exec;
703 GString *expanded_exec = g_string_new (NULL);
706 if (info->exec == NULL)
708 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
709 _("Desktop file didn't specify Exec field"));
715 if (p[0] == '%' && p[1] != '\0')
717 expand_macro (p[1], expanded_exec, info, uris);
721 g_string_append_c (expanded_exec, *p);
726 /* No file substitutions */
727 if (uri_list == *uris && uri_list != NULL)
729 /* If there is no macro default to %f. This is also what KDE does */
730 g_string_append_c (expanded_exec, ' ');
731 expand_macro ('f', expanded_exec, info, uris);
734 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
735 g_string_free (expanded_exec, TRUE);
740 prepend_terminal_to_vector (int *argc,
747 char **term_argv = NULL;
752 g_return_val_if_fail (argc != NULL, FALSE);
753 g_return_val_if_fail (argv != NULL, FALSE);
761 /* compute size if not given */
764 for (i = 0; the_argv[i] != NULL; i++)
770 term_argv = g_new0 (char *, 3);
772 check = g_find_program_in_path ("gnome-terminal");
775 term_argv[0] = check;
776 /* Note that gnome-terminal takes -x and
777 * as -e in gnome-terminal is broken we use that. */
778 term_argv[1] = g_strdup ("-x");
783 check = g_find_program_in_path ("nxterm");
785 check = g_find_program_in_path ("color-xterm");
787 check = g_find_program_in_path ("rxvt");
789 check = g_find_program_in_path ("xterm");
791 check = g_find_program_in_path ("dtterm");
794 check = g_strdup ("xterm");
795 g_warning ("couldn't find a terminal, falling back to xterm");
797 term_argv[0] = check;
798 term_argv[1] = g_strdup ("-e");
801 real_argc = term_argc + *argc;
802 real_argv = g_new (char *, real_argc + 1);
804 for (i = 0; i < term_argc; i++)
805 real_argv[i] = term_argv[i];
807 for (j = 0; j < *argc; j++, i++)
808 real_argv[i] = (char *)the_argv[j];
816 /* we use g_free here as we sucked all the inner strings
817 * out from it into real_argv */
822 #endif /* G_OS_WIN32 */
825 /* '=' is the new '\0'.
826 * DO NOT CALL unless at least one string ends with '='
829 is_env (const char *a,
834 if (*a == 0 || *b == 0)
847 /* free with g_strfreev */
849 replace_env_var (char **old_environ,
851 const char *new_value)
853 int length, new_length;
854 int index, new_index;
858 /* do two things at once:
859 * - discover the length of the environment ('length')
860 * - find the location (if any) of the env var ('index')
863 for (length = 0; old_environ[length]; length++)
865 /* if we already have it in our environment, replace */
866 if (is_env (old_environ[length], env_var))
871 /* no current env var, no desired env value.
874 if (new_value == NULL && index == -1)
877 /* in all cases now, we will be using a modified environment.
878 * determine its length and allocated it.
881 * new_index = location to insert, if any
882 * new_length = length of the new array
883 * new_environ = the pointer array for the new environment
886 if (new_value == NULL && index >= 0)
888 /* in this case, we will be removing an entry */
889 new_length = length - 1;
892 else if (new_value != NULL && index < 0)
894 /* in this case, we will be adding an entry to the end */
895 new_length = length + 1;
899 /* in this case, we will be replacing the existing entry */
905 new_environ = g_malloc (sizeof (char *) * (new_length + 1));
906 new_environ[new_length] = NULL;
908 /* now we do the copying.
909 * for each entry in the new environment, we decide what to do
913 for (new_i = 0; new_i < new_length; new_i++)
915 if (new_i == new_index)
917 /* insert our new item */
918 new_environ[new_i] = g_strconcat (env_var,
923 /* if we had an old entry, skip it now */
929 /* if this is the old DESKTOP_STARTUP_ID, skip it */
933 /* copy an old item */
934 new_environ[new_i] = g_strdup (old_environ[i]);
939 g_strfreev (old_environ);
945 uri_list_segment_to_files (GList *start,
952 while (start != NULL && start != end)
954 file = g_file_new_for_uri ((char *)start->data);
955 res = g_list_prepend (res, file);
959 return g_list_reverse (res);
962 #ifdef HAVE__NSGETENVIRON
963 #define environ (*_NSGetEnviron())
964 #elif !defined(G_OS_WIN32)
966 /* According to the Single Unix Specification, environ is not in
967 * * any system header, although unistd.h often declares it.
969 extern char **environ;
973 g_desktop_app_info_launch_uris (GAppInfo *appinfo,
975 GAppLaunchContext *launch_context,
978 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
979 gboolean completed = FALSE;
981 GList *launched_files;
988 g_return_val_if_fail (appinfo != NULL, FALSE);
996 if (!expand_application_parameters (info, &uris,
997 &argc, &argv, error))
1000 if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
1002 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1003 _("Unable to find terminal required for application"));
1010 launched_files = uri_list_segment_to_files (old_uris, uris);
1012 display = g_app_launch_context_get_display (launch_context,
1017 if (info->startup_notify)
1018 sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
1022 if (display || sn_id)
1026 envp = g_new0 (char *, 1);
1028 envp = g_strdupv (environ);
1032 envp = replace_env_var (envp,
1037 envp = replace_env_var (envp,
1038 "DESKTOP_STARTUP_ID",
1044 g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
1045 g_list_free (launched_files);
1048 if (!g_spawn_async (info->path, /* working directory */
1051 G_SPAWN_SEARCH_PATH /* flags */,
1052 NULL /* child_setup */,
1054 NULL /* child_pid */,
1059 g_app_launch_context_launch_failed (launch_context, sn_id);
1073 while (uris != NULL);
1085 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
1087 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1089 return info->exec &&
1090 ((strstr (info->exec, "%u") != NULL) ||
1091 (strstr (info->exec, "%U") != NULL));
1095 g_desktop_app_info_supports_files (GAppInfo *appinfo)
1097 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1099 return info->exec &&
1100 ((strstr (info->exec, "%f") != NULL) ||
1101 (strstr (info->exec, "%F") != NULL));
1105 g_desktop_app_info_launch (GAppInfo *appinfo,
1107 GAppLaunchContext *launch_context,
1117 uri = g_file_get_uri (files->data);
1118 uris = g_list_prepend (uris, uri);
1119 files = files->next;
1122 uris = g_list_reverse (uris);
1124 res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
1126 g_list_foreach (uris, (GFunc)g_free, NULL);
1132 G_LOCK_DEFINE_STATIC (g_desktop_env);
1133 static gchar *g_desktop_env = NULL;
1136 * g_desktop_app_info_set_desktop_env:
1137 * @desktop_env: a string specifying what desktop this is
1139 * Sets the name of the desktop that the application is running in.
1140 * This is used by g_app_info_should_show() to evaluate the
1141 * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
1142 * desktop entry fields.
1144 * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop
1145 * Menu specification</ulink> recognizes the following:
1147 * <member>GNOME</member>
1148 * <member>KDE</member>
1149 * <member>ROX</member>
1150 * <member>XFCE</member>
1151 * <member>Old</member>
1154 * Should be called only once; subsequent calls are ignored.
1157 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
1159 G_LOCK (g_desktop_env);
1161 g_desktop_env = g_strdup (desktop_env);
1162 G_UNLOCK (g_desktop_env);
1166 g_desktop_app_info_should_show (GAppInfo *appinfo)
1168 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1170 const gchar *desktop_env;
1173 if (info->nodisplay)
1176 G_LOCK (g_desktop_env);
1177 desktop_env = g_desktop_env;
1178 G_UNLOCK (g_desktop_env);
1180 if (info->only_show_in)
1182 if (desktop_env == NULL)
1186 for (i = 0; info->only_show_in[i] != NULL; i++)
1188 if (strcmp (info->only_show_in[i], desktop_env) == 0)
1198 if (info->not_show_in && desktop_env)
1200 for (i = 0; info->not_show_in[i] != NULL; i++)
1202 if (strcmp (info->not_show_in[i], desktop_env) == 0)
1216 ensure_dir (DirType type,
1219 char *path, *display_name;
1222 if (type == APP_DIR)
1223 path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
1225 path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
1228 if (g_mkdir_with_parents (path, 0700) == 0)
1232 display_name = g_filename_display_name (path);
1233 if (type == APP_DIR)
1234 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1235 _("Can't create user application configuration folder %s: %s"),
1236 display_name, g_strerror (errsv));
1238 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1239 _("Can't create user MIME configuration folder %s: %s"),
1240 display_name, g_strerror (errsv));
1242 g_free (display_name);
1249 update_mimeapps_list (const char *desktop_id,
1250 const char *content_type,
1251 gboolean add_as_default,
1252 gboolean add_non_default,
1256 char *dirname, *filename;
1258 gboolean load_succeeded, res;
1259 char **old_list, **list;
1260 GList *system_list, *l;
1261 gsize length, data_size;
1264 char **content_types;
1266 /* Don't add both at start and end */
1267 g_assert (!(add_as_default && add_non_default));
1269 dirname = ensure_dir (APP_DIR, error);
1273 filename = g_build_filename (dirname, "mimeapps.list", NULL);
1276 key_file = g_key_file_new ();
1277 load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
1278 if (!load_succeeded || !g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP))
1280 g_key_file_free (key_file);
1281 key_file = g_key_file_new ();
1286 content_types = g_new (char *, 2);
1287 content_types[0] = g_strdup (content_type);
1288 content_types[1] = NULL;
1292 content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
1295 for (k = 0; content_types && content_types[k]; k++)
1297 /* Add to the right place in the list */
1300 old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
1301 content_types[k], &length, NULL);
1303 list = g_new (char *, 1 + length + 1);
1307 list[i++] = g_strdup (desktop_id);
1310 for (j = 0; old_list[j] != NULL; j++)
1312 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1313 list[i++] = g_strdup (old_list[j]);
1314 else if (add_non_default)
1316 /* If adding as non-default, and it's already in,
1317 don't change order of desktop ids */
1318 add_non_default = FALSE;
1319 list[i++] = g_strdup (old_list[j]);
1324 if (add_non_default)
1326 /* We're adding as non-default, and it wasn't already in the list,
1327 so we add at the end. But to avoid listing the app before the
1328 current system default (thus changing the default) we have to
1329 add the current list of (not yet listed) apps before it. */
1331 list[i] = NULL; /* Terminate current list so we can use it */
1332 system_list = get_all_desktop_entries_for_mime_type (content_type, (const char **)list);
1334 list = g_renew (char *, list, 1 + length + g_list_length (system_list) + 1);
1336 for (l = system_list; l != NULL; l = l->next)
1338 list[i++] = l->data; /* no strdup, taking ownership */
1339 if (g_strcmp0 (l->data, desktop_id) == 0)
1340 add_non_default = FALSE;
1342 g_list_free (system_list);
1344 if (add_non_default)
1345 list[i++] = g_strdup (desktop_id);
1350 g_strfreev (old_list);
1352 if (list[0] == NULL || desktop_id == NULL)
1353 g_key_file_remove_key (key_file,
1354 ADDED_ASSOCIATIONS_GROUP,
1358 g_key_file_set_string_list (key_file,
1359 ADDED_ASSOCIATIONS_GROUP,
1361 (const char * const *)list, i);
1368 /* reuse the list from above */
1372 g_strfreev (content_types);
1373 content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
1376 for (k = 0; content_types && content_types[k]; k++)
1378 /* Remove from removed associations group (unless remove) */
1381 old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
1382 content_types[k], &length, NULL);
1384 list = g_new (char *, 1 + length + 1);
1388 list[i++] = g_strdup (desktop_id);
1391 for (j = 0; old_list[j] != NULL; j++)
1393 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1394 list[i++] = g_strdup (old_list[j]);
1399 g_strfreev (old_list);
1401 if (list[0] == NULL || desktop_id == NULL)
1402 g_key_file_remove_key (key_file,
1403 REMOVED_ASSOCIATIONS_GROUP,
1407 g_key_file_set_string_list (key_file,
1408 REMOVED_ASSOCIATIONS_GROUP,
1410 (const char * const *)list, i);
1415 g_strfreev (content_types);
1417 data = g_key_file_to_data (key_file, &data_size, error);
1418 g_key_file_free (key_file);
1420 res = g_file_set_contents (filename, data, data_size, error);
1422 mime_info_cache_reload (NULL);
1431 g_desktop_app_info_set_as_default_for_type (GAppInfo *appinfo,
1432 const char *content_type,
1435 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1437 if (!g_desktop_app_info_ensure_saved (info, error))
1440 return update_mimeapps_list (info->desktop_id, content_type, TRUE, FALSE, FALSE, error);
1444 update_program_done (GPid pid,
1448 /* Did the application exit correctly */
1449 if (WIFEXITED (status) &&
1450 WEXITSTATUS (status) == 0)
1452 /* Here we could clean out any caches in use */
1457 run_update_command (char *command,
1466 GError *error = NULL;
1469 argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
1471 if (g_spawn_async ("/", argv,
1473 G_SPAWN_SEARCH_PATH |
1474 G_SPAWN_STDOUT_TO_DEV_NULL |
1475 G_SPAWN_STDERR_TO_DEV_NULL |
1476 G_SPAWN_DO_NOT_REAP_CHILD,
1477 NULL, NULL, /* No setup function */
1480 g_child_watch_add (pid, update_program_done, NULL);
1483 /* If we get an error at this point, it's quite likely the user doesn't
1484 * have an installed copy of either 'update-mime-database' or
1485 * 'update-desktop-database'. I don't think we want to popup an error
1486 * dialog at this point, so we just do a g_warning to give the user a
1487 * chance of debugging it.
1489 g_warning ("%s", error->message);
1496 g_desktop_app_info_set_as_default_for_extension (GAppInfo *appinfo,
1497 const char *extension,
1500 char *filename, *basename, *mimetype;
1504 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
1507 dirname = ensure_dir (MIMETYPE_DIR, error);
1511 basename = g_strdup_printf ("user-extension-%s.xml", extension);
1512 filename = g_build_filename (dirname, basename, NULL);
1516 mimetype = g_strdup_printf ("application/x-extension-%s", extension);
1518 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1523 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1524 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
1525 " <mime-type type=\"%s\">\n"
1526 " <comment>%s document</comment>\n"
1527 " <glob pattern=\"*.%s\"/>\n"
1529 "</mime-info>\n", mimetype, extension, extension);
1531 g_file_set_contents (filename, contents, -1, NULL);
1534 run_update_command ("update-mime-database", "mime");
1538 res = g_desktop_app_info_set_as_default_for_type (appinfo,
1548 g_desktop_app_info_add_supports_type (GAppInfo *appinfo,
1549 const char *content_type,
1552 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1554 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1557 return update_mimeapps_list (info->desktop_id, content_type, FALSE, TRUE, FALSE, error);
1561 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
1567 g_desktop_app_info_remove_supports_type (GAppInfo *appinfo,
1568 const char *content_type,
1571 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1573 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1576 return update_mimeapps_list (info->desktop_id, content_type, FALSE, FALSE, TRUE, error);
1580 g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
1586 char *data, *desktop_id;
1591 if (info->filename != NULL)
1594 /* This is only used for object created with
1595 * g_app_info_create_from_commandline. All other
1596 * object should have a filename
1599 dirname = ensure_dir (APP_DIR, error);
1603 key_file = g_key_file_new ();
1605 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1606 "Encoding", "UTF-8");
1607 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1608 G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
1609 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1610 G_KEY_FILE_DESKTOP_KEY_TYPE,
1611 G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
1613 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1614 G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
1616 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1617 G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
1619 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1620 G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
1622 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1623 G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
1625 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1626 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
1628 data = g_key_file_to_data (key_file, &data_size, NULL);
1629 g_key_file_free (key_file);
1631 desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
1632 filename = g_build_filename (dirname, desktop_id, NULL);
1633 g_free (desktop_id);
1636 fd = g_mkstemp (filename);
1641 display_name = g_filename_display_name (filename);
1642 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1643 _("Can't create user desktop file %s"), display_name);
1644 g_free (display_name);
1650 desktop_id = g_path_get_basename (filename);
1654 res = g_file_set_contents (filename, data, data_size, error);
1657 g_free (desktop_id);
1662 info->filename = filename;
1663 info->desktop_id = desktop_id;
1665 run_update_command ("update-desktop-database", "applications");
1671 g_desktop_app_info_can_delete (GAppInfo *appinfo)
1673 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1677 if (strstr (info->filename, "/userapp-"))
1678 return g_access (info->filename, W_OK) == 0;
1685 g_desktop_app_info_delete (GAppInfo *appinfo)
1687 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1691 if (g_remove (info->filename) == 0)
1693 update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL);
1695 g_free (info->filename);
1696 info->filename = NULL;
1697 g_free (info->desktop_id);
1698 info->desktop_id = NULL;
1708 * g_app_info_create_from_commandline:
1709 * @commandline: the commandline to use
1710 * @application_name: the application name, or %NULL to use @commandline
1711 * @flags: flags that can specify details of the created #GAppInfo
1712 * @error: a #GError location to store the error occuring, %NULL to ignore.
1714 * Creates a new #GAppInfo from the given information.
1716 * Returns: new #GAppInfo for given command.
1719 g_app_info_create_from_commandline (const char *commandline,
1720 const char *application_name,
1721 GAppInfoCreateFlags flags,
1726 GDesktopAppInfo *info;
1728 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1730 info->filename = NULL;
1731 info->desktop_id = NULL;
1733 info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL;
1734 info->startup_notify = FALSE;
1735 info->hidden = FALSE;
1736 if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS)
1737 info->exec = g_strconcat (commandline, " %u", NULL);
1739 info->exec = g_strconcat (commandline, " %f", NULL);
1740 info->nodisplay = TRUE;
1741 info->binary = binary_from_exec (info->exec);
1743 if (application_name)
1744 info->name = g_strdup (application_name);
1747 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
1748 split = g_strsplit (commandline, " ", 2);
1749 basename = g_path_get_basename (split[0]);
1751 info->name = basename;
1752 if (info->name == NULL)
1753 info->name = g_strdup ("custom");
1755 info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
1757 return G_APP_INFO (info);
1761 g_desktop_app_info_iface_init (GAppInfoIface *iface)
1763 iface->dup = g_desktop_app_info_dup;
1764 iface->equal = g_desktop_app_info_equal;
1765 iface->get_id = g_desktop_app_info_get_id;
1766 iface->get_name = g_desktop_app_info_get_name;
1767 iface->get_description = g_desktop_app_info_get_description;
1768 iface->get_executable = g_desktop_app_info_get_executable;
1769 iface->get_icon = g_desktop_app_info_get_icon;
1770 iface->launch = g_desktop_app_info_launch;
1771 iface->supports_uris = g_desktop_app_info_supports_uris;
1772 iface->supports_files = g_desktop_app_info_supports_files;
1773 iface->launch_uris = g_desktop_app_info_launch_uris;
1774 iface->should_show = g_desktop_app_info_should_show;
1775 iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
1776 iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
1777 iface->add_supports_type = g_desktop_app_info_add_supports_type;
1778 iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
1779 iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
1780 iface->can_delete = g_desktop_app_info_can_delete;
1781 iface->do_delete = g_desktop_app_info_delete;
1782 iface->get_commandline = g_desktop_app_info_get_commandline;
1786 app_info_in_list (GAppInfo *info,
1789 while (list != NULL)
1791 if (g_app_info_equal (info, list->data))
1800 * g_app_info_get_all_for_type:
1801 * @content_type: the content type to find a #GAppInfo for
1803 * Gets a list of all #GAppInfo s for a given content type.
1805 * Returns: #GList of #GAppInfo s for given @content_type
1806 * or %NULL on error.
1809 g_app_info_get_all_for_type (const char *content_type)
1811 GList *desktop_entries, *l;
1813 GDesktopAppInfo *info;
1815 g_return_val_if_fail (content_type != NULL, NULL);
1817 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1820 for (l = desktop_entries; l != NULL; l = l->next)
1822 char *desktop_entry = l->data;
1824 info = g_desktop_app_info_new (desktop_entry);
1827 if (app_info_in_list (G_APP_INFO (info), infos))
1828 g_object_unref (info);
1830 infos = g_list_prepend (infos, info);
1832 g_free (desktop_entry);
1835 g_list_free (desktop_entries);
1837 return g_list_reverse (infos);
1841 * g_app_info_reset_type_associations:
1842 * @content_type: a content type
1844 * Removes all changes to the type associations done by
1845 * g_app_info_set_as_default_for_type(),
1846 * g_app_info_set_as_default_for_extension(),
1847 * g_app_info_add_supports_type() of g_app_info_remove_supports_type().
1852 g_app_info_reset_type_associations (const char *content_type)
1854 update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
1858 * g_app_info_get_default_for_type:
1859 * @content_type: the content type to find a #GAppInfo for
1860 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
1863 * Gets the #GAppInfo that correspond to a given content type.
1865 * Returns: #GAppInfo for given @content_type or %NULL on error.
1868 g_app_info_get_default_for_type (const char *content_type,
1869 gboolean must_support_uris)
1871 GList *desktop_entries, *l;
1874 g_return_val_if_fail (content_type != NULL, NULL);
1876 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1879 for (l = desktop_entries; l != NULL; l = l->next)
1881 char *desktop_entry = l->data;
1883 info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
1886 if (must_support_uris && !g_app_info_supports_uris (info))
1888 g_object_unref (info);
1896 g_list_foreach (desktop_entries, (GFunc)g_free, NULL);
1897 g_list_free (desktop_entries);
1903 * g_app_info_get_default_for_uri_scheme:
1904 * @uri_scheme: a string containing a URI scheme.
1906 * Gets the default application for launching applications
1907 * using this URI scheme. A URI scheme is the initial part
1908 * of the URI, up to but not including the ':', e.g. "http",
1911 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
1914 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
1916 static gsize lookup = 0;
1918 if (g_once_init_enter (&lookup))
1920 gsize setup_value = 1;
1921 GDesktopAppInfoLookup *lookup_instance;
1922 const char *use_this;
1923 GIOExtensionPoint *ep;
1924 GIOExtension *extension;
1927 use_this = g_getenv ("GIO_USE_URI_ASSOCIATION");
1929 /* Ensure vfs in modules loaded */
1930 _g_io_modules_ensure_loaded ();
1932 ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME);
1934 lookup_instance = NULL;
1937 extension = g_io_extension_point_get_extension_by_name (ep, use_this);
1939 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1942 if (lookup_instance == NULL)
1944 for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
1946 extension = l->data;
1947 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1948 if (lookup_instance != NULL)
1953 if (lookup_instance != NULL)
1954 setup_value = (gsize)lookup_instance;
1956 g_once_init_leave (&lookup, setup_value);
1962 return g_desktop_app_info_lookup_get_default_for_uri_scheme (G_DESKTOP_APP_INFO_LOOKUP (lookup),
1968 get_apps_from_dir (GHashTable *apps,
1969 const char *dirname,
1973 const char *basename;
1974 char *filename, *subprefix, *desktop_id;
1976 GDesktopAppInfo *appinfo;
1978 dir = g_dir_open (dirname, 0, NULL);
1981 while ((basename = g_dir_read_name (dir)) != NULL)
1983 filename = g_build_filename (dirname, basename, NULL);
1984 if (g_str_has_suffix (basename, ".desktop"))
1986 desktop_id = g_strconcat (prefix, basename, NULL);
1988 /* Use _extended so we catch NULLs too (hidden) */
1989 if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
1991 appinfo = g_desktop_app_info_new_from_filename (filename);
1993 if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
1995 g_object_unref (appinfo);
2000 if (appinfo || hidden)
2002 g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
2006 /* Reuse instead of strdup here */
2007 appinfo->desktop_id = desktop_id;
2012 g_free (desktop_id);
2016 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
2018 subprefix = g_strconcat (prefix, basename, "-", NULL);
2019 get_apps_from_dir (apps, filename, subprefix);
2031 * g_app_info_get_all:
2033 * Gets a list of all of the applications currently registered
2036 * For desktop files, this includes applications that have
2037 * <literal>NoDisplay=true</literal> set or are excluded from
2038 * display by means of <literal>OnlyShowIn</literal> or
2039 * <literal>NotShowIn</literal>. See g_app_info_should_show().
2040 * The returned list does not include applications which have
2041 * the <literal>Hidden</literal> key set.
2043 * Returns: a newly allocated #GList of references to #GAppInfo<!---->s.
2046 g_app_info_get_all (void)
2048 const char * const *dirs;
2050 GHashTableIter iter;
2055 dirs = get_applications_search_path ();
2057 apps = g_hash_table_new_full (g_str_hash, g_str_equal,
2061 for (i = 0; dirs[i] != NULL; i++)
2062 get_apps_from_dir (apps, dirs[i], "");
2066 g_hash_table_iter_init (&iter, apps);
2067 while (g_hash_table_iter_next (&iter, NULL, &value))
2070 infos = g_list_prepend (infos, value);
2073 g_hash_table_destroy (apps);
2075 return g_list_reverse (infos);
2078 /* Cacheing of mimeinfo.cache and defaults.list files */
2082 GHashTable *mime_info_cache_map;
2083 GHashTable *defaults_list_map;
2084 GHashTable *mimeapps_list_added_map;
2085 GHashTable *mimeapps_list_removed_map;
2086 time_t mime_info_cache_timestamp;
2087 time_t defaults_list_timestamp;
2088 time_t mimeapps_list_timestamp;
2092 GList *dirs; /* mimeinfo.cache and defaults.list */
2093 GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
2094 time_t last_stat_time;
2095 guint should_ping_mime_monitor : 1;
2098 static MimeInfoCache *mime_info_cache = NULL;
2099 G_LOCK_DEFINE_STATIC (mime_info_cache);
2101 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2102 const char *mime_type,
2103 char **new_desktop_file_ids);
2105 static MimeInfoCache * mime_info_cache_new (void);
2108 destroy_info_cache_value (gpointer key,
2112 g_list_foreach (value, (GFunc)g_free, NULL);
2113 g_list_free (value);
2117 destroy_info_cache_map (GHashTable *info_cache_map)
2119 g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
2120 g_hash_table_destroy (info_cache_map);
2124 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
2125 const char *cache_file,
2131 filename = g_build_filename (dir->path, cache_file, NULL);
2133 if (g_stat (filename, &buf) < 0)
2140 if (buf.st_mtime != *timestamp)
2146 /* Call with lock held */
2148 remove_all (gpointer key,
2157 mime_info_cache_blow_global_cache (void)
2159 g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
2164 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
2168 gchar *filename, **mime_types;
2175 if (dir->mime_info_cache_map != NULL &&
2176 !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
2177 &dir->mime_info_cache_timestamp))
2180 if (dir->mime_info_cache_map != NULL)
2181 destroy_info_cache_map (dir->mime_info_cache_map);
2183 dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2184 (GDestroyNotify) g_free,
2187 key_file = g_key_file_new ();
2189 filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
2191 if (g_stat (filename, &buf) < 0)
2194 if (dir->mime_info_cache_timestamp > 0)
2195 mime_info_cache->should_ping_mime_monitor = TRUE;
2197 dir->mime_info_cache_timestamp = buf.st_mtime;
2199 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2204 if (load_error != NULL)
2207 mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
2210 if (load_error != NULL)
2213 for (i = 0; mime_types[i] != NULL; i++)
2215 gchar **desktop_file_ids;
2216 char *unaliased_type;
2217 desktop_file_ids = g_key_file_get_string_list (key_file,
2223 if (desktop_file_ids == NULL)
2226 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2227 mime_info_cache_dir_add_desktop_entries (dir,
2230 g_free (unaliased_type);
2232 g_strfreev (desktop_file_ids);
2235 g_strfreev (mime_types);
2236 g_key_file_free (key_file);
2241 g_key_file_free (key_file);
2243 if (mime_types != NULL)
2244 g_strfreev (mime_types);
2247 g_error_free (load_error);
2251 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
2255 gchar *filename, **mime_types;
2256 char *unaliased_type;
2257 char **desktop_file_ids;
2264 if (dir->defaults_list_map != NULL &&
2265 !mime_info_cache_dir_out_of_date (dir, "defaults.list",
2266 &dir->defaults_list_timestamp))
2269 if (dir->defaults_list_map != NULL)
2270 g_hash_table_destroy (dir->defaults_list_map);
2271 dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2272 g_free, (GDestroyNotify)g_strfreev);
2275 key_file = g_key_file_new ();
2277 filename = g_build_filename (dir->path, "defaults.list", NULL);
2278 if (g_stat (filename, &buf) < 0)
2281 if (dir->defaults_list_timestamp > 0)
2282 mime_info_cache->should_ping_mime_monitor = TRUE;
2284 dir->defaults_list_timestamp = buf.st_mtime;
2286 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2290 if (load_error != NULL)
2293 mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
2295 if (mime_types != NULL)
2297 for (i = 0; mime_types[i] != NULL; i++)
2299 desktop_file_ids = g_key_file_get_string_list (key_file,
2300 DEFAULT_APPLICATIONS_GROUP,
2304 if (desktop_file_ids == NULL)
2307 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2308 g_hash_table_replace (dir->defaults_list_map,
2313 g_strfreev (mime_types);
2316 g_key_file_free (key_file);
2321 g_key_file_free (key_file);
2323 if (mime_types != NULL)
2324 g_strfreev (mime_types);
2327 g_error_free (load_error);
2331 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
2335 gchar *filename, **mime_types;
2336 char *unaliased_type;
2337 char **desktop_file_ids;
2344 if (dir->mimeapps_list_added_map != NULL &&
2345 !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
2346 &dir->mimeapps_list_timestamp))
2349 if (dir->mimeapps_list_added_map != NULL)
2350 g_hash_table_destroy (dir->mimeapps_list_added_map);
2351 dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2352 g_free, (GDestroyNotify)g_strfreev);
2354 if (dir->mimeapps_list_removed_map != NULL)
2355 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2356 dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2357 g_free, (GDestroyNotify)g_strfreev);
2359 key_file = g_key_file_new ();
2361 filename = g_build_filename (dir->path, "mimeapps.list", NULL);
2362 if (g_stat (filename, &buf) < 0)
2365 if (dir->mimeapps_list_timestamp > 0)
2366 mime_info_cache->should_ping_mime_monitor = TRUE;
2368 dir->mimeapps_list_timestamp = buf.st_mtime;
2370 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2374 if (load_error != NULL)
2377 mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
2379 if (mime_types != NULL)
2381 for (i = 0; mime_types[i] != NULL; i++)
2383 desktop_file_ids = g_key_file_get_string_list (key_file,
2384 ADDED_ASSOCIATIONS_GROUP,
2388 if (desktop_file_ids == NULL)
2391 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2392 g_hash_table_replace (dir->mimeapps_list_added_map,
2397 g_strfreev (mime_types);
2400 mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
2402 if (mime_types != NULL)
2404 for (i = 0; mime_types[i] != NULL; i++)
2406 desktop_file_ids = g_key_file_get_string_list (key_file,
2407 REMOVED_ASSOCIATIONS_GROUP,
2411 if (desktop_file_ids == NULL)
2414 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2415 g_hash_table_replace (dir->mimeapps_list_removed_map,
2420 g_strfreev (mime_types);
2423 g_key_file_free (key_file);
2428 g_key_file_free (key_file);
2430 if (mime_types != NULL)
2431 g_strfreev (mime_types);
2434 g_error_free (load_error);
2437 static MimeInfoCacheDir *
2438 mime_info_cache_dir_new (const char *path)
2440 MimeInfoCacheDir *dir;
2442 dir = g_new0 (MimeInfoCacheDir, 1);
2443 dir->path = g_strdup (path);
2449 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
2454 if (dir->mime_info_cache_map != NULL)
2456 destroy_info_cache_map (dir->mime_info_cache_map);
2457 dir->mime_info_cache_map = NULL;
2461 if (dir->defaults_list_map != NULL)
2463 g_hash_table_destroy (dir->defaults_list_map);
2464 dir->defaults_list_map = NULL;
2467 if (dir->mimeapps_list_added_map != NULL)
2469 g_hash_table_destroy (dir->mimeapps_list_added_map);
2470 dir->mimeapps_list_added_map = NULL;
2473 if (dir->mimeapps_list_removed_map != NULL)
2475 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2476 dir->mimeapps_list_removed_map = NULL;
2483 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2484 const char *mime_type,
2485 char **new_desktop_file_ids)
2487 GList *desktop_file_ids;
2490 desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
2493 for (i = 0; new_desktop_file_ids[i] != NULL; i++)
2495 if (!g_list_find (desktop_file_ids, new_desktop_file_ids[i]))
2496 desktop_file_ids = g_list_append (desktop_file_ids,
2497 g_strdup (new_desktop_file_ids[i]));
2500 g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
2504 mime_info_cache_init_dir_lists (void)
2506 const char * const *dirs;
2509 mime_info_cache = mime_info_cache_new ();
2511 dirs = get_applications_search_path ();
2513 for (i = 0; dirs[i] != NULL; i++)
2515 MimeInfoCacheDir *dir;
2517 dir = mime_info_cache_dir_new (dirs[i]);
2521 mime_info_cache_dir_init (dir);
2522 mime_info_cache_dir_init_defaults_list (dir);
2523 mime_info_cache_dir_init_mimeapps_list (dir);
2525 mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
2531 mime_info_cache_update_dir_lists (void)
2535 tmp = mime_info_cache->dirs;
2539 MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
2541 /* No need to do this if we had file monitors... */
2542 mime_info_cache_blow_global_cache ();
2543 mime_info_cache_dir_init (dir);
2544 mime_info_cache_dir_init_defaults_list (dir);
2545 mime_info_cache_dir_init_mimeapps_list (dir);
2552 mime_info_cache_init (void)
2554 G_LOCK (mime_info_cache);
2555 if (mime_info_cache == NULL)
2556 mime_info_cache_init_dir_lists ();
2562 if (now >= mime_info_cache->last_stat_time + 10)
2564 mime_info_cache_update_dir_lists ();
2565 mime_info_cache->last_stat_time = now;
2569 if (mime_info_cache->should_ping_mime_monitor)
2571 /* g_idle_add (emit_mime_changed, NULL); */
2572 mime_info_cache->should_ping_mime_monitor = FALSE;
2575 G_UNLOCK (mime_info_cache);
2578 static MimeInfoCache *
2579 mime_info_cache_new (void)
2581 MimeInfoCache *cache;
2583 cache = g_new0 (MimeInfoCache, 1);
2585 cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2586 (GDestroyNotify) g_free,
2587 (GDestroyNotify) g_free);
2592 mime_info_cache_free (MimeInfoCache *cache)
2597 g_list_foreach (cache->dirs,
2598 (GFunc) mime_info_cache_dir_free,
2600 g_list_free (cache->dirs);
2601 g_hash_table_destroy (cache->global_defaults_cache);
2606 * mime_info_cache_reload:
2607 * @dir: directory path which needs reloading.
2609 * Reload the mime information for the @dir.
2612 mime_info_cache_reload (const char *dir)
2614 /* FIXME: just reload the dir that needs reloading,
2615 * don't blow the whole cache
2617 if (mime_info_cache != NULL)
2619 G_LOCK (mime_info_cache);
2620 mime_info_cache_free (mime_info_cache);
2621 mime_info_cache = NULL;
2622 G_UNLOCK (mime_info_cache);
2627 append_desktop_entry (GList *list,
2628 const char *desktop_entry,
2629 GList *removed_entries)
2631 /* Add if not already in list, and valid */
2632 if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
2633 !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
2634 list = g_list_prepend (list, g_strdup (desktop_entry));
2640 * get_all_desktop_entries_for_mime_type:
2641 * @mime_type: a mime type.
2642 * @except: NULL or a strv list
2644 * Returns all the desktop ids for @mime_type. The desktop files
2645 * are listed in an order so that default applications are listed before
2646 * non-default ones, and handlers for inherited mimetypes are listed
2647 * after the base ones.
2649 * Optionally doesn't list the desktop ids given in the @except
2651 * Return value: a #GList containing the desktop ids which claim
2652 * to handle @mime_type.
2655 get_all_desktop_entries_for_mime_type (const char *base_mime_type,
2656 const char **except)
2658 GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
2659 MimeInfoCacheDir *dir;
2662 char **default_entries;
2663 char **removed_associations;
2668 mime_info_cache_init ();
2670 /* collect all ancestors */
2671 mime_types = _g_unix_content_type_get_parents (base_mime_type);
2672 array = g_ptr_array_new ();
2673 for (i = 0; mime_types[i]; i++)
2674 g_ptr_array_add (array, mime_types[i]);
2675 g_free (mime_types);
2676 for (i = 0; i < array->len; i++)
2678 anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
2679 for (j = 0; anc[j]; j++)
2681 for (k = 0; k < array->len; k++)
2683 if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
2686 if (k == array->len) /* not found */
2687 g_ptr_array_add (array, g_strdup (anc[j]));
2691 g_ptr_array_add (array, NULL);
2692 mime_types = (char **)g_ptr_array_free (array, FALSE);
2694 G_LOCK (mime_info_cache);
2696 removed_entries = NULL;
2697 desktop_entries = NULL;
2699 for (i = 0; except != NULL && except[i] != NULL; i++)
2700 removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
2702 for (i = 0; mime_types[i] != NULL; i++)
2704 mime_type = mime_types[i];
2706 /* Go through all apps listed as defaults */
2707 for (dir_list = mime_info_cache->dirs;
2709 dir_list = dir_list->next)
2711 dir = dir_list->data;
2713 /* First added associations from mimeapps.list */
2714 default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
2715 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2716 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2718 /* Then removed associations from mimeapps.list */
2719 removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
2720 for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
2721 removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
2723 /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
2724 default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
2725 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2726 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2729 /* Go through all entries that support the mimetype */
2730 for (dir_list = mime_info_cache->dirs;
2732 dir_list = dir_list->next)
2734 dir = dir_list->data;
2736 list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
2737 for (tmp = list; tmp != NULL; tmp = tmp->next)
2738 desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
2742 G_UNLOCK (mime_info_cache);
2744 g_strfreev (mime_types);
2746 g_list_foreach (removed_entries, (GFunc)g_free, NULL);
2747 g_list_free (removed_entries);
2749 desktop_entries = g_list_reverse (desktop_entries);
2751 return desktop_entries;
2754 /* GDesktopAppInfoLookup interface: */
2756 static void g_desktop_app_info_lookup_base_init (gpointer g_class);
2757 static void g_desktop_app_info_lookup_class_init (gpointer g_class,
2758 gpointer class_data);
2761 g_desktop_app_info_lookup_get_type (void)
2763 static volatile gsize g_define_type_id__volatile = 0;
2765 if (g_once_init_enter (&g_define_type_id__volatile))
2767 const GTypeInfo desktop_app_info_lookup_info =
2769 sizeof (GDesktopAppInfoLookupIface), /* class_size */
2770 g_desktop_app_info_lookup_base_init, /* base_init */
2771 NULL, /* base_finalize */
2772 g_desktop_app_info_lookup_class_init,
2773 NULL, /* class_finalize */
2774 NULL, /* class_data */
2776 0, /* n_preallocs */
2779 GType g_define_type_id =
2780 g_type_register_static (G_TYPE_INTERFACE, I_("GDesktopAppInfoLookup"),
2781 &desktop_app_info_lookup_info, 0);
2783 g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
2785 g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
2788 return g_define_type_id__volatile;
2792 g_desktop_app_info_lookup_class_init (gpointer g_class,
2793 gpointer class_data)
2798 g_desktop_app_info_lookup_base_init (gpointer g_class)
2803 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
2804 * @lookup: a #GDesktopAppInfoLookup
2805 * @uri_scheme: a string containing a URI scheme.
2807 * Gets the default application for launching applications
2808 * using this URI scheme for a particular GDesktopAppInfoLookup
2811 * The GDesktopAppInfoLookup interface and this function is used
2812 * to implement g_app_info_get_default_for_uri_scheme() backends
2813 * in a GIO module. There is no reason for applications to use it
2814 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
2816 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
2819 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
2820 const char *uri_scheme)
2822 GDesktopAppInfoLookupIface *iface;
2824 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
2826 iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
2828 return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
2831 #define __G_DESKTOP_APP_INFO_C__
2832 #include "gioaliasdef.c"