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);
421 new_info->icon = g_object_ref (info->icon);
422 new_info->only_show_in = g_strdupv (info->only_show_in);
423 new_info->not_show_in = g_strdupv (info->not_show_in);
424 new_info->try_exec = g_strdup (info->try_exec);
425 new_info->exec = g_strdup (info->exec);
426 new_info->binary = g_strdup (info->binary);
427 new_info->path = g_strdup (info->path);
428 new_info->hidden = info->hidden;
429 new_info->terminal = info->terminal;
430 new_info->startup_notify = info->startup_notify;
432 return G_APP_INFO (new_info);
436 g_desktop_app_info_equal (GAppInfo *appinfo1,
439 GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
440 GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
442 if (info1->desktop_id == NULL ||
443 info2->desktop_id == NULL)
444 return info1 == info2;
446 return strcmp (info1->desktop_id, info2->desktop_id) == 0;
450 g_desktop_app_info_get_id (GAppInfo *appinfo)
452 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
454 return info->desktop_id;
458 g_desktop_app_info_get_name (GAppInfo *appinfo)
460 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
462 if (info->name == NULL)
468 * g_desktop_app_info_get_is_hidden:
469 * @info: a #GDesktopAppInfo.
471 * A desktop file is hidden if the Hidden key in it is
474 * Returns: %TRUE if hidden, %FALSE otherwise.
477 g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
483 g_desktop_app_info_get_description (GAppInfo *appinfo)
485 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
487 return info->comment;
491 g_desktop_app_info_get_executable (GAppInfo *appinfo)
493 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
499 g_desktop_app_info_get_commandline (GAppInfo *appinfo)
501 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
507 g_desktop_app_info_get_icon (GAppInfo *appinfo)
509 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
515 expand_macro_single (char macro, char *uri)
521 file = g_file_new_for_uri (uri);
522 path = g_file_get_path (file);
523 g_object_unref (file);
529 result = g_shell_quote (uri);
534 result = g_shell_quote (path);
540 name = g_path_get_dirname (path);
541 result = g_shell_quote (name);
549 name = g_path_get_basename (path);
550 result = g_shell_quote (name);
562 expand_macro (char macro,
564 GDesktopAppInfo *info,
567 GList *uris = *uri_list;
569 gboolean force_file_uri;
570 char force_file_uri_macro;
573 g_return_if_fail (exec != NULL);
575 /* On %u and %U, pass POSIX file path pointing to the URI via
576 * the FUSE mount in ~/.gvfs. Note that if the FUSE daemon isn't
577 * running or the URI doesn't have a POSIX file path via FUSE
578 * we'll just pass the URI.
580 force_file_uri_macro = macro;
581 force_file_uri = FALSE;
587 force_file_uri_macro = 'f';
588 force_file_uri = TRUE;
591 force_file_uri_macro = 'F';
592 force_file_uri = TRUE;
608 if (!force_file_uri ||
609 /* Pass URI if it contains an anchor */
610 strchr (uri, '#') != NULL)
612 expanded = expand_macro_single (macro, uri);
616 expanded = expand_macro_single (force_file_uri_macro, uri);
617 if (expanded == NULL)
618 expanded = expand_macro_single (macro, uri);
623 g_string_append (exec, expanded);
639 if (!force_file_uri ||
640 /* Pass URI if it contains an anchor */
641 strchr (uri, '#') != NULL)
643 expanded = expand_macro_single (macro, uri);
647 expanded = expand_macro_single (force_file_uri_macro, uri);
648 if (expanded == NULL)
649 expanded = expand_macro_single (macro, uri);
654 g_string_append (exec, expanded);
660 if (uris != NULL && expanded)
661 g_string_append_c (exec, ' ');
669 g_string_append (exec, "--icon ");
670 g_string_append (exec, info->icon_name);
676 g_string_append (exec, info->name);
681 g_string_append (exec, info->filename);
684 case 'm': /* deprecated */
688 g_string_append_c (exec, '%');
696 expand_application_parameters (GDesktopAppInfo *info,
702 GList *uri_list = *uris;
703 const char *p = info->exec;
704 GString *expanded_exec = g_string_new (NULL);
707 if (info->exec == NULL)
709 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
710 _("Desktop file didn't specify Exec field"));
716 if (p[0] == '%' && p[1] != '\0')
718 expand_macro (p[1], expanded_exec, info, uris);
722 g_string_append_c (expanded_exec, *p);
727 /* No file substitutions */
728 if (uri_list == *uris && uri_list != NULL)
730 /* If there is no macro default to %f. This is also what KDE does */
731 g_string_append_c (expanded_exec, ' ');
732 expand_macro ('f', expanded_exec, info, uris);
735 res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
736 g_string_free (expanded_exec, TRUE);
741 prepend_terminal_to_vector (int *argc,
748 char **term_argv = NULL;
753 g_return_val_if_fail (argc != NULL, FALSE);
754 g_return_val_if_fail (argv != NULL, FALSE);
762 /* compute size if not given */
765 for (i = 0; the_argv[i] != NULL; i++)
771 term_argv = g_new0 (char *, 3);
773 check = g_find_program_in_path ("gnome-terminal");
776 term_argv[0] = check;
777 /* Note that gnome-terminal takes -x and
778 * as -e in gnome-terminal is broken we use that. */
779 term_argv[1] = g_strdup ("-x");
784 check = g_find_program_in_path ("nxterm");
786 check = g_find_program_in_path ("color-xterm");
788 check = g_find_program_in_path ("rxvt");
790 check = g_find_program_in_path ("xterm");
792 check = g_find_program_in_path ("dtterm");
795 check = g_strdup ("xterm");
796 g_warning ("couldn't find a terminal, falling back to xterm");
798 term_argv[0] = check;
799 term_argv[1] = g_strdup ("-e");
802 real_argc = term_argc + *argc;
803 real_argv = g_new (char *, real_argc + 1);
805 for (i = 0; i < term_argc; i++)
806 real_argv[i] = term_argv[i];
808 for (j = 0; j < *argc; j++, i++)
809 real_argv[i] = (char *)the_argv[j];
817 /* we use g_free here as we sucked all the inner strings
818 * out from it into real_argv */
823 #endif /* G_OS_WIN32 */
826 /* '=' is the new '\0'.
827 * DO NOT CALL unless at least one string ends with '='
830 is_env (const char *a,
835 if (*a == 0 || *b == 0)
848 /* free with g_strfreev */
850 replace_env_var (char **old_environ,
852 const char *new_value)
854 int length, new_length;
855 int index, new_index;
859 /* do two things at once:
860 * - discover the length of the environment ('length')
861 * - find the location (if any) of the env var ('index')
864 for (length = 0; old_environ[length]; length++)
866 /* if we already have it in our environment, replace */
867 if (is_env (old_environ[length], env_var))
872 /* no current env var, no desired env value.
875 if (new_value == NULL && index == -1)
878 /* in all cases now, we will be using a modified environment.
879 * determine its length and allocated it.
882 * new_index = location to insert, if any
883 * new_length = length of the new array
884 * new_environ = the pointer array for the new environment
887 if (new_value == NULL && index >= 0)
889 /* in this case, we will be removing an entry */
890 new_length = length - 1;
893 else if (new_value != NULL && index < 0)
895 /* in this case, we will be adding an entry to the end */
896 new_length = length + 1;
900 /* in this case, we will be replacing the existing entry */
906 new_environ = g_malloc (sizeof (char *) * (new_length + 1));
907 new_environ[new_length] = NULL;
909 /* now we do the copying.
910 * for each entry in the new environment, we decide what to do
914 for (new_i = 0; new_i < new_length; new_i++)
916 if (new_i == new_index)
918 /* insert our new item */
919 new_environ[new_i] = g_strconcat (env_var,
924 /* if we had an old entry, skip it now */
930 /* if this is the old DESKTOP_STARTUP_ID, skip it */
934 /* copy an old item */
935 new_environ[new_i] = g_strdup (old_environ[i]);
940 g_strfreev (old_environ);
946 uri_list_segment_to_files (GList *start,
953 while (start != NULL && start != end)
955 file = g_file_new_for_uri ((char *)start->data);
956 res = g_list_prepend (res, file);
960 return g_list_reverse (res);
963 #ifdef HAVE__NSGETENVIRON
964 #define environ (*_NSGetEnviron())
965 #elif !defined(G_OS_WIN32)
967 /* According to the Single Unix Specification, environ is not in
968 * * any system header, although unistd.h often declares it.
970 extern char **environ;
974 g_desktop_app_info_launch_uris (GAppInfo *appinfo,
976 GAppLaunchContext *launch_context,
979 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
980 gboolean completed = FALSE;
982 GList *launched_files;
989 g_return_val_if_fail (appinfo != NULL, FALSE);
997 if (!expand_application_parameters (info, &uris,
998 &argc, &argv, error))
1001 if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
1003 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1004 _("Unable to find terminal required for application"));
1011 launched_files = uri_list_segment_to_files (old_uris, uris);
1013 display = g_app_launch_context_get_display (launch_context,
1018 if (info->startup_notify)
1019 sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
1023 if (display || sn_id)
1027 envp = g_new0 (char *, 1);
1029 envp = g_strdupv (environ);
1033 envp = replace_env_var (envp,
1038 envp = replace_env_var (envp,
1039 "DESKTOP_STARTUP_ID",
1045 g_list_foreach (launched_files, (GFunc)g_object_unref, NULL);
1046 g_list_free (launched_files);
1049 if (!g_spawn_async (info->path, /* working directory */
1052 G_SPAWN_SEARCH_PATH /* flags */,
1053 NULL /* child_setup */,
1055 NULL /* child_pid */,
1060 g_app_launch_context_launch_failed (launch_context, sn_id);
1074 while (uris != NULL);
1086 g_desktop_app_info_supports_uris (GAppInfo *appinfo)
1088 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1090 return info->exec &&
1091 ((strstr (info->exec, "%u") != NULL) ||
1092 (strstr (info->exec, "%U") != NULL));
1096 g_desktop_app_info_supports_files (GAppInfo *appinfo)
1098 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1100 return info->exec &&
1101 ((strstr (info->exec, "%f") != NULL) ||
1102 (strstr (info->exec, "%F") != NULL));
1106 g_desktop_app_info_launch (GAppInfo *appinfo,
1108 GAppLaunchContext *launch_context,
1118 uri = g_file_get_uri (files->data);
1119 uris = g_list_prepend (uris, uri);
1120 files = files->next;
1123 uris = g_list_reverse (uris);
1125 res = g_desktop_app_info_launch_uris (appinfo, uris, launch_context, error);
1127 g_list_foreach (uris, (GFunc)g_free, NULL);
1133 G_LOCK_DEFINE_STATIC (g_desktop_env);
1134 static gchar *g_desktop_env = NULL;
1137 * g_desktop_app_info_set_desktop_env:
1138 * @desktop_env: a string specifying what desktop this is
1140 * Sets the name of the desktop that the application is running in.
1141 * This is used by g_app_info_should_show() to evaluate the
1142 * <literal>OnlyShowIn</literal> and <literal>NotShowIn</literal>
1143 * desktop entry fields.
1145 * The <ulink url="http://standards.freedesktop.org/menu-spec/latest/">Desktop
1146 * Menu specification</ulink> recognizes the following:
1148 * <member>GNOME</member>
1149 * <member>KDE</member>
1150 * <member>ROX</member>
1151 * <member>XFCE</member>
1152 * <member>Old</member>
1155 * Should be called only once; subsequent calls are ignored.
1158 g_desktop_app_info_set_desktop_env (const gchar *desktop_env)
1160 G_LOCK (g_desktop_env);
1162 g_desktop_env = g_strdup (desktop_env);
1163 G_UNLOCK (g_desktop_env);
1167 g_desktop_app_info_should_show (GAppInfo *appinfo)
1169 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1171 const gchar *desktop_env;
1174 if (info->nodisplay)
1177 G_LOCK (g_desktop_env);
1178 desktop_env = g_desktop_env;
1179 G_UNLOCK (g_desktop_env);
1181 if (info->only_show_in)
1183 if (desktop_env == NULL)
1187 for (i = 0; info->only_show_in[i] != NULL; i++)
1189 if (strcmp (info->only_show_in[i], desktop_env) == 0)
1199 if (info->not_show_in && desktop_env)
1201 for (i = 0; info->not_show_in[i] != NULL; i++)
1203 if (strcmp (info->not_show_in[i], desktop_env) == 0)
1217 ensure_dir (DirType type,
1220 char *path, *display_name;
1223 if (type == APP_DIR)
1224 path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
1226 path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
1229 if (g_mkdir_with_parents (path, 0700) == 0)
1233 display_name = g_filename_display_name (path);
1234 if (type == APP_DIR)
1235 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1236 _("Can't create user application configuration folder %s: %s"),
1237 display_name, g_strerror (errsv));
1239 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
1240 _("Can't create user MIME configuration folder %s: %s"),
1241 display_name, g_strerror (errsv));
1243 g_free (display_name);
1250 update_mimeapps_list (const char *desktop_id,
1251 const char *content_type,
1252 gboolean add_as_default,
1253 gboolean add_non_default,
1257 char *dirname, *filename;
1259 gboolean load_succeeded, res;
1260 char **old_list, **list;
1261 GList *system_list, *l;
1262 gsize length, data_size;
1265 char **content_types;
1267 /* Don't add both at start and end */
1268 g_assert (!(add_as_default && add_non_default));
1270 dirname = ensure_dir (APP_DIR, error);
1274 filename = g_build_filename (dirname, "mimeapps.list", NULL);
1277 key_file = g_key_file_new ();
1278 load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
1279 if (!load_succeeded || !g_key_file_has_group (key_file, ADDED_ASSOCIATIONS_GROUP))
1281 g_key_file_free (key_file);
1282 key_file = g_key_file_new ();
1287 content_types = g_new (char *, 2);
1288 content_types[0] = g_strdup (content_type);
1289 content_types[1] = NULL;
1293 content_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP, NULL, NULL);
1296 for (k = 0; content_types && content_types[k]; k++)
1298 /* Add to the right place in the list */
1301 old_list = g_key_file_get_string_list (key_file, ADDED_ASSOCIATIONS_GROUP,
1302 content_types[k], &length, NULL);
1304 list = g_new (char *, 1 + length + 1);
1308 list[i++] = g_strdup (desktop_id);
1311 for (j = 0; old_list[j] != NULL; j++)
1313 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1314 list[i++] = g_strdup (old_list[j]);
1315 else if (add_non_default)
1317 /* If adding as non-default, and it's already in,
1318 don't change order of desktop ids */
1319 add_non_default = FALSE;
1320 list[i++] = g_strdup (old_list[j]);
1325 if (add_non_default)
1327 /* We're adding as non-default, and it wasn't already in the list,
1328 so we add at the end. But to avoid listing the app before the
1329 current system default (thus changing the default) we have to
1330 add the current list of (not yet listed) apps before it. */
1332 list[i] = NULL; /* Terminate current list so we can use it */
1333 system_list = get_all_desktop_entries_for_mime_type (content_type, (const char **)list);
1335 list = g_renew (char *, list, 1 + length + g_list_length (system_list) + 1);
1337 for (l = system_list; l != NULL; l = l->next)
1339 list[i++] = l->data; /* no strdup, taking ownership */
1340 if (g_strcmp0 (l->data, desktop_id) == 0)
1341 add_non_default = FALSE;
1343 g_list_free (system_list);
1345 if (add_non_default)
1346 list[i++] = g_strdup (desktop_id);
1351 g_strfreev (old_list);
1353 if (list[0] == NULL || desktop_id == NULL)
1354 g_key_file_remove_key (key_file,
1355 ADDED_ASSOCIATIONS_GROUP,
1359 g_key_file_set_string_list (key_file,
1360 ADDED_ASSOCIATIONS_GROUP,
1362 (const char * const *)list, i);
1369 /* reuse the list from above */
1373 g_strfreev (content_types);
1374 content_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP, NULL, NULL);
1377 for (k = 0; content_types && content_types[k]; k++)
1379 /* Remove from removed associations group (unless remove) */
1382 old_list = g_key_file_get_string_list (key_file, REMOVED_ASSOCIATIONS_GROUP,
1383 content_types[k], &length, NULL);
1385 list = g_new (char *, 1 + length + 1);
1389 list[i++] = g_strdup (desktop_id);
1392 for (j = 0; old_list[j] != NULL; j++)
1394 if (g_strcmp0 (old_list[j], desktop_id) != 0)
1395 list[i++] = g_strdup (old_list[j]);
1400 g_strfreev (old_list);
1402 if (list[0] == NULL || desktop_id == NULL)
1403 g_key_file_remove_key (key_file,
1404 REMOVED_ASSOCIATIONS_GROUP,
1408 g_key_file_set_string_list (key_file,
1409 REMOVED_ASSOCIATIONS_GROUP,
1411 (const char * const *)list, i);
1416 g_strfreev (content_types);
1418 data = g_key_file_to_data (key_file, &data_size, error);
1419 g_key_file_free (key_file);
1421 res = g_file_set_contents (filename, data, data_size, error);
1423 mime_info_cache_reload (NULL);
1432 g_desktop_app_info_set_as_default_for_type (GAppInfo *appinfo,
1433 const char *content_type,
1436 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1438 if (!g_desktop_app_info_ensure_saved (info, error))
1441 return update_mimeapps_list (info->desktop_id, content_type, TRUE, FALSE, FALSE, error);
1445 update_program_done (GPid pid,
1449 /* Did the application exit correctly */
1450 if (WIFEXITED (status) &&
1451 WEXITSTATUS (status) == 0)
1453 /* Here we could clean out any caches in use */
1458 run_update_command (char *command,
1467 GError *error = NULL;
1470 argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
1472 if (g_spawn_async ("/", argv,
1474 G_SPAWN_SEARCH_PATH |
1475 G_SPAWN_STDOUT_TO_DEV_NULL |
1476 G_SPAWN_STDERR_TO_DEV_NULL |
1477 G_SPAWN_DO_NOT_REAP_CHILD,
1478 NULL, NULL, /* No setup function */
1481 g_child_watch_add (pid, update_program_done, NULL);
1484 /* If we get an error at this point, it's quite likely the user doesn't
1485 * have an installed copy of either 'update-mime-database' or
1486 * 'update-desktop-database'. I don't think we want to popup an error
1487 * dialog at this point, so we just do a g_warning to give the user a
1488 * chance of debugging it.
1490 g_warning ("%s", error->message);
1497 g_desktop_app_info_set_as_default_for_extension (GAppInfo *appinfo,
1498 const char *extension,
1501 char *filename, *basename, *mimetype;
1505 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (appinfo), error))
1508 dirname = ensure_dir (MIMETYPE_DIR, error);
1512 basename = g_strdup_printf ("user-extension-%s.xml", extension);
1513 filename = g_build_filename (dirname, basename, NULL);
1517 mimetype = g_strdup_printf ("application/x-extension-%s", extension);
1519 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1524 g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
1525 "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
1526 " <mime-type type=\"%s\">\n"
1527 " <comment>%s document</comment>\n"
1528 " <glob pattern=\"*.%s\"/>\n"
1530 "</mime-info>\n", mimetype, extension, extension);
1532 g_file_set_contents (filename, contents, -1, NULL);
1535 run_update_command ("update-mime-database", "mime");
1539 res = g_desktop_app_info_set_as_default_for_type (appinfo,
1549 g_desktop_app_info_add_supports_type (GAppInfo *appinfo,
1550 const char *content_type,
1553 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1555 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1558 return update_mimeapps_list (info->desktop_id, content_type, FALSE, TRUE, FALSE, error);
1562 g_desktop_app_info_can_remove_supports_type (GAppInfo *appinfo)
1568 g_desktop_app_info_remove_supports_type (GAppInfo *appinfo,
1569 const char *content_type,
1572 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1574 if (!g_desktop_app_info_ensure_saved (G_DESKTOP_APP_INFO (info), error))
1577 return update_mimeapps_list (info->desktop_id, content_type, FALSE, FALSE, TRUE, error);
1581 g_desktop_app_info_ensure_saved (GDesktopAppInfo *info,
1587 char *data, *desktop_id;
1592 if (info->filename != NULL)
1595 /* This is only used for object created with
1596 * g_app_info_create_from_commandline. All other
1597 * object should have a filename
1600 dirname = ensure_dir (APP_DIR, error);
1604 key_file = g_key_file_new ();
1606 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1607 "Encoding", "UTF-8");
1608 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1609 G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
1610 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1611 G_KEY_FILE_DESKTOP_KEY_TYPE,
1612 G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
1614 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1615 G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
1617 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1618 G_KEY_FILE_DESKTOP_KEY_EXEC, info->exec);
1620 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1621 G_KEY_FILE_DESKTOP_KEY_NAME, info->name);
1623 g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
1624 G_KEY_FILE_DESKTOP_KEY_COMMENT, info->comment);
1626 g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
1627 G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
1629 data = g_key_file_to_data (key_file, &data_size, NULL);
1630 g_key_file_free (key_file);
1632 desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", info->name);
1633 filename = g_build_filename (dirname, desktop_id, NULL);
1634 g_free (desktop_id);
1637 fd = g_mkstemp (filename);
1642 display_name = g_filename_display_name (filename);
1643 g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
1644 _("Can't create user desktop file %s"), display_name);
1645 g_free (display_name);
1651 desktop_id = g_path_get_basename (filename);
1655 res = g_file_set_contents (filename, data, data_size, error);
1658 g_free (desktop_id);
1663 info->filename = filename;
1664 info->desktop_id = desktop_id;
1666 run_update_command ("update-desktop-database", "applications");
1672 g_desktop_app_info_can_delete (GAppInfo *appinfo)
1674 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1678 if (strstr (info->filename, "/userapp-"))
1679 return g_access (info->filename, W_OK) == 0;
1686 g_desktop_app_info_delete (GAppInfo *appinfo)
1688 GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
1692 if (g_remove (info->filename) == 0)
1694 update_mimeapps_list (info->desktop_id, NULL, FALSE, FALSE, FALSE, NULL);
1696 g_free (info->filename);
1697 info->filename = NULL;
1698 g_free (info->desktop_id);
1699 info->desktop_id = NULL;
1709 * g_app_info_create_from_commandline:
1710 * @commandline: the commandline to use
1711 * @application_name: the application name, or %NULL to use @commandline
1712 * @flags: flags that can specify details of the created #GAppInfo
1713 * @error: a #GError location to store the error occuring, %NULL to ignore.
1715 * Creates a new #GAppInfo from the given information.
1717 * Returns: new #GAppInfo for given command.
1720 g_app_info_create_from_commandline (const char *commandline,
1721 const char *application_name,
1722 GAppInfoCreateFlags flags,
1727 GDesktopAppInfo *info;
1729 info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
1731 info->filename = NULL;
1732 info->desktop_id = NULL;
1734 info->terminal = flags & G_APP_INFO_CREATE_NEEDS_TERMINAL;
1735 info->startup_notify = FALSE;
1736 info->hidden = FALSE;
1737 if (flags & G_APP_INFO_CREATE_SUPPORTS_URIS)
1738 info->exec = g_strconcat (commandline, " %u", NULL);
1740 info->exec = g_strconcat (commandline, " %f", NULL);
1741 info->nodisplay = TRUE;
1742 info->binary = binary_from_exec (info->exec);
1744 if (application_name)
1745 info->name = g_strdup (application_name);
1748 /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
1749 split = g_strsplit (commandline, " ", 2);
1750 basename = g_path_get_basename (split[0]);
1752 info->name = basename;
1753 if (info->name == NULL)
1754 info->name = g_strdup ("custom");
1756 info->comment = g_strdup_printf (_("Custom definition for %s"), info->name);
1758 return G_APP_INFO (info);
1762 g_desktop_app_info_iface_init (GAppInfoIface *iface)
1764 iface->dup = g_desktop_app_info_dup;
1765 iface->equal = g_desktop_app_info_equal;
1766 iface->get_id = g_desktop_app_info_get_id;
1767 iface->get_name = g_desktop_app_info_get_name;
1768 iface->get_description = g_desktop_app_info_get_description;
1769 iface->get_executable = g_desktop_app_info_get_executable;
1770 iface->get_icon = g_desktop_app_info_get_icon;
1771 iface->launch = g_desktop_app_info_launch;
1772 iface->supports_uris = g_desktop_app_info_supports_uris;
1773 iface->supports_files = g_desktop_app_info_supports_files;
1774 iface->launch_uris = g_desktop_app_info_launch_uris;
1775 iface->should_show = g_desktop_app_info_should_show;
1776 iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
1777 iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
1778 iface->add_supports_type = g_desktop_app_info_add_supports_type;
1779 iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
1780 iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
1781 iface->can_delete = g_desktop_app_info_can_delete;
1782 iface->do_delete = g_desktop_app_info_delete;
1783 iface->get_commandline = g_desktop_app_info_get_commandline;
1787 app_info_in_list (GAppInfo *info,
1790 while (list != NULL)
1792 if (g_app_info_equal (info, list->data))
1801 * g_app_info_get_all_for_type:
1802 * @content_type: the content type to find a #GAppInfo for
1804 * Gets a list of all #GAppInfo s for a given content type.
1806 * Returns: #GList of #GAppInfo s for given @content_type
1807 * or %NULL on error.
1810 g_app_info_get_all_for_type (const char *content_type)
1812 GList *desktop_entries, *l;
1814 GDesktopAppInfo *info;
1816 g_return_val_if_fail (content_type != NULL, NULL);
1818 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1821 for (l = desktop_entries; l != NULL; l = l->next)
1823 char *desktop_entry = l->data;
1825 info = g_desktop_app_info_new (desktop_entry);
1828 if (app_info_in_list (G_APP_INFO (info), infos))
1829 g_object_unref (info);
1831 infos = g_list_prepend (infos, info);
1833 g_free (desktop_entry);
1836 g_list_free (desktop_entries);
1838 return g_list_reverse (infos);
1842 * g_app_info_reset_type_associations:
1843 * @content_type: a content type
1845 * Removes all changes to the type associations done by
1846 * g_app_info_set_as_default_for_type(),
1847 * g_app_info_set_as_default_for_extension(),
1848 * g_app_info_add_supports_type() of g_app_info_remove_supports_type().
1853 g_app_info_reset_type_associations (const char *content_type)
1855 update_mimeapps_list (NULL, content_type, FALSE, FALSE, FALSE, NULL);
1859 * g_app_info_get_default_for_type:
1860 * @content_type: the content type to find a #GAppInfo for
1861 * @must_support_uris: if %TRUE, the #GAppInfo is expected to
1864 * Gets the #GAppInfo that correspond to a given content type.
1866 * Returns: #GAppInfo for given @content_type or %NULL on error.
1869 g_app_info_get_default_for_type (const char *content_type,
1870 gboolean must_support_uris)
1872 GList *desktop_entries, *l;
1875 g_return_val_if_fail (content_type != NULL, NULL);
1877 desktop_entries = get_all_desktop_entries_for_mime_type (content_type, NULL);
1880 for (l = desktop_entries; l != NULL; l = l->next)
1882 char *desktop_entry = l->data;
1884 info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
1887 if (must_support_uris && !g_app_info_supports_uris (info))
1889 g_object_unref (info);
1897 g_list_foreach (desktop_entries, (GFunc)g_free, NULL);
1898 g_list_free (desktop_entries);
1904 * g_app_info_get_default_for_uri_scheme:
1905 * @uri_scheme: a string containing a URI scheme.
1907 * Gets the default application for launching applications
1908 * using this URI scheme. A URI scheme is the initial part
1909 * of the URI, up to but not including the ':', e.g. "http",
1912 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
1915 g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
1917 static gsize lookup = 0;
1919 if (g_once_init_enter (&lookup))
1921 gsize setup_value = 1;
1922 GDesktopAppInfoLookup *lookup_instance;
1923 const char *use_this;
1924 GIOExtensionPoint *ep;
1925 GIOExtension *extension;
1928 use_this = g_getenv ("GIO_USE_URI_ASSOCIATION");
1930 /* Ensure vfs in modules loaded */
1931 _g_io_modules_ensure_loaded ();
1933 ep = g_io_extension_point_lookup (G_DESKTOP_APP_INFO_LOOKUP_EXTENSION_POINT_NAME);
1935 lookup_instance = NULL;
1938 extension = g_io_extension_point_get_extension_by_name (ep, use_this);
1940 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1943 if (lookup_instance == NULL)
1945 for (l = g_io_extension_point_get_extensions (ep); l != NULL; l = l->next)
1947 extension = l->data;
1948 lookup_instance = g_object_new (g_io_extension_get_type (extension), NULL);
1949 if (lookup_instance != NULL)
1954 if (lookup_instance != NULL)
1955 setup_value = (gsize)lookup_instance;
1957 g_once_init_leave (&lookup, setup_value);
1963 return g_desktop_app_info_lookup_get_default_for_uri_scheme (G_DESKTOP_APP_INFO_LOOKUP (lookup),
1969 get_apps_from_dir (GHashTable *apps,
1970 const char *dirname,
1974 const char *basename;
1975 char *filename, *subprefix, *desktop_id;
1977 GDesktopAppInfo *appinfo;
1979 dir = g_dir_open (dirname, 0, NULL);
1982 while ((basename = g_dir_read_name (dir)) != NULL)
1984 filename = g_build_filename (dirname, basename, NULL);
1985 if (g_str_has_suffix (basename, ".desktop"))
1987 desktop_id = g_strconcat (prefix, basename, NULL);
1989 /* Use _extended so we catch NULLs too (hidden) */
1990 if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
1992 appinfo = g_desktop_app_info_new_from_filename (filename);
1994 if (appinfo && g_desktop_app_info_get_is_hidden (appinfo))
1996 g_object_unref (appinfo);
2001 if (appinfo || hidden)
2003 g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
2007 /* Reuse instead of strdup here */
2008 appinfo->desktop_id = desktop_id;
2013 g_free (desktop_id);
2017 if (g_file_test (filename, G_FILE_TEST_IS_DIR))
2019 subprefix = g_strconcat (prefix, basename, "-", NULL);
2020 get_apps_from_dir (apps, filename, subprefix);
2032 * g_app_info_get_all:
2034 * Gets a list of all of the applications currently registered
2037 * For desktop files, this includes applications that have
2038 * <literal>NoDisplay=true</literal> set or are excluded from
2039 * display by means of <literal>OnlyShowIn</literal> or
2040 * <literal>NotShowIn</literal>. See g_app_info_should_show().
2041 * The returned list does not include applications which have
2042 * the <literal>Hidden</literal> key set.
2044 * Returns: a newly allocated #GList of references to #GAppInfo<!---->s.
2047 g_app_info_get_all (void)
2049 const char * const *dirs;
2051 GHashTableIter iter;
2056 dirs = get_applications_search_path ();
2058 apps = g_hash_table_new_full (g_str_hash, g_str_equal,
2062 for (i = 0; dirs[i] != NULL; i++)
2063 get_apps_from_dir (apps, dirs[i], "");
2067 g_hash_table_iter_init (&iter, apps);
2068 while (g_hash_table_iter_next (&iter, NULL, &value))
2071 infos = g_list_prepend (infos, value);
2074 g_hash_table_destroy (apps);
2076 return g_list_reverse (infos);
2079 /* Cacheing of mimeinfo.cache and defaults.list files */
2083 GHashTable *mime_info_cache_map;
2084 GHashTable *defaults_list_map;
2085 GHashTable *mimeapps_list_added_map;
2086 GHashTable *mimeapps_list_removed_map;
2087 time_t mime_info_cache_timestamp;
2088 time_t defaults_list_timestamp;
2089 time_t mimeapps_list_timestamp;
2093 GList *dirs; /* mimeinfo.cache and defaults.list */
2094 GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
2095 time_t last_stat_time;
2096 guint should_ping_mime_monitor : 1;
2099 static MimeInfoCache *mime_info_cache = NULL;
2100 G_LOCK_DEFINE_STATIC (mime_info_cache);
2102 static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2103 const char *mime_type,
2104 char **new_desktop_file_ids);
2106 static MimeInfoCache * mime_info_cache_new (void);
2109 destroy_info_cache_value (gpointer key,
2113 g_list_foreach (value, (GFunc)g_free, NULL);
2114 g_list_free (value);
2118 destroy_info_cache_map (GHashTable *info_cache_map)
2120 g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
2121 g_hash_table_destroy (info_cache_map);
2125 mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
2126 const char *cache_file,
2132 filename = g_build_filename (dir->path, cache_file, NULL);
2134 if (g_stat (filename, &buf) < 0)
2141 if (buf.st_mtime != *timestamp)
2147 /* Call with lock held */
2149 remove_all (gpointer key,
2158 mime_info_cache_blow_global_cache (void)
2160 g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
2165 mime_info_cache_dir_init (MimeInfoCacheDir *dir)
2169 gchar *filename, **mime_types;
2176 if (dir->mime_info_cache_map != NULL &&
2177 !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
2178 &dir->mime_info_cache_timestamp))
2181 if (dir->mime_info_cache_map != NULL)
2182 destroy_info_cache_map (dir->mime_info_cache_map);
2184 dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2185 (GDestroyNotify) g_free,
2188 key_file = g_key_file_new ();
2190 filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
2192 if (g_stat (filename, &buf) < 0)
2195 if (dir->mime_info_cache_timestamp > 0)
2196 mime_info_cache->should_ping_mime_monitor = TRUE;
2198 dir->mime_info_cache_timestamp = buf.st_mtime;
2200 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2205 if (load_error != NULL)
2208 mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
2211 if (load_error != NULL)
2214 for (i = 0; mime_types[i] != NULL; i++)
2216 gchar **desktop_file_ids;
2217 char *unaliased_type;
2218 desktop_file_ids = g_key_file_get_string_list (key_file,
2224 if (desktop_file_ids == NULL)
2227 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2228 mime_info_cache_dir_add_desktop_entries (dir,
2231 g_free (unaliased_type);
2233 g_strfreev (desktop_file_ids);
2236 g_strfreev (mime_types);
2237 g_key_file_free (key_file);
2242 g_key_file_free (key_file);
2244 if (mime_types != NULL)
2245 g_strfreev (mime_types);
2248 g_error_free (load_error);
2252 mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
2256 gchar *filename, **mime_types;
2257 char *unaliased_type;
2258 char **desktop_file_ids;
2265 if (dir->defaults_list_map != NULL &&
2266 !mime_info_cache_dir_out_of_date (dir, "defaults.list",
2267 &dir->defaults_list_timestamp))
2270 if (dir->defaults_list_map != NULL)
2271 g_hash_table_destroy (dir->defaults_list_map);
2272 dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2273 g_free, (GDestroyNotify)g_strfreev);
2276 key_file = g_key_file_new ();
2278 filename = g_build_filename (dir->path, "defaults.list", NULL);
2279 if (g_stat (filename, &buf) < 0)
2282 if (dir->defaults_list_timestamp > 0)
2283 mime_info_cache->should_ping_mime_monitor = TRUE;
2285 dir->defaults_list_timestamp = buf.st_mtime;
2287 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2291 if (load_error != NULL)
2294 mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
2296 if (mime_types != NULL)
2298 for (i = 0; mime_types[i] != NULL; i++)
2300 desktop_file_ids = g_key_file_get_string_list (key_file,
2301 DEFAULT_APPLICATIONS_GROUP,
2305 if (desktop_file_ids == NULL)
2308 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2309 g_hash_table_replace (dir->defaults_list_map,
2314 g_strfreev (mime_types);
2317 g_key_file_free (key_file);
2322 g_key_file_free (key_file);
2324 if (mime_types != NULL)
2325 g_strfreev (mime_types);
2328 g_error_free (load_error);
2332 mime_info_cache_dir_init_mimeapps_list (MimeInfoCacheDir *dir)
2336 gchar *filename, **mime_types;
2337 char *unaliased_type;
2338 char **desktop_file_ids;
2345 if (dir->mimeapps_list_added_map != NULL &&
2346 !mime_info_cache_dir_out_of_date (dir, "mimeapps.list",
2347 &dir->mimeapps_list_timestamp))
2350 if (dir->mimeapps_list_added_map != NULL)
2351 g_hash_table_destroy (dir->mimeapps_list_added_map);
2352 dir->mimeapps_list_added_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2353 g_free, (GDestroyNotify)g_strfreev);
2355 if (dir->mimeapps_list_removed_map != NULL)
2356 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2357 dir->mimeapps_list_removed_map = g_hash_table_new_full (g_str_hash, g_str_equal,
2358 g_free, (GDestroyNotify)g_strfreev);
2360 key_file = g_key_file_new ();
2362 filename = g_build_filename (dir->path, "mimeapps.list", NULL);
2363 if (g_stat (filename, &buf) < 0)
2366 if (dir->mimeapps_list_timestamp > 0)
2367 mime_info_cache->should_ping_mime_monitor = TRUE;
2369 dir->mimeapps_list_timestamp = buf.st_mtime;
2371 g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
2375 if (load_error != NULL)
2378 mime_types = g_key_file_get_keys (key_file, ADDED_ASSOCIATIONS_GROUP,
2380 if (mime_types != NULL)
2382 for (i = 0; mime_types[i] != NULL; i++)
2384 desktop_file_ids = g_key_file_get_string_list (key_file,
2385 ADDED_ASSOCIATIONS_GROUP,
2389 if (desktop_file_ids == NULL)
2392 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2393 g_hash_table_replace (dir->mimeapps_list_added_map,
2398 g_strfreev (mime_types);
2401 mime_types = g_key_file_get_keys (key_file, REMOVED_ASSOCIATIONS_GROUP,
2403 if (mime_types != NULL)
2405 for (i = 0; mime_types[i] != NULL; i++)
2407 desktop_file_ids = g_key_file_get_string_list (key_file,
2408 REMOVED_ASSOCIATIONS_GROUP,
2412 if (desktop_file_ids == NULL)
2415 unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
2416 g_hash_table_replace (dir->mimeapps_list_removed_map,
2421 g_strfreev (mime_types);
2424 g_key_file_free (key_file);
2429 g_key_file_free (key_file);
2431 if (mime_types != NULL)
2432 g_strfreev (mime_types);
2435 g_error_free (load_error);
2438 static MimeInfoCacheDir *
2439 mime_info_cache_dir_new (const char *path)
2441 MimeInfoCacheDir *dir;
2443 dir = g_new0 (MimeInfoCacheDir, 1);
2444 dir->path = g_strdup (path);
2450 mime_info_cache_dir_free (MimeInfoCacheDir *dir)
2455 if (dir->mime_info_cache_map != NULL)
2457 destroy_info_cache_map (dir->mime_info_cache_map);
2458 dir->mime_info_cache_map = NULL;
2462 if (dir->defaults_list_map != NULL)
2464 g_hash_table_destroy (dir->defaults_list_map);
2465 dir->defaults_list_map = NULL;
2468 if (dir->mimeapps_list_added_map != NULL)
2470 g_hash_table_destroy (dir->mimeapps_list_added_map);
2471 dir->mimeapps_list_added_map = NULL;
2474 if (dir->mimeapps_list_removed_map != NULL)
2476 g_hash_table_destroy (dir->mimeapps_list_removed_map);
2477 dir->mimeapps_list_removed_map = NULL;
2484 mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
2485 const char *mime_type,
2486 char **new_desktop_file_ids)
2488 GList *desktop_file_ids;
2491 desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
2494 for (i = 0; new_desktop_file_ids[i] != NULL; i++)
2496 if (!g_list_find (desktop_file_ids, new_desktop_file_ids[i]))
2497 desktop_file_ids = g_list_append (desktop_file_ids,
2498 g_strdup (new_desktop_file_ids[i]));
2501 g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
2505 mime_info_cache_init_dir_lists (void)
2507 const char * const *dirs;
2510 mime_info_cache = mime_info_cache_new ();
2512 dirs = get_applications_search_path ();
2514 for (i = 0; dirs[i] != NULL; i++)
2516 MimeInfoCacheDir *dir;
2518 dir = mime_info_cache_dir_new (dirs[i]);
2522 mime_info_cache_dir_init (dir);
2523 mime_info_cache_dir_init_defaults_list (dir);
2524 mime_info_cache_dir_init_mimeapps_list (dir);
2526 mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
2532 mime_info_cache_update_dir_lists (void)
2536 tmp = mime_info_cache->dirs;
2540 MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
2542 /* No need to do this if we had file monitors... */
2543 mime_info_cache_blow_global_cache ();
2544 mime_info_cache_dir_init (dir);
2545 mime_info_cache_dir_init_defaults_list (dir);
2546 mime_info_cache_dir_init_mimeapps_list (dir);
2553 mime_info_cache_init (void)
2555 G_LOCK (mime_info_cache);
2556 if (mime_info_cache == NULL)
2557 mime_info_cache_init_dir_lists ();
2563 if (now >= mime_info_cache->last_stat_time + 10)
2565 mime_info_cache_update_dir_lists ();
2566 mime_info_cache->last_stat_time = now;
2570 if (mime_info_cache->should_ping_mime_monitor)
2572 /* g_idle_add (emit_mime_changed, NULL); */
2573 mime_info_cache->should_ping_mime_monitor = FALSE;
2576 G_UNLOCK (mime_info_cache);
2579 static MimeInfoCache *
2580 mime_info_cache_new (void)
2582 MimeInfoCache *cache;
2584 cache = g_new0 (MimeInfoCache, 1);
2586 cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
2587 (GDestroyNotify) g_free,
2588 (GDestroyNotify) g_free);
2593 mime_info_cache_free (MimeInfoCache *cache)
2598 g_list_foreach (cache->dirs,
2599 (GFunc) mime_info_cache_dir_free,
2601 g_list_free (cache->dirs);
2602 g_hash_table_destroy (cache->global_defaults_cache);
2607 * mime_info_cache_reload:
2608 * @dir: directory path which needs reloading.
2610 * Reload the mime information for the @dir.
2613 mime_info_cache_reload (const char *dir)
2615 /* FIXME: just reload the dir that needs reloading,
2616 * don't blow the whole cache
2618 if (mime_info_cache != NULL)
2620 G_LOCK (mime_info_cache);
2621 mime_info_cache_free (mime_info_cache);
2622 mime_info_cache = NULL;
2623 G_UNLOCK (mime_info_cache);
2628 append_desktop_entry (GList *list,
2629 const char *desktop_entry,
2630 GList *removed_entries)
2632 /* Add if not already in list, and valid */
2633 if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp) &&
2634 !g_list_find_custom (removed_entries, desktop_entry, (GCompareFunc) strcmp))
2635 list = g_list_prepend (list, g_strdup (desktop_entry));
2641 * get_all_desktop_entries_for_mime_type:
2642 * @mime_type: a mime type.
2643 * @except: NULL or a strv list
2645 * Returns all the desktop ids for @mime_type. The desktop files
2646 * are listed in an order so that default applications are listed before
2647 * non-default ones, and handlers for inherited mimetypes are listed
2648 * after the base ones.
2650 * Optionally doesn't list the desktop ids given in the @except
2652 * Return value: a #GList containing the desktop ids which claim
2653 * to handle @mime_type.
2656 get_all_desktop_entries_for_mime_type (const char *base_mime_type,
2657 const char **except)
2659 GList *desktop_entries, *removed_entries, *list, *dir_list, *tmp;
2660 MimeInfoCacheDir *dir;
2663 char **default_entries;
2664 char **removed_associations;
2669 mime_info_cache_init ();
2671 /* collect all ancestors */
2672 mime_types = _g_unix_content_type_get_parents (base_mime_type);
2673 array = g_ptr_array_new ();
2674 for (i = 0; mime_types[i]; i++)
2675 g_ptr_array_add (array, mime_types[i]);
2676 g_free (mime_types);
2677 for (i = 0; i < array->len; i++)
2679 anc = _g_unix_content_type_get_parents (g_ptr_array_index (array, i));
2680 for (j = 0; anc[j]; j++)
2682 for (k = 0; k < array->len; k++)
2684 if (strcmp (anc[j], g_ptr_array_index (array, k)) == 0)
2687 if (k == array->len) /* not found */
2688 g_ptr_array_add (array, g_strdup (anc[j]));
2692 g_ptr_array_add (array, NULL);
2693 mime_types = (char **)g_ptr_array_free (array, FALSE);
2695 G_LOCK (mime_info_cache);
2697 removed_entries = NULL;
2698 desktop_entries = NULL;
2700 for (i = 0; except != NULL && except[i] != NULL; i++)
2701 removed_entries = g_list_prepend (removed_entries, g_strdup (except[i]));
2703 for (i = 0; mime_types[i] != NULL; i++)
2705 mime_type = mime_types[i];
2707 /* Go through all apps listed as defaults */
2708 for (dir_list = mime_info_cache->dirs;
2710 dir_list = dir_list->next)
2712 dir = dir_list->data;
2714 /* First added associations from mimeapps.list */
2715 default_entries = g_hash_table_lookup (dir->mimeapps_list_added_map, mime_type);
2716 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2717 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2719 /* Then removed associations from mimeapps.list */
2720 removed_associations = g_hash_table_lookup (dir->mimeapps_list_removed_map, mime_type);
2721 for (j = 0; removed_associations != NULL && removed_associations[j] != NULL; j++)
2722 removed_entries = append_desktop_entry (removed_entries, removed_associations[j], NULL);
2724 /* Then system defaults (or old per-user config) (using removed associations from this dir or earlier) */
2725 default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
2726 for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
2727 desktop_entries = append_desktop_entry (desktop_entries, default_entries[j], removed_entries);
2730 /* Go through all entries that support the mimetype */
2731 for (dir_list = mime_info_cache->dirs;
2733 dir_list = dir_list->next)
2735 dir = dir_list->data;
2737 list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
2738 for (tmp = list; tmp != NULL; tmp = tmp->next)
2739 desktop_entries = append_desktop_entry (desktop_entries, tmp->data, removed_entries);
2743 G_UNLOCK (mime_info_cache);
2745 g_strfreev (mime_types);
2747 g_list_foreach (removed_entries, (GFunc)g_free, NULL);
2748 g_list_free (removed_entries);
2750 desktop_entries = g_list_reverse (desktop_entries);
2752 return desktop_entries;
2755 /* GDesktopAppInfoLookup interface: */
2757 static void g_desktop_app_info_lookup_base_init (gpointer g_class);
2758 static void g_desktop_app_info_lookup_class_init (gpointer g_class,
2759 gpointer class_data);
2762 g_desktop_app_info_lookup_get_type (void)
2764 static volatile gsize g_define_type_id__volatile = 0;
2766 if (g_once_init_enter (&g_define_type_id__volatile))
2768 const GTypeInfo desktop_app_info_lookup_info =
2770 sizeof (GDesktopAppInfoLookupIface), /* class_size */
2771 g_desktop_app_info_lookup_base_init, /* base_init */
2772 NULL, /* base_finalize */
2773 g_desktop_app_info_lookup_class_init,
2774 NULL, /* class_finalize */
2775 NULL, /* class_data */
2777 0, /* n_preallocs */
2780 GType g_define_type_id =
2781 g_type_register_static (G_TYPE_INTERFACE, I_("GDesktopAppInfoLookup"),
2782 &desktop_app_info_lookup_info, 0);
2784 g_type_interface_add_prerequisite (g_define_type_id, G_TYPE_OBJECT);
2786 g_once_init_leave (&g_define_type_id__volatile, g_define_type_id);
2789 return g_define_type_id__volatile;
2793 g_desktop_app_info_lookup_class_init (gpointer g_class,
2794 gpointer class_data)
2799 g_desktop_app_info_lookup_base_init (gpointer g_class)
2804 * g_desktop_app_info_lookup_get_default_for_uri_scheme:
2805 * @lookup: a #GDesktopAppInfoLookup
2806 * @uri_scheme: a string containing a URI scheme.
2808 * Gets the default application for launching applications
2809 * using this URI scheme for a particular GDesktopAppInfoLookup
2812 * The GDesktopAppInfoLookup interface and this function is used
2813 * to implement g_app_info_get_default_for_uri_scheme() backends
2814 * in a GIO module. There is no reason for applications to use it
2815 * directly. Applications should use g_app_info_get_default_for_uri_scheme().
2817 * Returns: #GAppInfo for given @uri_scheme or %NULL on error.
2820 g_desktop_app_info_lookup_get_default_for_uri_scheme (GDesktopAppInfoLookup *lookup,
2821 const char *uri_scheme)
2823 GDesktopAppInfoLookupIface *iface;
2825 g_return_val_if_fail (G_IS_DESKTOP_APP_INFO_LOOKUP (lookup), NULL);
2827 iface = G_DESKTOP_APP_INFO_LOOKUP_GET_IFACE (lookup);
2829 return (* iface->get_default_for_uri_scheme) (lookup, uri_scheme);
2832 #define __G_DESKTOP_APP_INFO_C__
2833 #include "gioaliasdef.c"