~ubuntu-branches/ubuntu/oneiric/avant-window-navigator/oneiric

« back to all changes in this revision

Viewing changes to libawn/egg/eggdesktopfile.c

  • Committer: Bazaar Package Importer
  • Author(s): Julien Lavergne
  • Date: 2008-05-24 14:42:01 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080524144201-r3v8e4g2hv2q1i9x
Tags: 0.2.6-6
* debian/patches/04-fix-colormap.patch
 - Fix crash in awn-manager if colormap == None. Thanks Emme for the 
   patch. (Closes: #482030) 

Show diffs side-by-side

added added

removed removed

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