~ubuntu-branches/ubuntu/maverick/gnome-session/maverick

« back to all changes in this revision

Viewing changes to egg/eggdesktopfile.c

  • Committer: Bazaar Package Importer
  • Author(s): Josselin Mouette
  • Date: 2009-04-14 19:24:09 UTC
  • mto: (1.3.1 upstream) (2.1.5 experimental)
  • mto: This revision was merged to the branch mainline in revision 112.
  • Revision ID: james.westby@ubuntu.com-20090414192409-koxw9bt1lf1zr01z
ImportĀ upstreamĀ versionĀ 2.26.1

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/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
char **
 
435
egg_desktop_file_get_string_list (EggDesktopFile  *desktop_file,
 
436
                                  const char      *key,
 
437
                                  gsize           *length,
 
438
                                  GError         **error)
 
439
{
 
440
  return g_key_file_get_string_list (desktop_file->key_file,
 
441
                                     EGG_DESKTOP_FILE_GROUP, key, length,
 
442
                                     error);
 
443
}
 
444
 
 
445
char **
 
446
egg_desktop_file_get_locale_string_list (EggDesktopFile  *desktop_file,
 
447
                                         const char      *key,
 
448
                                         const char      *locale,
 
449
                                         gsize           *length,
 
450
                                         GError         **error)
 
451
{
 
452
  return g_key_file_get_locale_string_list (desktop_file->key_file,
 
453
                                            EGG_DESKTOP_FILE_GROUP, key,
 
454
                                            locale, length,
 
455
                                            error);
 
456
}
 
457
 
 
458
/**
 
459
 * egg_desktop_file_can_launch:
 
460
 * @desktop_file: an #EggDesktopFile
 
461
 * @desktop_environment: the name of the running desktop environment,
 
462
 * or %NULL
 
463
 *
 
464
 * Tests if @desktop_file can/should be launched in the current
 
465
 * environment. If @desktop_environment is non-%NULL, @desktop_file's
 
466
 * "OnlyShowIn" and "NotShowIn" keys are checked to make sure that
 
467
 * this desktop_file is appropriate for the named environment.
 
468
 *
 
469
 * Furthermore, if @desktop_file has type
 
470
 * %EGG_DESKTOP_FILE_TYPE_APPLICATION, its "TryExec" key (if any) is
 
471
 * also checked, to make sure the binary it points to exists.
 
472
 *
 
473
 * egg_desktop_file_can_launch() does NOT check the value of the
 
474
 * "Hidden" key.
 
475
 *
 
476
 * Return value: %TRUE if @desktop_file can be launched
 
477
 **/
 
478
gboolean
 
479
egg_desktop_file_can_launch (EggDesktopFile *desktop_file,
 
480
                             const char     *desktop_environment)
 
481
{
 
482
  char *try_exec, *found_program;
 
483
  char **only_show_in, **not_show_in;
 
484
  gboolean found;
 
485
  int i;
 
486
 
 
487
  if (desktop_file->type != EGG_DESKTOP_FILE_TYPE_APPLICATION &&
 
488
      desktop_file->type != EGG_DESKTOP_FILE_TYPE_LINK)
 
489
    return FALSE;
 
490
 
 
491
  if (desktop_environment)
 
492
    {
 
493
      only_show_in = g_key_file_get_string_list (desktop_file->key_file,
 
494
                                                 EGG_DESKTOP_FILE_GROUP,
 
495
                                                 EGG_DESKTOP_FILE_KEY_ONLY_SHOW_IN,
 
496
                                                 NULL, NULL);
 
497
      if (only_show_in)
 
498
        {
 
499
          for (i = 0, found = FALSE; only_show_in[i] && !found; i++)
 
500
            {
 
501
              if (!strcmp (only_show_in[i], desktop_environment))
 
502
                found = TRUE;
 
503
            }
 
504
 
 
505
          g_strfreev (only_show_in);
 
506
 
 
507
          if (!found)
 
508
            return FALSE;
 
509
        }
 
510
 
 
511
      not_show_in = g_key_file_get_string_list (desktop_file->key_file,
 
512
                                                EGG_DESKTOP_FILE_GROUP,
 
513
                                                EGG_DESKTOP_FILE_KEY_NOT_SHOW_IN,
 
514
                                                NULL, NULL);
 
515
      if (not_show_in)
 
516
        {
 
517
          for (i = 0, found = FALSE; not_show_in[i] && !found; i++)
 
518
            {
 
519
              if (!strcmp (not_show_in[i], desktop_environment))
 
520
                found = TRUE;
 
521
            }
 
522
 
 
523
          g_strfreev (not_show_in);
 
524
 
 
525
          if (found)
 
526
            return FALSE;
 
527
        }
 
528
    }
 
529
 
 
530
  if (desktop_file->type == EGG_DESKTOP_FILE_TYPE_APPLICATION)
 
531
    {
 
532
      try_exec = g_key_file_get_string (desktop_file->key_file,
 
533
                                        EGG_DESKTOP_FILE_GROUP,
 
534
                                        EGG_DESKTOP_FILE_KEY_TRY_EXEC,
 
535
                                        NULL);
 
536
      if (try_exec)
 
537
        {
 
538
          found_program = g_find_program_in_path (try_exec);
 
539
          g_free (try_exec);
 
540
 
 
541
          if (!found_program)
 
542
            return FALSE;
 
543
          g_free (found_program);
 
544
        }
 
545
    }
 
546
 
 
547
  return TRUE;
 
548
}
 
549
 
 
550
/**
 
551
 * egg_desktop_file_accepts_documents:
 
552
 * @desktop_file: an #EggDesktopFile
 
553
 *
 
554
 * Tests if @desktop_file represents an application that can accept
 
555
 * documents on the command line.
 
556
 *
 
557
 * Return value: %TRUE or %FALSE
 
558
 **/
 
559
gboolean
 
560
egg_desktop_file_accepts_documents (EggDesktopFile *desktop_file)
 
561
{
 
562
  return desktop_file->document_code != 0;
 
563
}
 
564
 
 
565
/**
 
566
 * egg_desktop_file_accepts_multiple:
 
567
 * @desktop_file: an #EggDesktopFile
 
568
 *
 
569
 * Tests if @desktop_file can accept multiple documents at once.
 
570
 *
 
571
 * If this returns %FALSE, you can still pass multiple documents to
 
572
 * egg_desktop_file_launch(), but that will result in multiple copies
 
573
 * of the application being launched. See egg_desktop_file_launch()
 
574
 * for more details.
 
575
 *
 
576
 * Return value: %TRUE or %FALSE
 
577
 **/
 
578
gboolean
 
579
egg_desktop_file_accepts_multiple (EggDesktopFile *desktop_file)
 
580
{
 
581
  return (desktop_file->document_code == 'F' ||
 
582
          desktop_file->document_code == 'U');
 
583
}
 
584
 
 
585
/**
 
586
 * egg_desktop_file_accepts_uris:
 
587
 * @desktop_file: an #EggDesktopFile
 
588
 *
 
589
 * Tests if @desktop_file can accept (non-"file:") URIs as documents to
 
590
 * open.
 
591
 *
 
592
 * Return value: %TRUE or %FALSE
 
593
 **/
 
594
gboolean
 
595
egg_desktop_file_accepts_uris (EggDesktopFile *desktop_file)
 
596
{
 
597
  return (desktop_file->document_code == 'U' ||
 
598
          desktop_file->document_code == 'u');
 
599
}
 
600
 
 
601
static void
 
602
append_quoted_word (GString    *str,
 
603
                    const char *s,
 
604
                    gboolean    in_single_quotes,
 
605
                    gboolean    in_double_quotes)
 
606
{
 
607
  const char *p;
 
608
 
 
609
  if (!in_single_quotes && !in_double_quotes)
 
610
    g_string_append_c (str, '\'');
 
611
  else if (!in_single_quotes && in_double_quotes)
 
612
    g_string_append (str, "\"'");
 
613
 
 
614
  if (!strchr (s, '\''))
 
615
    g_string_append (str, s);
 
616
  else
 
617
    {
 
618
      for (p = s; *p != '\0'; p++)
 
619
        {
 
620
          if (*p == '\'')
 
621
            g_string_append (str, "'\\''");
 
622
          else
 
623
            g_string_append_c (str, *p);
 
624
        }
 
625
    }
 
626
 
 
627
  if (!in_single_quotes && !in_double_quotes)
 
628
    g_string_append_c (str, '\'');
 
629
  else if (!in_single_quotes && in_double_quotes)
 
630
    g_string_append (str, "'\"");
 
631
}
 
632
 
 
633
static void
 
634
do_percent_subst (EggDesktopFile *desktop_file,
 
635
                  char            code,
 
636
                  GString        *str,
 
637
                  GSList        **documents,
 
638
                  gboolean        in_single_quotes,
 
639
                  gboolean        in_double_quotes)
 
640
{
 
641
  GSList *d;
 
642
  char *doc;
 
643
 
 
644
  switch (code)
 
645
    {
 
646
    case '%':
 
647
      g_string_append_c (str, '%');
 
648
      break;
 
649
 
 
650
    case 'F':
 
651
    case 'U':
 
652
      for (d = *documents; d; d = d->next)
 
653
        {
 
654
          doc = d->data;
 
655
          g_string_append (str, " ");
 
656
          append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
 
657
        }
 
658
      *documents = NULL;
 
659
      break;
 
660
 
 
661
    case 'f':
 
662
    case 'u':
 
663
      if (*documents)
 
664
        {
 
665
          doc = (*documents)->data;
 
666
          g_string_append (str, " ");
 
667
          append_quoted_word (str, doc, in_single_quotes, in_double_quotes);
 
668
          *documents = (*documents)->next;
 
669
        }
 
670
      break;
 
671
 
 
672
    case 'i':
 
673
      if (desktop_file->icon)
 
674
        {
 
675
          g_string_append (str, "--icon ");
 
676
          append_quoted_word (str, desktop_file->icon,
 
677
                              in_single_quotes, in_double_quotes);
 
678
        }
 
679
      break;
 
680
 
 
681
    case 'c':
 
682
      if (desktop_file->name)
 
683
        {
 
684
          append_quoted_word (str, desktop_file->name,
 
685
                              in_single_quotes, in_double_quotes);
 
686
        }
 
687
      break;
 
688
 
 
689
    case 'k':
 
690
      if (desktop_file->source)
 
691
        {
 
692
          append_quoted_word (str, desktop_file->source,
 
693
                              in_single_quotes, in_double_quotes);
 
694
        }
 
695
      break;
 
696
 
 
697
    case 'D':
 
698
    case 'N':
 
699
    case 'd':
 
700
    case 'n':
 
701
    case 'v':
 
702
    case 'm':
 
703
      /* Deprecated; skip */
 
704
      break;
 
705
 
 
706
    default:
 
707
      g_warning ("Unrecognized %%-code '%%%c' in Exec", code);
 
708
      break;
 
709
    }
 
710
}
 
711
 
 
712
static char *
 
713
parse_exec (EggDesktopFile  *desktop_file,
 
714
            GSList         **documents,
 
715
            GError         **error)
 
716
{
 
717
  char *exec, *p, *command;
 
718
  gboolean escape, single_quot, double_quot;
 
719
  GString *gs;
 
720
 
 
721
  exec = g_key_file_get_string (desktop_file->key_file,
 
722
                                EGG_DESKTOP_FILE_GROUP,
 
723
                                EGG_DESKTOP_FILE_KEY_EXEC,
 
724
                                error);
 
725
  if (!exec)
 
726
    return NULL;
 
727
 
 
728
  /* Build the command */
 
729
  gs = g_string_new (NULL);
 
730
  escape = single_quot = double_quot = FALSE;
 
731
 
 
732
  for (p = exec; *p != '\0'; p++)
 
733
    {
 
734
      if (escape)
 
735
        {
 
736
          escape = FALSE;
 
737
          g_string_append_c (gs, *p);
 
738
        }
 
739
      else if (*p == '\\')
 
740
        {
 
741
          if (!single_quot)
 
742
            escape = TRUE;
 
743
          g_string_append_c (gs, *p);
 
744
        }
 
745
      else if (*p == '\'')
 
746
        {
 
747
          g_string_append_c (gs, *p);
 
748
          if (!single_quot && !double_quot)
 
749
            single_quot = TRUE;
 
750
          else if (single_quot)
 
751
            single_quot = FALSE;
 
752
        }
 
753
      else if (*p == '"')
 
754
        {
 
755
          g_string_append_c (gs, *p);
 
756
          if (!single_quot && !double_quot)
 
757
            double_quot = TRUE;
 
758
          else if (double_quot)
 
759
            double_quot = FALSE;
 
760
        }
 
761
      else if (*p == '%' && p[1])
 
762
        {
 
763
          do_percent_subst (desktop_file, p[1], gs, documents,
 
764
                            single_quot, double_quot);
 
765
          p++;
 
766
        }
 
767
      else
 
768
        g_string_append_c (gs, *p);
 
769
    }
 
770
 
 
771
  g_free (exec);
 
772
  command = g_string_free (gs, FALSE);
 
773
 
 
774
  /* Prepend "xdg-terminal " if needed (FIXME: use gvfs) */
 
775
  if (g_key_file_has_key (desktop_file->key_file,
 
776
                          EGG_DESKTOP_FILE_GROUP,
 
777
                          EGG_DESKTOP_FILE_KEY_TERMINAL,
 
778
                          NULL))
 
779
    {
 
780
      GError *terminal_error = NULL;
 
781
      gboolean use_terminal =
 
782
        g_key_file_get_boolean (desktop_file->key_file,
 
783
                                EGG_DESKTOP_FILE_GROUP,
 
784
                                EGG_DESKTOP_FILE_KEY_TERMINAL,
 
785
                                &terminal_error);
 
786
      if (terminal_error)
 
787
        {
 
788
          g_free (command);
 
789
          g_propagate_error (error, terminal_error);
 
790
          return NULL;
 
791
        }
 
792
 
 
793
      if (use_terminal)
 
794
        {
 
795
          gs = g_string_new ("xdg-terminal ");
 
796
          append_quoted_word (gs, command, FALSE, FALSE);
 
797
          g_free (command);
 
798
          command = g_string_free (gs, FALSE);
 
799
        }
 
800
    }
 
801
 
 
802
  return command;
 
803
}
 
804
 
 
805
static GSList *
 
806
translate_document_list (EggDesktopFile *desktop_file, GSList *documents)
 
807
{
 
808
  gboolean accepts_uris = egg_desktop_file_accepts_uris (desktop_file);
 
809
  GSList *ret, *d;
 
810
 
 
811
  for (d = documents, ret = NULL; d; d = d->next)
 
812
    {
 
813
      const char *document = d->data;
 
814
      gboolean is_uri = !g_path_is_absolute (document);
 
815
      char *translated;
 
816
 
 
817
      if (accepts_uris)
 
818
        {
 
819
          if (is_uri)
 
820
            translated = g_strdup (document);
 
821
          else
 
822
            translated = g_filename_to_uri (document, NULL, NULL);
 
823
        }
 
824
      else
 
825
        {
 
826
          if (is_uri)
 
827
            translated = g_filename_from_uri (document, NULL, NULL);
 
828
          else
 
829
            translated = g_strdup (document);
 
830
        }
 
831
 
 
832
      if (translated)
 
833
        ret = g_slist_prepend (ret, translated);
 
834
    }
 
835
 
 
836
  return g_slist_reverse (ret);
 
837
}
 
838
 
 
839
static void
 
840
free_document_list (GSList *documents)
 
841
{
 
842
  GSList *d;
 
843
 
 
844
  for (d = documents; d; d = d->next)
 
845
    g_free (d->data);
 
846
  g_slist_free (documents);
 
847
}
 
848
 
 
849
/**
 
850
 * egg_desktop_file_parse_exec:
 
851
 * @desktop_file: a #EggDesktopFile
 
852
 * @documents: a list of document paths or URIs
 
853
 * @error: error pointer
 
854
 *
 
855
 * Parses @desktop_file's Exec key, inserting @documents into it, and
 
856
 * returns the result.
 
857
 *
 
858
 * If @documents contains non-file: URIs and @desktop_file does not
 
859
 * accept URIs, those URIs will be ignored. Likewise, if @documents
 
860
 * contains more elements than @desktop_file accepts, the extra
 
861
 * documents will be ignored.
 
862
 *
 
863
 * Return value: the parsed Exec string
 
864
 **/
 
865
char *
 
866
egg_desktop_file_parse_exec (EggDesktopFile  *desktop_file,
 
867
                             GSList          *documents,
 
868
                             GError         **error)
 
869
{
 
870
  GSList *translated, *docs;
 
871
  char *command;
 
872
 
 
873
  docs = translated = translate_document_list (desktop_file, documents);
 
874
  command = parse_exec (desktop_file, &docs, error);
 
875
  free_document_list (translated);
 
876
 
 
877
  return command;
 
878
}
 
879
 
 
880
static gboolean
 
881
parse_link (EggDesktopFile  *desktop_file,
 
882
            EggDesktopFile **app_desktop_file,
 
883
            GSList         **documents,
 
884
            GError         **error)
 
885
{
 
886
  char *url;
 
887
  GKeyFile *key_file;
 
888
 
 
889
  url = g_key_file_get_string (desktop_file->key_file,
 
890
                               EGG_DESKTOP_FILE_GROUP,
 
891
                               EGG_DESKTOP_FILE_KEY_URL,
 
892
                               error);
 
893
  if (!url)
 
894
    return FALSE;
 
895
  *documents = g_slist_prepend (NULL, url);
 
896
 
 
897
  /* FIXME: use gvfs */
 
898
  key_file = g_key_file_new ();
 
899
  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
 
900
                         EGG_DESKTOP_FILE_KEY_NAME,
 
901
                         "xdg-open");
 
902
  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
 
903
                         EGG_DESKTOP_FILE_KEY_TYPE,
 
904
                         "Application");
 
905
  g_key_file_set_string (key_file, EGG_DESKTOP_FILE_GROUP,
 
906
                         EGG_DESKTOP_FILE_KEY_EXEC,
 
907
                         "xdg-open %u");
 
908
  *app_desktop_file = egg_desktop_file_new_from_key_file (key_file, NULL, NULL);
 
909
  return TRUE;
 
910
}
 
911
 
 
912
#if GTK_CHECK_VERSION (2, 12, 0)
 
913
static char *
 
914
start_startup_notification (GdkDisplay     *display,
 
915
                            EggDesktopFile *desktop_file,
 
916
                            const char     *argv0,
 
917
                            int             screen,
 
918
                            int             workspace,
 
919
                            guint32         launch_time)
 
920
{
 
921
  static int sequence = 0;
 
922
  char *startup_id;
 
923
  char *description, *wmclass;
 
924
  char *screen_str, *workspace_str;
 
925
 
 
926
  if (g_key_file_has_key (desktop_file->key_file,
 
927
                          EGG_DESKTOP_FILE_GROUP,
 
928
                          EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
 
929
                          NULL))
 
930
    {
 
931
      if (!g_key_file_get_boolean (desktop_file->key_file,
 
932
                                   EGG_DESKTOP_FILE_GROUP,
 
933
                                   EGG_DESKTOP_FILE_KEY_STARTUP_NOTIFY,
 
934
                                   NULL))
 
935
        return NULL;
 
936
      wmclass = NULL;
 
937
    }
 
938
  else
 
939
    {
 
940
      wmclass = g_key_file_get_string (desktop_file->key_file,
 
941
                                       EGG_DESKTOP_FILE_GROUP,
 
942
                                       EGG_DESKTOP_FILE_KEY_STARTUP_WM_CLASS,
 
943
                                       NULL);
 
944
      if (!wmclass)
 
945
        return NULL;
 
946
    }
 
947
 
 
948
  if (launch_time == (guint32)-1)
 
949
    launch_time = gdk_x11_display_get_user_time (display);
 
950
  startup_id = g_strdup_printf ("%s-%lu-%s-%s-%d_TIME%lu",
 
951
                                g_get_prgname (),
 
952
                                (unsigned long)getpid (),
 
953
                                g_get_host_name (),
 
954
                                argv0,
 
955
                                sequence++,
 
956
                                (unsigned long)launch_time);
 
957
 
 
958
  description = g_strdup_printf (_("Starting %s"), desktop_file->name);
 
959
  screen_str = g_strdup_printf ("%d", screen);
 
960
  workspace_str = workspace == -1 ? NULL : g_strdup_printf ("%d", workspace);
 
961
 
 
962
  gdk_x11_display_broadcast_startup_message (display, "new",
 
963
                                             "ID", startup_id,
 
964
                                             "NAME", desktop_file->name,
 
965
                                             "SCREEN", screen_str,
 
966
                                             "BIN", argv0,
 
967
                                             "ICON", desktop_file->icon,
 
968
                                             "DESKTOP", workspace_str,
 
969
                                             "DESCRIPTION", description,
 
970
                                             "WMCLASS", wmclass,
 
971
                                             NULL);
 
972
 
 
973
  g_free (description);
 
974
  g_free (wmclass);
 
975
  g_free (screen_str);
 
976
  g_free (workspace_str);
 
977
 
 
978
  return startup_id;
 
979
}
 
980
 
 
981
static void
 
982
end_startup_notification (GdkDisplay *display,
 
983
                          const char *startup_id)
 
984
{
 
985
  gdk_x11_display_broadcast_startup_message (display, "remove",
 
986
                                             "ID", startup_id,
 
987
                                             NULL);
 
988
}
 
989
 
 
990
#define EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH (30 /* seconds */ * 1000)
 
991
 
 
992
typedef struct {
 
993
  GdkDisplay *display;
 
994
  char *startup_id;
 
995
} StartupNotificationData;
 
996
 
 
997
static gboolean
 
998
startup_notification_timeout (gpointer data)
 
999
{
 
1000
  StartupNotificationData *sn_data = data;
 
1001
 
 
1002
  end_startup_notification (sn_data->display, sn_data->startup_id);
 
1003
  g_object_unref (sn_data->display);
 
1004
  g_free (sn_data->startup_id);
 
1005
  g_free (sn_data);
 
1006
 
 
1007
  return FALSE;
 
1008
}
 
1009
 
 
1010
static void
 
1011
set_startup_notification_timeout (GdkDisplay *display,
 
1012
                                  const char *startup_id)
 
1013
{
 
1014
  StartupNotificationData *sn_data;
 
1015
 
 
1016
  sn_data = g_new (StartupNotificationData, 1);
 
1017
  sn_data->display = g_object_ref (display);
 
1018
  sn_data->startup_id = g_strdup (startup_id);
 
1019
 
 
1020
  g_timeout_add (EGG_DESKTOP_FILE_SN_TIMEOUT_LENGTH,
 
1021
                 startup_notification_timeout, sn_data);
 
1022
}
 
1023
#endif /* GTK 2.12 */
 
1024
 
 
1025
static GPtrArray *
 
1026
array_putenv (GPtrArray *env, char *variable)
 
1027
{
 
1028
  int i, keylen;
 
1029
 
 
1030
  if (!env)
 
1031
    {
 
1032
      char **envp;
 
1033
 
 
1034
      env = g_ptr_array_new ();
 
1035
 
 
1036
      envp = g_listenv ();
 
1037
      for (i = 0; envp[i]; i++)
 
1038
        {
 
1039
          const char *value;
 
1040
 
 
1041
          value = g_getenv (envp[i]);
 
1042
          g_ptr_array_add (env, g_strdup_printf ("%s=%s", envp[i],
 
1043
                                                 value ? value : ""));
 
1044
        }
 
1045
      g_strfreev (envp);
 
1046
    }
 
1047
 
 
1048
  keylen = strcspn (variable, "=");
 
1049
 
 
1050
  /* Remove old value of key */
 
1051
  for (i = 0; i < env->len; i++)
 
1052
    {
 
1053
      char *envvar = env->pdata[i];
 
1054
 
 
1055
      if (!strncmp (envvar, variable, keylen) && envvar[keylen] == '=')
 
1056
        {
 
1057
          g_free (envvar);
 
1058
          g_ptr_array_remove_index_fast (env, i);
 
1059
          break;
 
1060
        }
 
1061
    }
 
1062
 
 
1063
  /* Add new value */
 
1064
  g_ptr_array_add (env, g_strdup (variable));
 
1065
 
 
1066
  return env;
 
1067
}
 
1068
 
 
1069
static gboolean
 
1070
egg_desktop_file_launchv (EggDesktopFile *desktop_file,
 
1071
                          GSList *documents, va_list args,
 
1072
                          GError **error)
 
1073
{
 
1074
  EggDesktopFileLaunchOption option;
 
1075
  GSList *translated_documents, *docs;
 
1076
  char *command, **argv;
 
1077
  int argc, i, screen_num;
 
1078
  gboolean success, current_success;
 
1079
  GdkDisplay *display;
 
1080
  char *startup_id;
 
1081
 
 
1082
  GPtrArray   *env = NULL;
 
1083
  char       **variables = NULL;
 
1084
  GdkScreen   *screen = NULL;
 
1085
  int          workspace = -1;
 
1086
  const char  *directory = NULL;
 
1087
  guint32      launch_time = (guint32)-1;
 
1088
  GSpawnFlags  flags = G_SPAWN_SEARCH_PATH;
 
1089
  GSpawnChildSetupFunc setup_func = NULL;
 
1090
  gpointer     setup_data = NULL;
 
1091
 
 
1092
  GPid        *ret_pid = NULL;
 
1093
  int         *ret_stdin = NULL, *ret_stdout = NULL, *ret_stderr = NULL;
 
1094
  char       **ret_startup_id = NULL;
 
1095
 
 
1096
  if (documents && desktop_file->document_code == 0)
 
1097
    {
 
1098
      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
 
1099
                   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
 
1100
                   _("Application does not accept documents on command line"));
 
1101
      return FALSE;
 
1102
    }
 
1103
 
 
1104
  /* Read the options: technically it's incorrect for the caller to
 
1105
   * NULL-terminate the list of options (rather than 0-terminating
 
1106
   * it), but NULL-terminating lets us use G_GNUC_NULL_TERMINATED,
 
1107
   * it's more consistent with other glib/gtk methods, and it will
 
1108
   * work as long as sizeof (int) <= sizeof (NULL), and NULL is
 
1109
   * represented as 0. (Which is true everywhere we care about.)
 
1110
   */
 
1111
  while ((option = va_arg (args, EggDesktopFileLaunchOption)))
 
1112
    {
 
1113
      switch (option)
 
1114
        {
 
1115
        case EGG_DESKTOP_FILE_LAUNCH_CLEARENV:
 
1116
          if (env)
 
1117
            g_ptr_array_free (env, TRUE);
 
1118
          env = g_ptr_array_new ();
 
1119
          break;
 
1120
        case EGG_DESKTOP_FILE_LAUNCH_PUTENV:
 
1121
          variables = va_arg (args, char **);
 
1122
          for (i = 0; variables[i]; i++)
 
1123
            env = array_putenv (env, variables[i]);
 
1124
          break;
 
1125
 
 
1126
        case EGG_DESKTOP_FILE_LAUNCH_SCREEN:
 
1127
          screen = va_arg (args, GdkScreen *);
 
1128
          break;
 
1129
        case EGG_DESKTOP_FILE_LAUNCH_WORKSPACE:
 
1130
          workspace = va_arg (args, int);
 
1131
          break;
 
1132
 
 
1133
        case EGG_DESKTOP_FILE_LAUNCH_DIRECTORY:
 
1134
          directory = va_arg (args, const char *);
 
1135
          break;
 
1136
        case EGG_DESKTOP_FILE_LAUNCH_TIME:
 
1137
          launch_time = va_arg (args, guint32);
 
1138
          break;
 
1139
        case EGG_DESKTOP_FILE_LAUNCH_FLAGS:
 
1140
          flags |= va_arg (args, GSpawnFlags);
 
1141
          /* Make sure they didn't set any flags that don't make sense. */
 
1142
          flags &= ~G_SPAWN_FILE_AND_ARGV_ZERO;
 
1143
          break;
 
1144
        case EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC:
 
1145
          setup_func = va_arg (args, GSpawnChildSetupFunc);
 
1146
          setup_data = va_arg (args, gpointer);
 
1147
          break;
 
1148
 
 
1149
        case EGG_DESKTOP_FILE_LAUNCH_RETURN_PID:
 
1150
          ret_pid = va_arg (args, GPid *);
 
1151
          break;
 
1152
        case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE:
 
1153
          ret_stdin = va_arg (args, int *);
 
1154
          break;
 
1155
        case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE:
 
1156
          ret_stdout = va_arg (args, int *);
 
1157
          break;
 
1158
        case EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE:
 
1159
          ret_stderr = va_arg (args, int *);
 
1160
          break;
 
1161
        case EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID:
 
1162
          ret_startup_id = va_arg (args, char **);
 
1163
          break;
 
1164
 
 
1165
        default:
 
1166
          g_set_error (error, EGG_DESKTOP_FILE_ERROR,
 
1167
                       EGG_DESKTOP_FILE_ERROR_UNRECOGNIZED_OPTION,
 
1168
                       _("Unrecognized launch option: %d"),
 
1169
                       GPOINTER_TO_INT (option));
 
1170
          success = FALSE;
 
1171
          goto out;
 
1172
        }
 
1173
    }
 
1174
 
 
1175
  if (screen)
 
1176
    {
 
1177
      char *display_name = gdk_screen_make_display_name (screen);
 
1178
      char *display_env = g_strdup_printf ("DISPLAY=%s", display_name);
 
1179
      env = array_putenv (env, display_env);
 
1180
      g_free (display_name);
 
1181
      g_free (display_env);
 
1182
 
 
1183
      display = gdk_screen_get_display (screen);
 
1184
    }
 
1185
  else
 
1186
    {
 
1187
      display = gdk_display_get_default ();
 
1188
      screen = gdk_display_get_default_screen (display);
 
1189
    }
 
1190
  screen_num = gdk_screen_get_number (screen);
 
1191
 
 
1192
  translated_documents = translate_document_list (desktop_file, documents);
 
1193
  docs = translated_documents;
 
1194
 
 
1195
  success = FALSE;
 
1196
 
 
1197
  do
 
1198
    {
 
1199
      command = parse_exec (desktop_file, &docs, error);
 
1200
      if (!command)
 
1201
        goto out;
 
1202
 
 
1203
      if (!g_shell_parse_argv (command, &argc, &argv, error))
 
1204
        {
 
1205
          g_free (command);
 
1206
          goto out;
 
1207
        }
 
1208
      g_free (command);
 
1209
 
 
1210
#if GTK_CHECK_VERSION (2, 12, 0)
 
1211
      startup_id = start_startup_notification (display, desktop_file,
 
1212
                                               argv[0], screen_num,
 
1213
                                               workspace, launch_time);
 
1214
      if (startup_id)
 
1215
        {
 
1216
          char *startup_id_env = g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
 
1217
                                                  startup_id);
 
1218
          env = array_putenv (env, startup_id_env);
 
1219
          g_free (startup_id_env);
 
1220
        }
 
1221
#else
 
1222
      startup_id = NULL;
 
1223
#endif /* GTK 2.12 */
 
1224
 
 
1225
      if (env != NULL)
 
1226
        g_ptr_array_add (env, NULL);
 
1227
 
 
1228
      current_success =
 
1229
        g_spawn_async_with_pipes (directory,
 
1230
                                  argv,
 
1231
                                  env ? (char **)(env->pdata) : NULL,
 
1232
                                  flags,
 
1233
                                  setup_func, setup_data,
 
1234
                                  ret_pid,
 
1235
                                  ret_stdin, ret_stdout, ret_stderr,
 
1236
                                  error);
 
1237
      g_strfreev (argv);
 
1238
 
 
1239
      if (startup_id)
 
1240
        {
 
1241
#if GTK_CHECK_VERSION (2, 12, 0)
 
1242
          if (current_success)
 
1243
            {
 
1244
              set_startup_notification_timeout (display, startup_id);
 
1245
 
 
1246
              if (ret_startup_id)
 
1247
                *ret_startup_id = startup_id;
 
1248
              else
 
1249
                g_free (startup_id);
 
1250
            }
 
1251
          else
 
1252
#endif /* GTK 2.12 */
 
1253
            g_free (startup_id);
 
1254
        }
 
1255
      else if (ret_startup_id)
 
1256
        *ret_startup_id = NULL;
 
1257
 
 
1258
      if (current_success)
 
1259
        {
 
1260
          /* If we successfully launch any instances of the app, make
 
1261
           * sure we return TRUE and don't set @error.
 
1262
           */
 
1263
          success = TRUE;
 
1264
          error = NULL;
 
1265
 
 
1266
          /* Also, only set the output params on the first one */
 
1267
          ret_pid = NULL;
 
1268
          ret_stdin = ret_stdout = ret_stderr = NULL;
 
1269
          ret_startup_id = NULL;
 
1270
        }
 
1271
    }
 
1272
  while (docs && current_success);
 
1273
 
 
1274
 out:
 
1275
  if (env)
 
1276
    {
 
1277
      g_strfreev ((char **)env->pdata);
 
1278
      g_ptr_array_free (env, FALSE);
 
1279
    }
 
1280
  free_document_list (translated_documents);
 
1281
 
 
1282
  return success;
 
1283
}
 
1284
 
 
1285
/**
 
1286
 * egg_desktop_file_launch:
 
1287
 * @desktop_file: an #EggDesktopFile
 
1288
 * @documents: a list of URIs or paths to documents to open
 
1289
 * @error: error pointer
 
1290
 * @...: additional options
 
1291
 *
 
1292
 * Launches @desktop_file with the given arguments. Additional options
 
1293
 * can be specified as follows:
 
1294
 *
 
1295
 *   %EGG_DESKTOP_FILE_LAUNCH_CLEARENV: (no arguments)
 
1296
 *       clears the environment in the child process
 
1297
 *   %EGG_DESKTOP_FILE_LAUNCH_PUTENV: (char **variables)
 
1298
 *       adds the NAME=VALUE strings in the given %NULL-terminated
 
1299
 *       array to the child process's environment
 
1300
 *   %EGG_DESKTOP_FILE_LAUNCH_SCREEN: (GdkScreen *screen)
 
1301
 *       causes the application to be launched on the given screen
 
1302
 *   %EGG_DESKTOP_FILE_LAUNCH_WORKSPACE: (int workspace)
 
1303
 *       causes the application to be launched on the given workspace
 
1304
 *   %EGG_DESKTOP_FILE_LAUNCH_DIRECTORY: (char *dir)
 
1305
 *       causes the application to be launched in the given directory
 
1306
 *   %EGG_DESKTOP_FILE_LAUNCH_TIME: (guint32 launch_time)
 
1307
 *       sets the "launch time" for the application. If the user
 
1308
 *       interacts with another window after @launch_time but before
 
1309
 *       the launched application creates its first window, the window
 
1310
 *       manager may choose to not give focus to the new application.
 
1311
 *       Passing 0 for @launch_time will explicitly request that the
 
1312
 *       application not receive focus.
 
1313
 *   %EGG_DESKTOP_FILE_LAUNCH_FLAGS (GSpawnFlags flags)
 
1314
 *       Sets additional #GSpawnFlags to use. See g_spawn_async() for
 
1315
 *       more details.
 
1316
 *   %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC (GSpawnChildSetupFunc, gpointer)
 
1317
 *       Sets the child setup callback and the data to pass to it.
 
1318
 *       (See g_spawn_async() for more details.)
 
1319
 *
 
1320
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID (GPid **pid)
 
1321
 *       On a successful launch, sets *@pid to the PID of the launched
 
1322
 *       application.
 
1323
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STARTUP_ID (char **startup_id)
 
1324
 *       On a successful launch, sets *@startup_id to the Startup
 
1325
 *       Notification "startup id" of the launched application.
 
1326
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDIN_PIPE (int *fd)
 
1327
 *       On a successful launch, sets *@fd to the file descriptor of
 
1328
 *       a pipe connected to the application's stdin.
 
1329
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDOUT_PIPE (int *fd)
 
1330
 *       On a successful launch, sets *@fd to the file descriptor of
 
1331
 *       a pipe connected to the application's stdout.
 
1332
 *   %EGG_DESKTOP_FILE_LAUNCH_RETURN_STDERR_PIPE (int *fd)
 
1333
 *       On a successful launch, sets *@fd to the file descriptor of
 
1334
 *       a pipe connected to the application's stderr.
 
1335
 *
 
1336
 * The options should be terminated with a single %NULL.
 
1337
 *
 
1338
 * If @documents contains multiple documents, but
 
1339
 * egg_desktop_file_accepts_multiple() returns %FALSE for
 
1340
 * @desktop_file, then egg_desktop_file_launch() will actually launch
 
1341
 * multiple instances of the application. In that case, the return
 
1342
 * value (as well as any values passed via
 
1343
 * %EGG_DESKTOP_FILE_LAUNCH_RETURN_PID, etc) will only reflect the
 
1344
 * first instance of the application that was launched (but the
 
1345
 * %EGG_DESKTOP_FILE_LAUNCH_SETUP_FUNC will be called for each
 
1346
 * instance).
 
1347
 *
 
1348
 * Return value: %TRUE if the application was successfully launched.
 
1349
 **/
 
1350
gboolean
 
1351
egg_desktop_file_launch (EggDesktopFile *desktop_file,
 
1352
                         GSList *documents, GError **error,
 
1353
                         ...)
 
1354
{
 
1355
  va_list args;
 
1356
  gboolean success;
 
1357
  EggDesktopFile *app_desktop_file;
 
1358
 
 
1359
  switch (desktop_file->type)
 
1360
    {
 
1361
    case EGG_DESKTOP_FILE_TYPE_APPLICATION:
 
1362
      va_start (args, error);
 
1363
      success = egg_desktop_file_launchv (desktop_file, documents,
 
1364
                                          args, error);
 
1365
      va_end (args);
 
1366
      break;
 
1367
 
 
1368
    case EGG_DESKTOP_FILE_TYPE_LINK:
 
1369
      if (documents)
 
1370
        {
 
1371
          g_set_error (error, EGG_DESKTOP_FILE_ERROR,
 
1372
                       EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
 
1373
                       _("Can't pass document URIs to a 'Type=Link' desktop entry"));
 
1374
          return FALSE;
 
1375
        }         
 
1376
 
 
1377
      if (!parse_link (desktop_file, &app_desktop_file, &documents, error))
 
1378
        return FALSE;
 
1379
 
 
1380
      va_start (args, error);
 
1381
      success = egg_desktop_file_launchv (app_desktop_file, documents,
 
1382
                                          args, error);
 
1383
      va_end (args);
 
1384
 
 
1385
      egg_desktop_file_free (app_desktop_file);
 
1386
      free_document_list (documents);
 
1387
      break;
 
1388
 
 
1389
    default:
 
1390
      g_set_error (error, EGG_DESKTOP_FILE_ERROR,
 
1391
                   EGG_DESKTOP_FILE_ERROR_NOT_LAUNCHABLE,
 
1392
                   _("Not a launchable item"));
 
1393
      success = FALSE;
 
1394
      break;
 
1395
    }
 
1396
 
 
1397
  return success;
 
1398
}
 
1399
 
 
1400
 
 
1401
GQuark
 
1402
egg_desktop_file_error_quark (void)
 
1403
{
 
1404
  return g_quark_from_static_string ("egg-desktop_file-error-quark");
 
1405
}
 
1406
 
 
1407
 
 
1408
G_LOCK_DEFINE_STATIC (egg_desktop_file);
 
1409
static EggDesktopFile *egg_desktop_file;
 
1410
 
 
1411
/**
 
1412
 * egg_set_desktop_file:
 
1413
 * @desktop_file_path: path to the application's desktop file
 
1414
 *
 
1415
 * Creates an #EggDesktopFile for the application from the data at
 
1416
 * @desktop_file_path. This will also call g_set_application_name()
 
1417
 * with the localized application name from the desktop file, and
 
1418
 * gtk_window_set_default_icon_name() or
 
1419
 * gtk_window_set_default_icon_from_file() with the application's
 
1420
 * icon. Other code may use additional information from the desktop
 
1421
 * file.
 
1422
 *
 
1423
 * Note that for thread safety reasons, this function can only
 
1424
 * be called once.
 
1425
 **/
 
1426
void
 
1427
egg_set_desktop_file (const char *desktop_file_path)
 
1428
{
 
1429
  GError *error = NULL;
 
1430
 
 
1431
  G_LOCK (egg_desktop_file);
 
1432
  if (egg_desktop_file)
 
1433
    egg_desktop_file_free (egg_desktop_file);
 
1434
 
 
1435
  egg_desktop_file = egg_desktop_file_new (desktop_file_path, &error);
 
1436
  if (error)
 
1437
    {
 
1438
      g_warning ("Could not load desktop file '%s': %s",
 
1439
                 desktop_file_path, error->message);
 
1440
      g_error_free (error);
 
1441
    }
 
1442
 
 
1443
  /* Set localized application name and default window icon */
 
1444
  if (egg_desktop_file->name)
 
1445
    g_set_application_name (egg_desktop_file->name);
 
1446
  if (egg_desktop_file->icon)
 
1447
    {
 
1448
      if (g_path_is_absolute (egg_desktop_file->icon))
 
1449
        gtk_window_set_default_icon_from_file (egg_desktop_file->icon, NULL);
 
1450
      else
 
1451
        gtk_window_set_default_icon_name (egg_desktop_file->icon);
 
1452
    }
 
1453
 
 
1454
  G_UNLOCK (egg_desktop_file);
 
1455
}
 
1456
 
 
1457
/**
 
1458
 * egg_get_desktop_file:
 
1459
 * 
 
1460
 * Gets the application's #EggDesktopFile, as set by
 
1461
 * egg_set_desktop_file().
 
1462
 * 
 
1463
 * Return value: the #EggDesktopFile, or %NULL if it hasn't been set.
 
1464
 **/
 
1465
EggDesktopFile *
 
1466
egg_get_desktop_file (void)
 
1467
{
 
1468
  EggDesktopFile *retval;
 
1469
 
 
1470
  G_LOCK (egg_desktop_file);
 
1471
  retval = egg_desktop_file;
 
1472
  G_UNLOCK (egg_desktop_file);
 
1473
 
 
1474
  return retval;
 
1475
}