]> www.fi.muni.cz Git - evince.git/blob - cut-n-paste/smclient/eggdesktopfile.c
Fixing Ukranian
[evince.git] / cut-n-paste / smclient / eggdesktopfile.c
1 /* eggdesktopfile.c - Freedesktop.Org Desktop Files
2  * Copyright (C) 2007 Novell, Inc.
3  *
4  * Based on gnome-desktop-item.c
5  * Copyright (C) 1999, 2000 Red Hat Inc.
6  * Copyright (C) 2001 George Lebl
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; see the file COPYING.LIB. If not,
20  * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 
21  * Boston, MA 02110-1301, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "eggdesktopfile.h"
29
30 #include <string.h>
31 #include <unistd.h>
32
33 #include <glib/gi18n.h>
34 #include <gdk/gdkx.h>
35 #include <gtk/gtk.h>
36
37 struct EggDesktopFile {
38   GKeyFile           *key_file;
39   char               *source;
40
41   char               *name, *icon;
42   EggDesktopFileType  type;
43   char                document_code;
44 };
45
46 /**
47  * egg_desktop_file_new:
48  * @desktop_file_path: path to a Freedesktop-style Desktop file
49  * @error: error pointer
50  *
51  * Creates a new #EggDesktopFile for @desktop_file.
52  *
53  * Return value: the new #EggDesktopFile, or %NULL on error.
54  **/
55 EggDesktopFile *
56 egg_desktop_file_new (const char *desktop_file_path, GError **error)
57 {
58   GKeyFile *key_file;
59
60   key_file = g_key_file_new ();
61   if (!g_key_file_load_from_file (key_file, desktop_file_path, 0, error))
62     {
63       g_key_file_free (key_file);
64       return NULL;
65     }
66
67   return egg_desktop_file_new_from_key_file (key_file, desktop_file_path,
68                                              error);
69 }
70
71 /**
72  * egg_desktop_file_new_from_data_dirs:
73  * @desktop_file_path: relative path to a Freedesktop-style Desktop file
74  * @error: error pointer
75  *
76  * Looks for @desktop_file_path in the paths returned from
77  * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
78  * a new #EggDesktopFile from it.
79  *
80  * Return value: the new #EggDesktopFile, or %NULL on error.
81  **/
82 EggDesktopFile *
83 egg_desktop_file_new_from_data_dirs (const char  *desktop_file_path,
84                                      GError     **error)
85 {
86   EggDesktopFile *desktop_file;
87   GKeyFile *key_file;
88   char *full_path;
89
90   key_file = g_key_file_new ();
91   if (!g_key_file_load_from_data_dirs (key_file, desktop_file_path,
92                                        &full_path, 0, error))
93     {
94       g_key_file_free (key_file);
95       return NULL;
96     }
97
98   desktop_file = egg_desktop_file_new_from_key_file (key_file,
99                                                      full_path,
100                                                      error);
101   g_free (full_path);
102   return desktop_file;
103 }
104
105 /**
106  * egg_desktop_file_new_from_dirs:
107  * @desktop_file_path: relative path to a Freedesktop-style Desktop file
108  * @search_dirs: NULL-terminated array of directories to search
109  * @error: error pointer
110  *
111  * Looks for @desktop_file_path in the paths returned from
112  * g_get_user_data_dir() and g_get_system_data_dirs(), and creates
113  * a new #EggDesktopFile from it.
114  *
115  * Return value: the new #EggDesktopFile, or %NULL on error.
116  **/
117 EggDesktopFile *
118 egg_desktop_file_new_from_dirs (const char  *desktop_file_path,
119                                 const char **search_dirs,
120                                 GError     **error)
121 {
122   EggDesktopFile *desktop_file;
123   GKeyFile *key_file;
124   char *full_path;
125
126   key_file = g_key_file_new ();
127   if (!g_key_file_load_from_dirs (key_file, desktop_file_path, search_dirs,
128                                   &full_path, 0, error))
129     {
130       g_key_file_free (key_file);
131       return NULL;
132     }
133
134   desktop_file = egg_desktop_file_new_from_key_file (key_file,
135                                                      full_path,
136                                                      error);
137   g_free (full_path);
138   return desktop_file;
139 }
140
141 /**
142  * egg_desktop_file_new_from_key_file:
143  * @key_file: a #GKeyFile representing a desktop file
144  * @source: the path or URI that @key_file was loaded from, or %NULL
145  * @error: error pointer
146  *
147  * Creates a new #EggDesktopFile for @key_file. Assumes ownership of
148  * @key_file (on success or failure); you should consider @key_file to
149  * be freed after calling this function.
150  *
151  * Return value: the new #EggDesktopFile, or %NULL on error.
152  **/
153 EggDesktopFile *
154 egg_desktop_file_new_from_key_file (GKeyFile    *key_file,
155                                     const char  *source,
156                                     GError     **error)
157 {
158   EggDesktopFile *desktop_file;
159   char *version, *type;
160
161   if (!g_key_file_has_group (key_file, EGG_DESKTOP_FILE_GROUP))
162     {
163       g_set_error (error, EGG_DESKTOP_FILE_ERROR,
164                    EGG_DESKTOP_FILE_ERROR_INVALID,
165                    _("File is not a valid .desktop file"));
166       g_key_file_free (key_file);
167       return NULL;
168     }
169
170   version = g_key_file_get_value (key_file, EGG_DESKTOP_FILE_GROUP,
171                                   EGG_DESKTOP_FILE_KEY_VERSION,
172                                   NULL);
173   if (version)
174     {
175       double version_num;
176       char *end;
177
178       version_num = g_ascii_strtod (version, &end);
179       if (*end)
180         {
181           g_warning ("Invalid Version string '%s' in %s",
182                      version, source ? source : "(unknown)");
183         }
184       else if (version_num > 1.0)
185         {
186           g_set_error (error, EGG_DESKTOP_FILE_ERROR,
187                        EGG_DESKTOP_FILE_ERROR_INVALID,
188                        _("Unrecognized desktop file Version '%s'"), version);
189           g_free (version);
190           g_key_file_free (key_file);
191           return NULL;
192         }
193       g_free (version);
194     }
195
196   desktop_file = g_new0 (EggDesktopFile, 1);
197   desktop_file->key_file = key_file;
198
199   if (g_path_is_absolute (source))
200     desktop_file->source = g_filename_to_uri (source, NULL, NULL);
201   else
202     desktop_file->source = g_strdup (source);
203
204   desktop_file->name = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
205                                               EGG_DESKTOP_FILE_KEY_NAME, error);
206   if (!desktop_file->name)
207     {
208       egg_desktop_file_free (desktop_file);
209       return NULL;
210     }
211
212   type = g_key_file_get_string (key_file, EGG_DESKTOP_FILE_GROUP,
213                                 EGG_DESKTOP_FILE_KEY_TYPE, error);
214   if (!type)
215     {
216       egg_desktop_file_free (desktop_file);
217       return NULL;
218     }
219
220   if (!strcmp (type, "Application"))
221     {
222       char *exec, *p;
223
224       desktop_file->type = EGG_DESKTOP_FILE_TYPE_APPLICATION;
225
226       exec = g_key_file_get_string (key_file,
227                                     EGG_DESKTOP_FILE_GROUP,
228                                     EGG_DESKTOP_FILE_KEY_EXEC,
229                                     error);
230       if (!exec)
231         {
232           egg_desktop_file_free (desktop_file);
233           g_free (type);
234           return NULL;
235         }
236
237       /* See if it takes paths or URIs or neither */
238       for (p = exec; *p; p++)
239         {
240           if (*p == '%')
241             {
242               if (p[1] == '\0' || strchr ("FfUu", p[1]))
243                 {
244                   desktop_file->document_code = p[1];
245                   break;
246                 }
247               p++;
248             }
249         }
250
251       g_free (exec);
252     }
253   else if (!strcmp (type, "Link"))
254     {
255       char *url;
256
257       desktop_file->type = EGG_DESKTOP_FILE_TYPE_LINK;
258
259       url = g_key_file_get_string (key_file,
260                                    EGG_DESKTOP_FILE_GROUP,
261                                    EGG_DESKTOP_FILE_KEY_URL,
262                                    error);
263       if (!url)
264         {
265           egg_desktop_file_free (desktop_file);
266           g_free (type);
267           return NULL;
268         }
269       g_free (url);
270     }
271   else if (!strcmp (type, "Directory"))
272     desktop_file->type = EGG_DESKTOP_FILE_TYPE_DIRECTORY;
273   else
274     desktop_file->type = EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED;
275
276   g_free (type);
277
278   /* Check the Icon key */
279   desktop_file->icon = g_key_file_get_string (key_file,
280                                               EGG_DESKTOP_FILE_GROUP,
281                                               EGG_DESKTOP_FILE_KEY_ICON,
282                                               NULL);
283   if (desktop_file->icon && !g_path_is_absolute (desktop_file->icon))
284     {
285       char *ext;
286
287       /* Lots of .desktop files still get this wrong */
288       ext = strrchr (desktop_file->icon, '.');
289       if (ext && (!strcmp (ext, ".png") ||
290                   !strcmp (ext, ".xpm") ||
291                   !strcmp (ext, ".svg")))
292         {
293           g_warning ("Desktop file '%s' has malformed Icon key '%s'"
294                      "(should not include extension)",
295                      source ? source : "(unknown)",
296                      desktop_file->icon);
297           *ext = '\0';
298         }
299     }
300
301   return desktop_file;
302 }
303
304 /**
305  * egg_desktop_file_free:
306  * @desktop_file: an #EggDesktopFile
307  *
308  * Frees @desktop_file.
309  **/
310 void
311 egg_desktop_file_free (EggDesktopFile *desktop_file)
312 {
313   g_key_file_free (desktop_file->key_file);
314   g_free (desktop_file->source);
315   g_free (desktop_file->name);
316   g_free (desktop_file->icon);
317   g_free (desktop_file);
318 }
319
320 /**
321  * egg_desktop_file_get_source:
322  * @desktop_file: an #EggDesktopFile
323  *
324  * Gets the URI that @desktop_file was loaded from.
325  *
326  * Return value: @desktop_file's source URI
327  **/
328 const char *
329 egg_desktop_file_get_source (EggDesktopFile *desktop_file)
330 {
331   return desktop_file->source;
332 }
333
334 /**
335  * egg_desktop_file_get_desktop_file_type:
336  * @desktop_file: an #EggDesktopFile
337  *
338  * Gets the desktop file type of @desktop_file.
339  *
340  * Return value: @desktop_file's type
341  **/
342 EggDesktopFileType
343 egg_desktop_file_get_desktop_file_type (EggDesktopFile *desktop_file)
344 {
345   return desktop_file->type;
346 }
347
348 /**
349  * egg_desktop_file_get_name:
350  * @desktop_file: an #EggDesktopFile
351  *
352  * Gets the (localized) value of @desktop_file's "Name" key.
353  *
354  * Return value: the application/link name
355  **/
356 const char *
357 egg_desktop_file_get_name (EggDesktopFile *desktop_file)
358 {
359   return desktop_file->name;
360 }
361
362 /**
363  * egg_desktop_file_get_icon:
364  * @desktop_file: an #EggDesktopFile
365  *
366  * Gets the value of @desktop_file's "Icon" key.
367  *
368  * If the icon string is a full path (that is, if g_path_is_absolute()
369  * returns %TRUE when called on it), it points to a file containing an
370  * unthemed icon. If the icon string is not a full path, it is the
371  * name of a themed icon, which can be looked up with %GtkIconTheme,
372  * or passed directly to a theme-aware widget like %GtkImage or
373  * %GtkCellRendererPixbuf.
374  *
375  * Return value: the icon path or name
376  **/
377 const char *
378 egg_desktop_file_get_icon (EggDesktopFile *desktop_file)
379 {
380   return desktop_file->icon;
381 }
382
383 gboolean
384 egg_desktop_file_has_key (EggDesktopFile  *desktop_file,
385                           const char      *key,
386                           GError         **error)
387 {
388   return g_key_file_has_key (desktop_file->key_file,
389                              EGG_DESKTOP_FILE_GROUP, key,
390                              error);
391 }
392
393 char *
394 egg_desktop_file_get_string (EggDesktopFile  *desktop_file,
395                              const char      *key,
396                              GError         **error)
397 {
398   return g_key_file_get_string (desktop_file->key_file,
399                                 EGG_DESKTOP_FILE_GROUP, key,
400                                 error);
401 }
402
403 char *
404 egg_desktop_file_get_locale_string (EggDesktopFile  *desktop_file,
405                                     const char      *key,
406                                     const char      *locale,
407                                     GError         **error)
408 {
409   return g_key_file_get_locale_string (desktop_file->key_file,
410                                        EGG_DESKTOP_FILE_GROUP, key, locale,
411                                        error);
412 }
413
414 gboolean
415 egg_desktop_file_get_boolean (EggDesktopFile  *desktop_file,
416                               const char      *key,
417                               GError         **error)
418 {
419   return g_key_file_get_boolean (desktop_file->key_file,
420                                  EGG_DESKTOP_FILE_GROUP, key,
421                                  error);
422 }
423
424 double
425 egg_desktop_file_get_numeric (EggDesktopFile  *desktop_file,
426                               const char      *key,
427                               GError         **error)
428 {
429   return g_key_file_get_double (desktop_file->key_file,
430                                 EGG_DESKTOP_FILE_GROUP, key,
431                                 error);
432 }
433
434 int
435 egg_desktop_file_get_integer (EggDesktopFile *desktop_file,
436                               const char     *key,
437                               GError        **error)
438 {
439   return g_key_file_get_integer (desktop_file->key_file,
440                                  EGG_DESKTOP_FILE_GROUP, key,
441                                  error);
442 }
443
444 char **
445 egg_desktop_file_get_string_list (EggDesktopFile  *desktop_file,
446                                   const char      *key,
447                                   gsize           *length,
448                                   GError         **error)
449 {
450   return g_key_file_get_string_list (desktop_file->key_file,
451                                      EGG_DESKTOP_FILE_GROUP, key, length,
452                                      error);
453 }
454
455 char **
456 egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file,
457                                          const char      *key,
458                                          const char      *locale,
459                                          gsize           *length,
460                                          GError         **error)
461 {
462   return g_key_file_get_locale_string_list (desktop_file->key_file,
463                                             EGG_DESKTOP_FILE_GROUP, key,
464                                             locale, length,
465                                             error);
466 }
467
468 /**
469  * egg_desktop_file_can_launch:
470  * @desktop_file: an #EggDesktopFile
471  * @desktop_environment: the name of the running desktop environment,
472  * or %NULL
473  *
474  * Tests if @desktop_file can/should be launched in the current
475  * environment. If @desktop_environment is non-%NULL, @desktop_file's
476  * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that
477  * this desktop_file is appropriate for the named environment.
478  *
479  * Furthermore, if @desktop_file has type
480  * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is
481  * also checked, to make sure the binary it points to exists.
482  *
483  * egg_desktop_file_can_launch() does NOT check the value of the
484  * "Hidden" key.
485  *
486  * Return value: %TRUE if @desktop_file can be launched
487  **/
488 gboolean
489 egg_desktop_file_can_launch (EggDesktopFile *desktop_file,
490                              const char     *desktop_environment)
491 {
492   char *try_exec, *found_program;
493   char **only_show_in, **not_show_in;
494   gboolean found;
495   int i;
496
497   if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION &&
498       desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK)
499     return FALSE;
500
501   if (desktop_environment)
502     {
503       only_show_in = g_key_file_get_string_list (desktop_file->key_file,
504                                                  EGG_DESKTOP_FILE_GROUP,
505                                                  EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN,
506                                                  NULL, NULL);
507       if (only_show_in)
508         {
509           for (i = 0, found = FALSE; only_show_in[i] && !found; i++)
510             {
511               if (!strcmp (only_show_in[i], desktop_environment))
512                 found = TRUE;
513             }
514
515           g_strfreev (only_show_in);
516
517           if (!found)
518             return FALSE;
519         }
520
521       not_show_in = g_key_file_get_string_list (desktop_file->key_file,
522                                                 EGG_DESKTOP_FILE_GROUP,
523                                                 EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN,
524                                                 NULL, NULL);
525       if (not_show_in)
526         {
527           for (i = 0, found = FALSE; not_show_in[i] && !found; i++)
528             {
529               if (!strcmp (not_show_in[i], desktop_environment))
530                 found = TRUE;
531             }
532
533           g_strfreev (not_show_in);
534
535           if (found)
536             return FALSE;
537         }
538     }
539
540   if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION)
541     {
542       try_exec = g_key_file_get_string (desktop_file->key_file,
543                                         EGG_DESKTOP_FILE_GROUP,
544                                         EGG_DESKTOP_FILE_KEY_TRY_EXEC,
545                                         NULL);
546       if (try_exec)
547         {
548           found_program = g_find_program_in_path (try_exec);
549           g_free (try_exec);
550
551           if (!found_program)
552             return FALSE;
553           g_free (found_program);
554         }
555     }
556
557   return TRUE;
558 }
559
560 /**
561  * egg_desktop_file_accepts_documents:
562  * @desktop_file: an #EggDesktopFile
563  *
564  * Tests if @desktop_file represents an application that can accept
565  * documents on the command line.
566  *
567  * Return value: %TRUE or %FALSE
568  **/
569 gboolean
570 egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file)
571 {
572   return desktop_file->document_code != 0;
573 }
574
575 /**
576  * egg_desktop_file_accepts_multiple:
577  * @desktop_file: an #EggDesktopFile
578  *
579  * Tests if @desktop_file can accept multiple documents at once.
580  *
581  * If this returns %FALSE, you can still pass multiple documents to
582  * egg_desktop_file_launch(), but that will result in multiple copies
583  * of the application being launched. See egg_desktop_file_launch()
584  * for more details.
585  *
586  * Return value: %TRUE or %FALSE
587  **/
588 gboolean
589 egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file)
590 {
591   return (desktop_file->document_code == 'F' ||
592           desktop_file->document_code == 'U');
593 }
594
595 /**
596  * egg_desktop_file_accepts_uris:
597  * @desktop_file: an #EggDesktopFile
598  *
599  * Tests if @desktop_file can accept (non-"file:") URIs as documents to
600  * open.
601  *
602  * Return value: %TRUE or %FALSE
603  **/
604 gboolean
605 egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file)
606 {
607   return (desktop_file->document_code == 'U' ||
608           desktop_file->document_code == 'u');
609 }
610
611 static void
612 append_quoted_word (GString    *str,
613                     const char *s,
614                     gboolean    in_single_quotes,
615                     gboolean    in_double_quotes)
616 {
617   const char *p;
618
619   if (!in_single_quotes && !in_double_quotes)
620     g_string_append_c (str, '\'');
621   else if (!in_single_quotes && in_double_quotes)
622     g_string_append (str, "\"'");
623
624   if (!strchr (s, '\''))
625     g_string_append (str, s);
626   else
627     {
628       for (p = s; *p != '\0'; p++)
629         {
630           if (*p == '\'')
631             g_string_append (str, "'\\''");
632           else
633             g_string_append_c (str, *p);
634         }
635     }
636
637   if (!in_single_quotes && !in_double_quotes)
638     g_string_append_c (str, '\'');
639   else if (!in_single_quotes && in_double_quotes)
640     g_string_append (str, "'\"");
641 }
642
643 static void
644 do_percent_subst (EggDesktopFile *desktop_file,
645                   char            code,
646                   GString        *str,
647                   GSList        **documents,
648                   gboolean        in_single_quotes,
649                   gboolean        in_double_quotes)
650 {
651   GSList *d;
652   char *doc;
653
654   switch (code)
655     {
656     case '%':
657       g_string_append_c (str, '%');
658       break;
659
660     case 'F':
661     case 'U':
662       for (d = *documents; d; d = d->next)
663         {
664           doc = d->data;
665           g_string_append (str, " ");
666           append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
667         }
668       *documents = NULL;
669       break;
670
671     case 'f':
672     case 'u':
673       if (*documents)
674         {
675           doc = (*documents)->data;
676           g_string_append (str, " ");
677           append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
678           *documents = (*documents)->next;
679         }
680       break;
681
682     case 'i':
683       if (desktop_file->icon)
684         {
685           g_string_append (str, "--icon ");
686           append_quoted_word (str, desktop_file->icon,
687                               in_single_quotes, in_double_quotes);
688         }
689       break;
690
691     case 'c':
692       if (desktop_file->name)
693         {
694           append_quoted_word (str, desktop_file->name,
695                               in_single_quotes, in_double_quotes);
696         }
697       break;
698
699     case 'k':
700       if (desktop_file->source)
701         {
702           append_quoted_word (str, desktop_file->source,
703                               in_single_quotes, in_double_quotes);
704         }
705       break;
706
707     case 'D':
708     case 'N':
709     case 'd':
710     case 'n':
711     case 'v':
712     case 'm':
713       /* Deprecated; skip */
714       break;
715
716     default:
717       g_warning ("Unrecognized %%-code '%%%c' in Exec", code);
718       break;
719     }
720 }
721
722 static char *
723 parse_exec (EggDesktopFile  *desktop_file,
724             GSList         **documents,
725             GError         **error)
726 {
727   char *exec, *p, *command;
728   gboolean escape, single_quot, double_quot;
729   GString *gs;
730
731   exec = g_key_file_get_string (desktop_file->key_file,
732                                 EGG_DESKTOP_FILE_GROUP,
733                                 EGG_DESKTOP_FILE_KEY_EXEC,
734                                 error);
735   if (!exec)
736     return NULL;
737
738   /* Build the command */
739   gs = g_string_new (NULL);
740   escape = single_quot = double_quot = FALSE;
741
742   for (p = exec; *p != '\0'; p++)
743     {
744       if (escape)
745         {
746           escape = FALSE;
747           g_string_append_c (gs, *p);
748         }
749       else if (*p == '\\')
750         {
751           if (!single_quot)
752             escape = TRUE;
753           g_string_append_c (gs, *p);
754         }
755       else if (*p == '\'')
756         {
757           g_string_append_c (gs, *p);
758           if (!single_quot && !double_quot)
759             single_quot = TRUE;
760           else if (single_quot)
761             single_quot = FALSE;
762         }
763       else if (*p == '"')
764         {
765           g_string_append_c (gs, *p);
766           if (!single_quot && !double_quot)
767             double_quot = TRUE;
768           else if (double_quot)
769             double_quot = FALSE;
770         }
771       else if (*p == '%' && p[1])
772         {
773           do_percent_subst (desktop_file, p[1], gs, documents,
774                             single_quot, double_quot);
775           p++;
776         }
777       else
778         g_string_append_c (gs, *p);
779     }
780
781   g_free (exec);
782   command = g_string_free (gs, FALSE);
783
784   /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */
785   if (g_key_file_has_key (desktop_file->key_file,
786                           EGG_DESKTOP_FILE_GROUP,
787                           EGG_DESKTOP_FILE_KEY_TERMINAL,
788                           NULL))
789     {
790       GError *terminal_error = NULL;
791       gboolean use_terminal =
792         g_key_file_get_boolean (desktop_file->key_file,
793                                 EGG_DESKTOP_FILE_GROUP,
794                                 EGG_DESKTOP_FILE_KEY_TERMINAL,
795                                 &terminal_error);
796       if (terminal_error)
797         {
798           g_free (command);
799           g_propagate_error (error, terminal_error);
800           return NULL;
801         }
802
803       if (use_terminal)
804         {
805           gs = g_string_new ("xdg-terminal ");
806           append_quoted_word (gs, command, FALSE, FALSE);
807           g_free (command);
808           command = g_string_free (gs, FALSE);
809         }
810     }
811
812   return command;
813 }
814
815 static GSList *
816 translate_document_list (EggDesktopFile *desktop_file, GSList *documents)
817 {
818   gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file);
819   GSList *ret, *d;
820
821   for (d = documents, ret = NULL; d; d = d->next)
822     {
823       const char *document = d->data;
824       gboolean is_uri = !g_path_is_absolute (document);
825       char *translated;
826
827       if (accepts_uris)
828         {
829           if (is_uri)
830             translated = g_strdup (document);
831           else
832             translated = g_filename_to_uri (document, NULL, NULL);
833         }
834       else
835         {
836           if (is_uri)
837             translated = g_filename_from_uri (document, NULL, NULL);
838           else
839             translated = g_strdup (document);
840         }
841
842       if (translated)
843         ret = g_slist_prepend (ret, translated);
844     }
845
846   return g_slist_reverse (ret);
847 }
848
849 static void
850 free_document_list (GSList *documents)
851 {
852   GSList *d;
853
854   for (d = documents; d; d = d->next)
855     g_free (d->data);
856   g_slist_free (documents);
857 }
858
859 /**
860  * egg_desktop_file_parse_exec:
861  * @desktop_file: a #EggDesktopFile
862  * @documents: a list of document paths or URIs
863  * @error: error pointer
864  *
865  * Parses @desktop_file's Exec key, inserting @documents into it, and
866  * returns the result.
867  *
868  * If @documents contains non-file: URIs and @desktop_file does not
869  * accept URIs, those URIs will be ignored. Likewise, if @documents
870  * contains more elements than @desktop_file accepts, the extra
871  * documents will be ignored.
872  *
873  * Return value: the parsed Exec string
874  **/
875 char *
876 egg_desktop_file_parse_exec (EggDesktopFile  *desktop_file,
877                              GSList          *documents,
878                              GError         **error)
879 {
880   GSList *translated, *docs;
881   char *command;
882
883   docs = translated = translate_document_list (desktop_file, documents);
884   command = parse_exec (desktop_file, &docs, error);
885   free_document_list (translated);
886
887   return command;
888 }
889
890 static gboolean
891 parse_link (EggDesktopFile  *desktop_file,
892             EggDesktopFile **app_desktop_file,
893             GSList         **documents,
894             GError         **error)
895 {
896   char *url;
897   GKeyFile *key_file;
898
899   url = g_key_file_get_string (desktop_file->key_file,
900                                EGG_DESKTOP_FILE_GROUP,
901                                EGG_DESKTOP_FILE_KEY_URL,
902                                error);
903   if (!url)
904     return FALSE;
905   *documents = g_slist_prepend (NULL, url);
906
907   /* FIXME: use gvfs */
908   key_file = g_key_file_new ();
909   g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
910                          EGG_DESKTOP_FILE_KEY_NAME,
911                          "xdg-open");
912   g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
913                          EGG_DESKTOP_FILE_KEY_TYPE,
914                          "Application");
915   g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
916                          EGG_DESKTOP_FILE_KEY_EXEC,
917                          "xdg-open %u");
918   *app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL);
919   return TRUE;
920 }
921
922 #if GTK_CHECK_VERSION (2, 12, 0)
923 static char *
924 start_startup_notification (GdkDisplay     *display,
925                             EggDesktopFile *desktop_file,
926                             const char     *argv0,
927                             int             screen,
928                             int             workspace,
929                             guint32         launch_time)
930 {
931   static int sequence = 0;
932   char *startup_id;
933   char *description, *wmclass;
934   char *screen_str, *workspace_str;
935
936   if (g_key_file_has_key (desktop_file->key_file,
937                           EGG_DESKTOP_FILE_GROUP,
938                           EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
939                           NULL))
940     {
941       if (!g_key_file_get_boolean (desktop_file->key_file,
942                                    EGG_DESKTOP_FILE_GROUP,
943                                    EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
944                                    NULL))
945         return NULL;
946       wmclass = NULL;
947     }
948   else
949     {
950       wmclass = g_key_file_get_string (desktop_file->key_file,
951                                        EGG_DESKTOP_FILE_GROUP,
952                                        EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS,
953                                        NULL);
954       if (!wmclass)
955         return NULL;
956     }
957
958   if (launch_time == (guint32)-1)
959     launch_time = gdk_x11_display_get_user_time (display);
960   startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
961                                 g_get_prgname (),
962                                 (unsigned long)getpid (),
963                                 g_get_host_name (),
964                                 argv0,
965                                 sequence++,
966                                 (unsigned long)launch_time);
967
968   description = g_strdup_printf (_("Starting %s"), desktop_file->name);
969   screen_str = g_strdup_printf ("%d", screen);
970   workspace_str = workspace == -1 ? NULL : g_strdup_printf ("%d", workspace);
971
972   gdk_x11_display_broadcast_startup_message (display, "new",
973                                              "ID", startup_id,
974                                              "NAME", desktop_file->name,
975                                              "SCREEN", screen_str,
976                                              "BIN", argv0,
977                                              "ICON", desktop_file->icon,
978                                              "DESKTOP", workspace_str,
979                                              "DESCRIPTION", description,
980                                              "WMCLASS", wmclass,
981                                              NULL);
982
983   g_free (description);
984   g_free (wmclass);
985   g_free (screen_str);
986   g_free (workspace_str);
987
988   return startup_id;
989 }
990
991 static void
992 end_startup_notification (GdkDisplay *display,
993                           const char *startup_id)
994 {
995   gdk_x11_display_broadcast_startup_message (display, "remove",
996                                              "ID", startup_id,
997                                              NULL);
998 }
999
1000 #define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */)
1001
1002 typedef struct {
1003   GdkDisplay *display;
1004   char *startup_id;
1005 } StartupNotificationData;
1006
1007 static gboolean
1008 startup_notification_timeout (gpointer data)
1009 {
1010   StartupNotificationData *sn_data = data;
1011
1012   end_startup_notification (sn_data->display, sn_data->startup_id);
1013   g_object_unref (sn_data->display);
1014   g_free (sn_data->startup_id);
1015   g_free (sn_data);
1016
1017   return FALSE;
1018 }
1019
1020 static void
1021 set_startup_notification_timeout (GdkDisplay *display,
1022                                   const char *startup_id)
1023 {
1024   StartupNotificationData *sn_data;
1025
1026   sn_data = g_new (StartupNotificationData, 1);
1027   sn_data->display = g_object_ref (display);
1028   sn_data->startup_id = g_strdup (startup_id);
1029
1030   g_timeout_add_seconds (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH,
1031                          startup_notification_timeout, sn_data);
1032 }
1033 #endif /* GTK 2.12 */
1034
1035 static GPtrArray *
1036 array_putenv (GPtrArray *env, char *variable)
1037 {
1038   guint i, keylen;
1039
1040   if (!env)
1041     {
1042       char **envp;
1043
1044       env = g_ptr_array_new ();
1045
1046       envp = g_listenv ();
1047       for (i = 0; envp[i]; i++)
1048         {
1049           const char *value;
1050
1051           value = g_getenv (envp[i]);
1052           g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i],
1053                                                  value ? value : ""));
1054         }
1055       g_strfreev (envp);
1056     }
1057
1058   keylen = strcspn (variable, "=");
1059
1060   /* Remove old value of key */
1061   for (i = 0; i < env->len; i++)
1062     {
1063       char *envvar = env->pdata[i];
1064
1065       if (!strncmp (envvar, variable, keylen) && envvar[keylen] == '=')
1066         {
1067           g_free (envvar);
1068           g_ptr_array_remove_index_fast (env, i);
1069           break;
1070         }
1071     }
1072
1073   /* Add new value */
1074   g_ptr_array_add (env, g_strdup (variable));
1075
1076   return env;
1077 }
1078
1079 static gboolean
1080 egg_desktop_file_launchv (EggDesktopFile *desktop_file,
1081                           GSList *documents, va_list args,
1082                           GError **error)
1083 {
1084   EggDesktopFileLaunchOption option;
1085   GSList *translated_documents = NULL, *docs = NULL;
1086   char *command, **argv;
1087   int argc, i, screen_num;
1088   gboolean success, current_success;
1089   GdkDisplay *display;
1090   char *startup_id;
1091
1092   GPtrArray   *env = NULL;
1093   char       **variables = NULL;
1094   GdkScreen   *screen = NULL;
1095   int          workspace = -1;
1096   const char  *directory = NULL;
1097   guint32      launch_time = (guint32)-1;
1098   GSpawnFlags  flags = G_SPAWN_SEARCH_PATH;
1099   GSpawnChildSetupFunc setup_func = NULL;
1100   gpointer     setup_data = NULL;
1101
1102   GPid        *ret_pid = NULL;
1103   int         *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL;
1104   char       **ret_startup_id = NULL;
1105
1106   if (documents && desktop_file->document_code == 0)
1107     {
1108       g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1109                    EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
1110                    _("Application does not accept documents on command line"));
1111       return FALSE;
1112     }
1113
1114   /* Read the options: technically it's incorrect for the caller to
1115    * NULL-terminate the list of options (rather than 0-terminating
1116    * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED,
1117    * it's more consistent with other glib/gtk methods, and it will
1118    * work as long as sizeof (int) <= sizeof (NULL), and NULL is
1119    * represented as 0. (Which is true everywhere we care about.)
1120    */
1121   while ((option = va_arg (args, EggDesktopFileLaunchOption)))
1122     {
1123       switch (option)
1124         {
1125         case EGG_DESKTOP_FILE_LAUNCH_CLEARENV:
1126           if (env)
1127             g_ptr_array_free (env, TRUE);
1128           env = g_ptr_array_new ();
1129           break;
1130         case EGG_DESKTOP_FILE_LAUNCH_PUTENV:
1131           variables = va_arg (args, char **);
1132           for (i = 0; variables[i]; i++)
1133             env = array_putenv (env, variables[i]);
1134           break;
1135
1136         case EGG_DESKTOP_FILE_LAUNCH_SCREEN:
1137           screen = va_arg (args, GdkScreen *);
1138           break;
1139         case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE:
1140           workspace = va_arg (args, int);
1141           break;
1142
1143         case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY:
1144           directory = va_arg (args, const char *);
1145           break;
1146         case EGG_DESKTOP_FILE_LAUNCH_TIME:
1147           launch_time = va_arg (args, guint32);
1148           break;
1149         case EGG_DESKTOP_FILE_LAUNCH_FLAGS:
1150           flags |= va_arg (args, GSpawnFlags);
1151           /* Make sure they didn't set any flags that don't make sense. */
1152           flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO;
1153           break;
1154         case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC:
1155           setup_func = va_arg (args, GSpawnChildSetupFunc);
1156           setup_data = va_arg (args, gpointer);
1157           break;
1158
1159         case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID:
1160           ret_pid = va_arg (args, GPid *);
1161           break;
1162         case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE:
1163           ret_stdin = va_arg (args, int *);
1164           break;
1165         case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE:
1166           ret_stdout = va_arg (args, int *);
1167           break;
1168         case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE:
1169           ret_stderr = va_arg (args, int *);
1170           break;
1171         case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID:
1172           ret_startup_id = va_arg (args, char **);
1173           break;
1174
1175         default:
1176           g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1177                        EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION,
1178                        _("Unrecognized launch option: %d"),
1179                        GPOINTER_TO_INT (option));
1180           success = FALSE;
1181           goto out;
1182         }
1183     }
1184
1185   if (screen)
1186     {
1187       char *display_name = gdk_screen_make_display_name (screen);
1188       char *display_env = g_strdup_printf ("DISPLAY=%s", display_name);
1189       env = array_putenv (env, display_env);
1190       g_free (display_name);
1191       g_free (display_env);
1192
1193       display = gdk_screen_get_display (screen);
1194     }
1195   else
1196     {
1197       display = gdk_display_get_default ();
1198       screen = gdk_display_get_default_screen (display);
1199     }
1200   screen_num = gdk_screen_get_number (screen);
1201
1202   translated_documents = translate_document_list (desktop_file, documents);
1203   docs = translated_documents;
1204
1205   success = FALSE;
1206
1207   do
1208     {
1209       command = parse_exec (desktop_file, &docs, error);
1210       if (!command)
1211         goto out;
1212
1213       if (!g_shell_parse_argv (command, &argc, &argv, error))
1214         {
1215           g_free (command);
1216           goto out;
1217         }
1218       g_free (command);
1219
1220 #if GTK_CHECK_VERSION (2, 12, 0)
1221       startup_id = start_startup_notification (display, desktop_file,
1222                                                argv[0], screen_num,
1223                                                workspace, launch_time);
1224       if (startup_id)
1225         {
1226           char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
1227                                                   startup_id);
1228           env = array_putenv (env, startup_id_env);
1229           g_free (startup_id_env);
1230         }
1231 #else
1232       startup_id = NULL;
1233 #endif /* GTK 2.12 */
1234
1235       if (env != NULL)
1236         g_ptr_array_add (env, NULL);
1237
1238       current_success =
1239         g_spawn_async_with_pipes (directory,
1240                                   argv,
1241                                   env ? (char **)(env->pdata) : NULL,
1242                                   flags,
1243                                   setup_func, setup_data,
1244                                   ret_pid,
1245                                   ret_stdin, ret_stdout, ret_stderr,
1246                                   error);
1247       g_strfreev (argv);
1248
1249       if (startup_id)
1250         {
1251 #if GTK_CHECK_VERSION (2, 12, 0)
1252           if (current_success)
1253             {
1254               set_startup_notification_timeout (display, startup_id);
1255
1256               if (ret_startup_id)
1257                 *ret_startup_id = startup_id;
1258               else
1259                 g_free (startup_id);
1260             }
1261           else
1262 #endif /* GTK 2.12 */
1263             g_free (startup_id);
1264         }
1265       else if (ret_startup_id)
1266         *ret_startup_id = NULL;
1267
1268       if (current_success)
1269         {
1270           /* If we successfully launch any instances of the app, make
1271            * sure we return TRUE and don't set @error.
1272            */
1273           success = TRUE;
1274           error = NULL;
1275
1276           /* Also, only set the output params on the first one */
1277           ret_pid = NULL;
1278           ret_stdin = ret_stdout = ret_stderr = NULL;
1279           ret_startup_id = NULL;
1280         }
1281     }
1282   while (docs && current_success);
1283
1284  out:
1285   if (env)
1286     {
1287       g_ptr_array_foreach (env, (GFunc)g_free, NULL);
1288       g_ptr_array_free (env, TRUE);
1289     }
1290   free_document_list (translated_documents);
1291
1292   return success;
1293 }
1294
1295 /**
1296  * egg_desktop_file_launch:
1297  * @desktop_file: an #EggDesktopFile
1298  * @documents: a list of URIs or paths to documents to open
1299  * @error: error pointer
1300  * @...: additional options
1301  *
1302  * Launches @desktop_file with the given arguments. Additional options
1303  * can be specified as follows:
1304  *
1305  *   %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments)
1306  *       clears the environment in the child process
1307  *   %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables)
1308  *       adds the NAME=VALUE strings in the given %NULL-terminated
1309  *       array to the child process's environment
1310  *   %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen)
1311  *       causes the application to be launched on the given screen
1312  *   %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace)
1313  *       causes the application to be launched on the given workspace
1314  *   %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir)
1315  *       causes the application to be launched in the given directory
1316  *   %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time)
1317  *       sets the "launch time" for the application. If the user
1318  *       interacts with another window after @launch_time but before
1319  *       the launched application creates its first window, the window
1320  *       manager may choose to not give focus to the new application.
1321  *       Passing 0 for @launch_time will explicitly request that the
1322  *       application not receive focus.
1323  *   %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags)
1324  *       Sets additional #GSpawnFlags to use. See g_spawn_async() for
1325  *       more details.
1326  *   %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer)
1327  *       Sets the child setup callback and the data to pass to it.
1328  *       (See g_spawn_async() for more details.)
1329  *
1330  *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid)
1331  *       On a successful launch, sets *@pid to the PID of the launched
1332  *       application.
1333  *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id)
1334  *       On a successful launch, sets *@startup_id to the Startup
1335  *       Notification "startup id" of the launched application.
1336  *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd)
1337  *       On a successful launch, sets *@fd to the file descriptor of
1338  *       a pipe connected to the application's stdin.
1339  *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd)
1340  *       On a successful launch, sets *@fd to the file descriptor of
1341  *       a pipe connected to the application's stdout.
1342  *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd)
1343  *       On a successful launch, sets *@fd to the file descriptor of
1344  *       a pipe connected to the application's stderr.
1345  *
1346  * The options should be terminated with a single %NULL.
1347  *
1348  * If @documents contains multiple documents, but
1349  * egg_desktop_file_accepts_multiple() returns %FALSE for
1350  * @desktop_file, then egg_desktop_file_launch() will actually launch
1351  * multiple instances of the application. In that case, the return
1352  * value (as well as any values passed via
1353  * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the
1354  * first instance of the application that was launched (but the
1355  * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each
1356  * instance).
1357  *
1358  * Return value: %TRUE if the application was successfully launched.
1359  **/
1360 gboolean
1361 egg_desktop_file_launch (EggDesktopFile *desktop_file,
1362                          GSList *documents, GError **error,
1363                          ...)
1364 {
1365   va_list args;
1366   gboolean success;
1367   EggDesktopFile *app_desktop_file;
1368
1369   switch (desktop_file->type)
1370     {
1371     case EGG_DESKTOP_FILE_TYPE_APPLICATION:
1372       va_start (args, error);
1373       success = egg_desktop_file_launchv (desktop_file, documents,
1374                                           args, error);
1375       va_end (args);
1376       break;
1377
1378     case EGG_DESKTOP_FILE_TYPE_LINK:
1379       if (documents)
1380         {
1381           g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1382                        EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
1383                        _("Can't pass document URIs to a 'Type=Link' desktop entry"));
1384           return FALSE;
1385         }         
1386
1387       if (!parse_link (desktop_file, &app_desktop_file, &documents, error))
1388         return FALSE;
1389
1390       va_start (args, error);
1391       success = egg_desktop_file_launchv (app_desktop_file, documents,
1392                                           args, error);
1393       va_end (args);
1394
1395       egg_desktop_file_free (app_desktop_file);
1396       free_document_list (documents);
1397       break;
1398
1399     case EGG_DESKTOP_FILE_TYPE_UNRECOGNIZED:
1400     case EGG_DESKTOP_FILE_TYPE_DIRECTORY:
1401     default:
1402       g_set_error (error, EGG_DESKTOP_FILE_ERROR,
1403                    EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
1404                    _("Not a launchable item"));
1405       success = FALSE;
1406       break;
1407     }
1408
1409   return success;
1410 }
1411
1412
1413 GQuark
1414 egg_desktop_file_error_quark (void)
1415 {
1416   return g_quark_from_static_string ("egg-desktop_file-error-quark");
1417 }
1418
1419
1420 G_LOCK_DEFINE_STATIC (egg_desktop_file);
1421 static EggDesktopFile *egg_desktop_file;
1422
1423 static void
1424 egg_set_desktop_file_internal (const char *desktop_file_path,
1425                                gboolean set_defaults)
1426 {
1427   GError *error = NULL;
1428
1429   G_LOCK (egg_desktop_file);
1430   if (egg_desktop_file)
1431     egg_desktop_file_free (egg_desktop_file);
1432
1433   egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error);
1434   if (error)
1435     {
1436       g_warning ("Could not load desktop file '%s': %s",
1437                  desktop_file_path, error->message);
1438       g_error_free (error);
1439     }
1440
1441   if (set_defaults && egg_desktop_file != NULL) {
1442     /* Set localized application name and default window icon */
1443     if (egg_desktop_file->name)
1444       g_set_application_name (egg_desktop_file->name);
1445     if (egg_desktop_file->icon)
1446       {
1447         if (g_path_is_absolute (egg_desktop_file->icon))
1448           gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL);
1449         else
1450           gtk_window_set_default_icon_name (egg_desktop_file->icon);
1451       }
1452   }
1453
1454   G_UNLOCK (egg_desktop_file);
1455 }
1456
1457 /**
1458  * egg_set_desktop_file:
1459  * @desktop_file_path: path to the application's desktop file
1460  *
1461  * Creates an #EggDesktopFile for the application from the data at
1462  * @desktop_file_path. This will also call g_set_application_name()
1463  * with the localized application name from the desktop file, and
1464  * gtk_window_set_default_icon_name() or
1465  * gtk_window_set_default_icon_from_file() with the application's
1466  * icon. Other code may use additional information from the desktop
1467  * file.
1468  * See egg_set_desktop_file_without_defaults() for a variant of this
1469  * function that does not set the application name and default window
1470  * icon.
1471  *
1472  * Note that for thread safety reasons, this function can only
1473  * be called once, and is mutually exclusive with calling
1474  * egg_set_desktop_file_without_defaults().
1475  **/
1476 void
1477 egg_set_desktop_file (const char *desktop_file_path)
1478 {
1479   egg_set_desktop_file_internal (desktop_file_path, TRUE);
1480 }
1481
1482 /**
1483  * egg_set_desktop_file_without_defaults:
1484  * @desktop_file_path: path to the application's desktop file
1485  *
1486  * Creates an #EggDesktopFile for the application from the data at
1487  * @desktop_file_path.
1488  * See egg_set_desktop_file() for a variant of this function that
1489  * sets the application name and default window icon from the information
1490  * in the desktop file.
1491  *
1492  * Note that for thread safety reasons, this function can only
1493  * be called once, and is mutually exclusive with calling
1494  * egg_set_desktop_file().
1495  **/
1496 void
1497 egg_set_desktop_file_without_defaults (const char *desktop_file_path)
1498 {
1499   egg_set_desktop_file_internal (desktop_file_path, FALSE);
1500 }
1501
1502 /**
1503  * egg_get_desktop_file:
1504  * 
1505  * Gets the application's #EggDesktopFile, as set by
1506  * egg_set_desktop_file().
1507  * 
1508  * Return value: the #EggDesktopFile, or %NULL if it hasn't been set.
1509  **/
1510 EggDesktopFile *
1511 egg_get_desktop_file (void)
1512 {
1513   EggDesktopFile *retval;
1514
1515   G_LOCK (egg_desktop_file);
1516   retval = egg_desktop_file;
1517   G_UNLOCK (egg_desktop_file);
1518
1519   return retval;
1520 }