1 /* vi:set et ai sw=2 sts=2 ts=2: */
2 /*-
3 * Copyright (c) 2006-2010 Jannis Pohlmann <jannis@xfce.org>
4 * Copyright (c) 2009-2010 Nick Schermer <nick@xfce.org>
5 * Copyright (c) 2015 Danila Poyarkov <dannotemail@gmail.com>
6 * Copyright (c) 2017 Gregor Santner <gsantner@mailbox.org>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General
19 * Public License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <gio/gio.h>
29 #include <libxfce4util/libxfce4util.h>
30
31 #include <garcon/garcon-environment.h>
32 #include <garcon/garcon-menu-element.h>
33 #include <garcon/garcon-menu-item.h>
34 #include <garcon/garcon-menu-item-action.h>
35 #include <garcon/garcon-private.h>
36
37
38
39 /* Property identifiers */
40 enum
41 {
42 PROP_0,
43 PROP_FILE,
44 PROP_DESKTOP_ID,
45 PROP_REQUIRES_TERMINAL,
46 PROP_NO_DISPLAY,
47 PROP_STARTUP_NOTIFICATION,
48 PROP_NAME,
49 PROP_GENERIC_NAME,
50 PROP_COMMENT,
51 PROP_ICON_NAME,
52 PROP_COMMAND,
53 PROP_TRY_EXEC,
54 PROP_HIDDEN,
55 PROP_PATH,
56 };
57
58 /* Signal identifiers */
59 enum
60 {
61 CHANGED,
62 LAST_SIGNAL,
63 };
64
65
66
67 static void garcon_menu_item_element_init (GarconMenuElementIface *iface);
68 static void garcon_menu_item_finalize (GObject *object);
69 static void garcon_menu_item_get_property (GObject *object,
70 guint prop_id,
71 GValue *value,
72 GParamSpec *pspec);
73 static void garcon_menu_item_set_property (GObject *object,
74 guint prop_id,
75 const GValue *value,
76 GParamSpec *pspec);
77 static const gchar *garcon_menu_item_get_element_name (GarconMenuElement *element);
78 static const gchar *garcon_menu_item_get_element_comment (GarconMenuElement *element);
79 static const gchar *garcon_menu_item_get_element_icon_name (GarconMenuElement *element);
80 static gboolean garcon_menu_item_get_element_visible (GarconMenuElement *element);
81 static gboolean garcon_menu_item_get_element_show_in_environment (GarconMenuElement *element);
82 static gboolean garcon_menu_item_get_element_no_display (GarconMenuElement *element);
83 static gboolean garcon_menu_item_get_element_equal (GarconMenuElement *element,
84 GarconMenuElement *other);
85 static gboolean garcon_menu_item_lists_equal (GList *list1,
86 GList *list2);
87
88
89
90 static guint item_signals[LAST_SIGNAL];
91
92
93
94 struct _GarconMenuItemPrivate
95 {
96 /* Source file of the menu item */
97 GFile *file;
98
99 /* Desktop file id */
100 gchar *desktop_id;
101
102 /* List of categories */
103 GList *categories;
104
105 /* List of keywords */
106 GList *keywords;
107
108 /* Whether this application requires a terminal to be started in */
109 guint requires_terminal : 1;
110
111 /* Whether this menu item should be hidden */
112 guint no_display : 1;
113
114 /* Whether this application supports startup notification */
115 guint supports_startup_notification : 1;
116
117 /* Name to be displayed for the menu item */
118 gchar *name;
119
120 /* Generic name of the menu item */
121 gchar *generic_name;
122
123 /* Comment/description of the item */
124 gchar *comment;
125
126 /* Command to be executed when the menu item is clicked */
127 gchar *command;
128
129 /* TryExec value */
130 gchar *try_exec;
131
132 /* Menu item icon name */
133 gchar *icon_name;
134
135 /* Environments in which the menu item should be displayed only */
136 gchar **only_show_in;
137
138 /* Environments in which the menu item should be hidden */
139 gchar **not_show_in;
140
141 /* Working directory */
142 gchar *path;
143
144 /* List of application actions of type GarconMenuItemAction */
145 GList *actions;
146
147 /* Hidden value */
148 guint hidden : 1;
149
150 /* Counter keeping the number of menus which use this item. This works
151 * like a reference counter and should be increased / decreased by GarconMenu
152 * items whenever the item is added to or removed from the menu. */
153 guint num_allocated;
154 };
155
156
157
G_DEFINE_TYPE_WITH_CODE(GarconMenuItem,garcon_menu_item,G_TYPE_OBJECT,G_ADD_PRIVATE (GarconMenuItem)G_IMPLEMENT_INTERFACE (GARCON_TYPE_MENU_ELEMENT,garcon_menu_item_element_init))158 G_DEFINE_TYPE_WITH_CODE (GarconMenuItem, garcon_menu_item, G_TYPE_OBJECT,
159 G_ADD_PRIVATE (GarconMenuItem)
160 G_IMPLEMENT_INTERFACE (GARCON_TYPE_MENU_ELEMENT,
161 garcon_menu_item_element_init))
162
163
164
165 static void
166 garcon_menu_item_class_init (GarconMenuItemClass *klass)
167 {
168 GObjectClass *gobject_class;
169
170 gobject_class = G_OBJECT_CLASS (klass);
171 gobject_class->finalize = garcon_menu_item_finalize;
172 gobject_class->get_property = garcon_menu_item_get_property;
173 gobject_class->set_property = garcon_menu_item_set_property;
174
175 /**
176 * GarconMenuItem:file:
177 *
178 * The #GFile from which the %GarconMenuItem was loaded.
179 **/
180 g_object_class_install_property (gobject_class,
181 PROP_FILE,
182 g_param_spec_object ("file",
183 "file",
184 "file",
185 G_TYPE_FILE,
186 G_PARAM_READWRITE |
187 G_PARAM_STATIC_STRINGS |
188 G_PARAM_CONSTRUCT_ONLY));
189
190 /**
191 * GarconMenuItem:desktop-id:
192 *
193 * The desktop-file id of this application.
194 **/
195 g_object_class_install_property (gobject_class,
196 PROP_DESKTOP_ID,
197 g_param_spec_string ("desktop-id",
198 "Desktop-File Id",
199 "Desktop-File Id of the application",
200 NULL,
201 G_PARAM_READWRITE |
202 G_PARAM_STATIC_STRINGS));
203
204 /**
205 * GarconMenuItem:requires-terminal:
206 *
207 * Whether this application requires a terinal to be started in.
208 **/
209 g_object_class_install_property (gobject_class,
210 PROP_REQUIRES_TERMINAL,
211 g_param_spec_boolean ("requires-terminal",
212 "Requires a terminal",
213 "Whether this application requires a terminal",
214 FALSE,
215 G_PARAM_READWRITE |
216 G_PARAM_STATIC_STRINGS));
217
218 /**
219 * GarconMenuItem:no-display:
220 *
221 * Whether this menu item is hidden in menus.
222 **/
223 g_object_class_install_property (gobject_class,
224 PROP_NO_DISPLAY,
225 g_param_spec_boolean ("no-display",
226 "No Display",
227 "Visibility state of the menu item",
228 FALSE,
229 G_PARAM_READWRITE |
230 G_PARAM_STATIC_STRINGS));
231
232 /**
233 * GarconMenuItem:startup-notification:
234 *
235 * Whether this application supports startup notification.
236 **/
237 g_object_class_install_property (gobject_class,
238 PROP_STARTUP_NOTIFICATION,
239 g_param_spec_boolean ("supports-startup-notification",
240 "Startup notification",
241 "Startup notification support",
242 FALSE,
243 G_PARAM_READWRITE |
244 G_PARAM_STATIC_STRINGS));
245
246 /**
247 * GarconMenuItem:name:
248 *
249 * Name of the application (will be displayed in menus etc.).
250 **/
251 g_object_class_install_property (gobject_class,
252 PROP_NAME,
253 g_param_spec_string ("name",
254 "Name",
255 "Name of the application",
256 NULL,
257 G_PARAM_READWRITE |
258 G_PARAM_STATIC_STRINGS));
259
260 /**
261 * GarconMenuItem:generic-name:
262 *
263 * GenericName of the application (will be displayed in menus etc.).
264 **/
265 g_object_class_install_property (gobject_class,
266 PROP_GENERIC_NAME,
267 g_param_spec_string ("generic-name",
268 "Generic name",
269 "Generic name of the application",
270 NULL,
271 G_PARAM_READWRITE |
272 G_PARAM_STATIC_STRINGS));
273
274 /**
275 * GarconMenuItem:comment:
276 *
277 * Comment/description for the application. To be displayed e.g. in tooltips of
278 * GtkMenuItems.
279 **/
280 g_object_class_install_property (gobject_class,
281 PROP_COMMENT,
282 g_param_spec_string ("comment",
283 "Comment",
284 "Comment/description for the application",
285 NULL,
286 G_PARAM_READWRITE |
287 G_PARAM_STATIC_STRINGS));
288
289 /**
290 * GarconMenuItem:command:
291 *
292 * Command to be executed when the menu item is clicked.
293 **/
294 g_object_class_install_property (gobject_class,
295 PROP_COMMAND,
296 g_param_spec_string ("command",
297 "Command",
298 "Application command",
299 NULL,
300 G_PARAM_READWRITE |
301 G_PARAM_STATIC_STRINGS));
302
303 /**
304 * GarconMenuItem:try-exec:
305 *
306 * Path to an executable file on disk used to determine if the program
307 * is actually installed. If the path is not an absolute path, the file
308 * is looked up in the $PATH environment variable. If the file is not
309 * present or if it is not executable, the entry may be ignored (not be
310 * used in menus, for example).
311 **/
312 g_object_class_install_property (gobject_class,
313 PROP_TRY_EXEC,
314 g_param_spec_string ("try-exec",
315 "TryExec",
316 "Command to check if application is installed",
317 NULL,
318 G_PARAM_READWRITE |
319 G_PARAM_STATIC_STRINGS));
320
321 /**
322 * GarconMenuItem:icon-name:
323 *
324 * Name of the icon to be displayed for this menu item.
325 **/
326 g_object_class_install_property (gobject_class,
327 PROP_ICON_NAME,
328 g_param_spec_string ("icon-name",
329 "Icon name",
330 "Name of the application icon",
331 NULL,
332 G_PARAM_READWRITE |
333 G_PARAM_STATIC_STRINGS));
334
335 /**
336 * GarconMenuItem:hidden:
337 *
338 * It means the user deleted (at his level) something that was present
339 * (at an upper level, e.g. in the system dirs). It's strictly equivalent
340 * to the .desktop file not existing at all, as far as that user is concerned.
341 **/
342 g_object_class_install_property (gobject_class,
343 PROP_HIDDEN,
344 g_param_spec_boolean ("hidden",
345 "Hidden",
346 "Whether the application has been deleted",
347 FALSE,
348 G_PARAM_READWRITE |
349 G_PARAM_STATIC_STRINGS));
350
351 /**
352 * GarconMenuItem:path:
353 *
354 * Working directory the application should be started in.
355 **/
356 g_object_class_install_property (gobject_class,
357 PROP_PATH,
358 g_param_spec_string ("path",
359 "Path",
360 "Working directory path",
361 NULL,
362 G_PARAM_READWRITE |
363 G_PARAM_STATIC_STRINGS));
364
365 /**
366 * GarconMenuItem::changed:
367 * @item : a #GarconMenuItem.
368 *
369 * Emitted when #GarconMenuItem has been reloaded.
370 **/
371 item_signals[CHANGED] =
372 g_signal_new (g_intern_static_string ("changed"),
373 G_TYPE_FROM_CLASS (klass),
374 G_SIGNAL_RUN_FIRST,
375 G_STRUCT_OFFSET (GarconMenuItemClass, changed),
376 NULL, NULL,
377 g_cclosure_marshal_VOID__VOID,
378 G_TYPE_NONE, 0);
379 }
380
381
382
383 static void
garcon_menu_item_element_init(GarconMenuElementIface * iface)384 garcon_menu_item_element_init (GarconMenuElementIface *iface)
385 {
386 iface->get_name = garcon_menu_item_get_element_name;
387 iface->get_comment = garcon_menu_item_get_element_comment;
388 iface->get_icon_name = garcon_menu_item_get_element_icon_name;
389 iface->get_visible = garcon_menu_item_get_element_visible;
390 iface->get_show_in_environment = garcon_menu_item_get_element_show_in_environment;
391 iface->get_no_display = garcon_menu_item_get_element_no_display;
392 iface->equal = garcon_menu_item_get_element_equal;
393 }
394
395
396
397 static void
garcon_menu_item_init(GarconMenuItem * item)398 garcon_menu_item_init (GarconMenuItem *item)
399 {
400 item->priv = garcon_menu_item_get_instance_private (item);
401 }
402
403
404
405 static void
garcon_menu_item_finalize(GObject * object)406 garcon_menu_item_finalize (GObject *object)
407 {
408 GarconMenuItem *item = GARCON_MENU_ITEM (object);
409
410 g_free (item->priv->desktop_id);
411 g_free (item->priv->name);
412 g_free (item->priv->generic_name);
413 g_free (item->priv->comment);
414 g_free (item->priv->command);
415 g_free (item->priv->try_exec);
416 g_free (item->priv->icon_name);
417 g_free (item->priv->path);
418
419 g_strfreev (item->priv->only_show_in);
420 g_strfreev (item->priv->not_show_in);
421
422 _garcon_g_list_free_full (item->priv->categories, g_free);
423 _garcon_g_list_free_full (item->priv->keywords, g_free);
424 _garcon_g_list_free_full (item->priv->actions, garcon_menu_item_action_unref);
425
426 if (item->priv->file != NULL)
427 g_object_unref (G_OBJECT (item->priv->file));
428
429 (*G_OBJECT_CLASS (garcon_menu_item_parent_class)->finalize) (object);
430 }
431
432
433
434 static void
garcon_menu_item_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)435 garcon_menu_item_get_property (GObject *object,
436 guint prop_id,
437 GValue *value,
438 GParamSpec *pspec)
439 {
440 GarconMenuItem *item = GARCON_MENU_ITEM (object);
441
442 switch (prop_id)
443 {
444 case PROP_FILE:
445 g_value_set_object (value, item->priv->file);
446 break;
447
448 case PROP_DESKTOP_ID:
449 g_value_set_string (value, garcon_menu_item_get_desktop_id (item));
450 break;
451
452 case PROP_COMMENT:
453 g_value_set_string (value, garcon_menu_item_get_comment (item));
454 break;
455
456 case PROP_REQUIRES_TERMINAL:
457 g_value_set_boolean (value, garcon_menu_item_requires_terminal (item));
458 break;
459
460 case PROP_NO_DISPLAY:
461 g_value_set_boolean (value, garcon_menu_item_get_no_display (item));
462 break;
463
464 case PROP_STARTUP_NOTIFICATION:
465 g_value_set_boolean (value, garcon_menu_item_supports_startup_notification (item));
466 break;
467
468 case PROP_NAME:
469 g_value_set_string (value, garcon_menu_item_get_name (item));
470 break;
471
472 case PROP_GENERIC_NAME:
473 g_value_set_string (value, garcon_menu_item_get_generic_name (item));
474 break;
475
476 case PROP_COMMAND:
477 g_value_set_string (value, garcon_menu_item_get_command (item));
478 break;
479
480 case PROP_ICON_NAME:
481 g_value_set_string (value, garcon_menu_item_get_icon_name (item));
482 break;
483
484 case PROP_TRY_EXEC:
485 g_value_set_string (value, garcon_menu_item_get_try_exec (item));
486 break;
487
488 case PROP_HIDDEN:
489 g_value_set_boolean (value, garcon_menu_item_get_hidden (item));
490 break;
491
492 case PROP_PATH:
493 g_value_set_string (value, garcon_menu_item_get_path (item));
494 break;
495
496 default:
497 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
498 break;
499 }
500 }
501
502
503
504 static void
garcon_menu_item_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)505 garcon_menu_item_set_property (GObject *object,
506 guint prop_id,
507 const GValue *value,
508 GParamSpec *pspec)
509 {
510 GarconMenuItem *item = GARCON_MENU_ITEM (object);
511
512 switch (prop_id)
513 {
514 case PROP_FILE:
515 item->priv->file = g_value_dup_object (value);
516 break;
517
518 case PROP_DESKTOP_ID:
519 garcon_menu_item_set_desktop_id (item, g_value_get_string (value));
520 break;
521
522 case PROP_REQUIRES_TERMINAL:
523 garcon_menu_item_set_requires_terminal (item, g_value_get_boolean (value));
524 break;
525
526 case PROP_NO_DISPLAY:
527 garcon_menu_item_set_no_display (item, g_value_get_boolean (value));
528 break;
529
530 case PROP_STARTUP_NOTIFICATION:
531 garcon_menu_item_set_supports_startup_notification (item, g_value_get_boolean (value));
532 break;
533
534 case PROP_NAME:
535 garcon_menu_item_set_name (item, g_value_get_string (value));
536 break;
537
538 case PROP_GENERIC_NAME:
539 garcon_menu_item_set_generic_name (item, g_value_get_string (value));
540 break;
541
542 case PROP_COMMENT:
543 garcon_menu_item_set_comment (item, g_value_get_string (value));
544 break;
545
546 case PROP_COMMAND:
547 garcon_menu_item_set_command (item, g_value_get_string (value));
548 break;
549
550 case PROP_TRY_EXEC:
551 garcon_menu_item_set_try_exec (item, g_value_get_string (value));
552 break;
553
554 case PROP_ICON_NAME:
555 garcon_menu_item_set_icon_name (item, g_value_get_string (value));
556 break;
557
558 case PROP_HIDDEN:
559 garcon_menu_item_set_hidden (item, g_value_get_boolean (value));
560 break;
561
562 case PROP_PATH:
563 garcon_menu_item_set_path (item, g_value_get_string (value));
564 break;
565
566 default:
567 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
568 break;
569 }
570 }
571
572
573
574 static gboolean
garcon_menu_item_get_element_visible(GarconMenuElement * element)575 garcon_menu_item_get_element_visible (GarconMenuElement *element)
576 {
577 GarconMenuItem *item;
578 const gchar *try_exec;
579 gchar **mt;
580 gboolean result = TRUE;
581 gchar *command;
582
583 g_return_val_if_fail (GARCON_IS_MENU_ITEM (element), FALSE);
584
585 item = GARCON_MENU_ITEM (element);
586
587 if (garcon_menu_item_get_hidden (item)
588 || garcon_menu_item_get_no_display (item)
589 || !garcon_menu_item_get_show_in_environment (item))
590 return FALSE;
591
592 /* Check the TryExec field */
593 try_exec = garcon_menu_item_get_try_exec (item);
594 if (try_exec != NULL && g_shell_parse_argv (try_exec, NULL, &mt, NULL))
595 {
596 /* Check if we have an absolute path to an existing file */
597 result = g_file_test (mt[0], G_FILE_TEST_EXISTS);
598
599 /* Else, we may have a program in $PATH */
600 if (!result)
601 {
602 command = g_find_program_in_path (mt[0]);
603 result = (command != NULL);
604 g_free (command);
605 }
606
607 /* Cleanup */
608 g_strfreev (mt);
609 }
610
611 return result;
612 }
613
614
615
616 static gboolean
garcon_menu_item_get_element_show_in_environment(GarconMenuElement * element)617 garcon_menu_item_get_element_show_in_environment (GarconMenuElement *element)
618 {
619 g_return_val_if_fail (GARCON_IS_MENU_ITEM (element), FALSE);
620 return garcon_menu_item_get_show_in_environment (GARCON_MENU_ITEM (element));
621 }
622
623
624
625 static gboolean
garcon_menu_item_get_element_no_display(GarconMenuElement * element)626 garcon_menu_item_get_element_no_display (GarconMenuElement *element)
627 {
628 g_return_val_if_fail (GARCON_IS_MENU_ITEM (element), FALSE);
629 return garcon_menu_item_get_no_display (GARCON_MENU_ITEM (element));
630 }
631
632
633
634 static gboolean
garcon_menu_item_get_element_equal(GarconMenuElement * element,GarconMenuElement * other)635 garcon_menu_item_get_element_equal (GarconMenuElement *element,
636 GarconMenuElement *other)
637 {
638 g_return_val_if_fail (GARCON_IS_MENU_ITEM (element), FALSE);
639 g_return_val_if_fail (GARCON_IS_MENU_ITEM (other), FALSE);
640
641 return g_file_equal (GARCON_MENU_ITEM (element)->priv->file,
642 GARCON_MENU_ITEM (other)->priv->file);
643 }
644
645
646
647 static const gchar*
garcon_menu_item_get_element_name(GarconMenuElement * element)648 garcon_menu_item_get_element_name (GarconMenuElement *element)
649 {
650 g_return_val_if_fail (GARCON_IS_MENU_ITEM (element), NULL);
651 return GARCON_MENU_ITEM (element)->priv->name;
652 }
653
654
655
656 static const gchar*
garcon_menu_item_get_element_comment(GarconMenuElement * element)657 garcon_menu_item_get_element_comment (GarconMenuElement *element)
658 {
659 g_return_val_if_fail (GARCON_IS_MENU_ITEM (element), NULL);
660 return GARCON_MENU_ITEM (element)->priv->comment;
661 }
662
663
664
665 static const gchar*
garcon_menu_item_get_element_icon_name(GarconMenuElement * element)666 garcon_menu_item_get_element_icon_name (GarconMenuElement *element)
667 {
668 g_return_val_if_fail (GARCON_IS_MENU_ITEM (element), NULL);
669 return GARCON_MENU_ITEM (element)->priv->icon_name;
670 }
671
672
673
674 static gboolean
garcon_menu_item_lists_equal(GList * list1,GList * list2)675 garcon_menu_item_lists_equal (GList *list1,
676 GList *list2)
677 {
678 gboolean element_missing = FALSE;
679 GList *lp;
680
681 if (g_list_length (list1) != g_list_length (list2))
682 return FALSE;
683
684 for (lp = list1; !element_missing && lp != NULL; lp = lp->next)
685 {
686 if (g_list_find_custom (list2, lp->data, (GCompareFunc) g_strcmp0) == NULL)
687 element_missing = TRUE;
688 }
689
690 return !element_missing;
691 }
692
693
694
695 static gchar *
garcon_menu_item_url_exec(XfceRc * rc)696 garcon_menu_item_url_exec (XfceRc *rc)
697 {
698 const gchar *url;
699 gchar *url_exec = NULL;
700
701 /* Support Type=Link items */
702 url = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_URL, NULL);
703 if (url != NULL)
704 url_exec = g_strdup_printf ("exo-open '%s'", url);
705
706 return url_exec;
707 }
708
709
710 /**
711 * garcon_menu_item_new: (constructor)
712 * @file: a #GFile
713 *
714 * Returns (transfer full): a new #GarconMenuItem
715 */
716 GarconMenuItem *
garcon_menu_item_new(GFile * file)717 garcon_menu_item_new (GFile *file)
718 {
719 GarconMenuItem *item = NULL;
720 GarconMenuItemAction *action = NULL;
721 XfceRc *rc;
722 GList *categories = NULL;
723 GList *keywords = NULL;
724 gchar *filename;
725 gboolean terminal;
726 gboolean no_display;
727 gboolean startup_notify;
728 gboolean hidden;
729 const gchar *path;
730 const gchar *name;
731 const gchar *generic_name;
732 const gchar *comment;
733 const gchar *exec;
734 const gchar *try_exec;
735 const gchar *icon;
736 gchar *action_group;
737 gchar **mt;
738 gchar **str_list;
739 gchar *url_exec = NULL;
740
741 g_return_val_if_fail (G_IS_FILE (file), NULL);
742 g_return_val_if_fail (g_file_is_native (file), NULL);
743
744 /* Open the rc file */
745 filename = g_file_get_path (file);
746 rc = xfce_rc_simple_open (filename, TRUE);
747 g_free (filename);
748 if (G_UNLIKELY (rc == NULL))
749 return NULL;
750
751 xfce_rc_set_group (rc, G_KEY_FILE_DESKTOP_GROUP);
752
753 /* Parse name and exec command */
754 name = xfce_rc_read_entry (rc, G_KEY_FILE_DESKTOP_KEY_NAME, NULL);
755 exec = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
756
757 /* Support Type=Link items */
758 if (G_UNLIKELY (exec == NULL))
759 exec = url_exec = garcon_menu_item_url_exec (rc);
760
761 /* Validate Name and Exec fields */
762 if (G_LIKELY (exec != NULL && name != NULL))
763 {
764 /* Determine other application properties */
765 generic_name = xfce_rc_read_entry (rc, G_KEY_FILE_DESKTOP_KEY_GENERIC_NAME, NULL);
766 comment = xfce_rc_read_entry (rc, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL);
767 try_exec = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_TRY_EXEC, NULL);
768 icon = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
769 path = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
770 terminal = xfce_rc_read_bool_entry (rc, G_KEY_FILE_DESKTOP_KEY_TERMINAL, FALSE);
771 no_display = xfce_rc_read_bool_entry (rc, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, FALSE);
772 startup_notify = xfce_rc_read_bool_entry (rc, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, FALSE)
773 || xfce_rc_read_bool_entry (rc, "X-KDE-StartupNotify", FALSE);
774 hidden = xfce_rc_read_bool_entry (rc, G_KEY_FILE_DESKTOP_KEY_HIDDEN, FALSE);
775
776 /* Allocate a new menu item instance */
777 item = g_object_new (GARCON_TYPE_MENU_ITEM,
778 "file", file,
779 "command", exec,
780 "try-exec", try_exec,
781 "name", name,
782 "generic-name", generic_name,
783 "comment", comment,
784 "icon-name", icon,
785 "requires-terminal", terminal,
786 "no-display", no_display,
787 "supports-startup-notification", startup_notify,
788 "path", path,
789 "hidden", hidden,
790 NULL);
791
792 /* Determine the categories this application should be shown in */
793 str_list = xfce_rc_read_list_entry (rc, G_KEY_FILE_DESKTOP_KEY_CATEGORIES, ";");
794 if (G_LIKELY (str_list != NULL))
795 {
796 for (mt = str_list; *mt != NULL; ++mt)
797 {
798 /* Try to steal the values */
799 if (**mt != '\0')
800 categories = g_list_prepend (categories, *mt);
801 else
802 g_free (*mt);
803 }
804
805 /* Cleanup */
806 g_free (str_list);
807
808 /* Assign categories list to the menu item */
809 garcon_menu_item_set_categories (item, categories);
810 }
811
812 /* Determine the keywords this application should be shown in */
813 str_list = xfce_rc_read_list_entry (rc, G_KEY_FILE_DESKTOP_KEY_KEYWORDS, ";");
814 if (G_LIKELY (str_list != NULL))
815 {
816 for (mt = str_list; *mt != NULL; ++mt)
817 {
818 /* Try to steal the values */
819 if (**mt != '\0')
820 keywords = g_list_prepend (keywords, *mt);
821 else
822 g_free (*mt);
823 }
824
825 /* Cleanup */
826 g_free (str_list);
827
828 /* Assign keywords list to the menu item */
829 garcon_menu_item_set_keywords (item, keywords);
830 }
831
832 /* Set the rest of the private data directly */
833 item->priv->only_show_in = xfce_rc_read_list_entry (rc, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, ";");
834 item->priv->not_show_in = xfce_rc_read_list_entry (rc, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, ";");
835
836 /* Determine this application actions */
837 str_list = xfce_rc_read_list_entry (rc, G_KEY_FILE_DESKTOP_KEY_ACTIONS, ";");
838 if (G_LIKELY (str_list != NULL))
839 {
840 for (mt = str_list; *mt != NULL; ++mt)
841 {
842 if (**mt != '\0')
843 {
844 /* Set current desktop action group */
845 action_group = g_strdup_printf ("Desktop Action %s", *mt);
846 xfce_rc_set_group (rc, action_group);
847
848 /* Parse name and exec command */
849 name = xfce_rc_read_entry (rc, G_KEY_FILE_DESKTOP_KEY_NAME, NULL);
850 exec = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
851 icon = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
852
853 /* Validate Name and Exec fields, icon is optional */
854 if (G_LIKELY (exec != NULL && name != NULL))
855 {
856 /* Allocate a new action instance */
857 action = g_object_new (GARCON_TYPE_MENU_ITEM_ACTION,
858 "name", name,
859 "command", exec,
860 "icon-name", icon,
861 NULL);
862
863 garcon_menu_item_set_action (item, *mt, action);
864 }
865
866 g_free (action_group);
867 }
868 }
869
870 /* Cleanup */
871 g_strfreev (str_list);
872 }
873
874 else
875 {
876 str_list = xfce_rc_read_list_entry (rc, "X-Ayatana-Desktop-Shortcuts", ";");
877 if (G_LIKELY (str_list != NULL))
878 {
879 for (mt = str_list; *mt != NULL; ++mt)
880 {
881 if (**mt != '\0')
882 {
883 action_group = g_strdup_printf ("%s Shortcut Group", *mt);
884 xfce_rc_set_group (rc, action_group);
885
886 name = xfce_rc_read_entry (rc, G_KEY_FILE_DESKTOP_KEY_NAME, NULL);
887 exec = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
888 icon = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
889
890 /* Validate Name and Exec fields, icon is optional */
891 if (G_LIKELY (exec != NULL && name != NULL))
892 {
893 action = g_object_new (GARCON_TYPE_MENU_ITEM_ACTION,
894 "name", name,
895 "command", exec,
896 "icon-name", icon,
897 NULL);
898
899 garcon_menu_item_set_action (item, *mt, action);
900 }
901
902 g_free (action_group);
903 }
904 }
905
906 g_strfreev (str_list);
907 }
908 }
909 }
910
911 /* Cleanup */
912 xfce_rc_close (rc);
913 g_free (url_exec);
914
915 return item;
916 }
917
918
919 /**
920 * garcon_menu_item_new_for_path: (constructor)
921 * @filename: (type filename): name of a file
922 *
923 * Returns: (transfer full): a new #GarconMenuItem
924 */
925 GarconMenuItem *
garcon_menu_item_new_for_path(const gchar * filename)926 garcon_menu_item_new_for_path (const gchar *filename)
927 {
928 GFile *file;
929 GarconMenuItem *item;
930
931 g_return_val_if_fail (filename != NULL, NULL);
932
933 file = g_file_new_for_path (filename);
934 item = garcon_menu_item_new (file);
935 g_object_unref (G_OBJECT (file));
936
937 return item;
938 }
939
940
941 /**
942 * garcon_menu_item_new_for_uri: (constructor)
943 * @uri: a given URI
944 *
945 * Returns: (transfer full): a new #GarconMenuItem
946 */
947 GarconMenuItem *
garcon_menu_item_new_for_uri(const gchar * uri)948 garcon_menu_item_new_for_uri (const gchar *uri)
949 {
950 GFile *file;
951 GarconMenuItem *item;
952
953 g_return_val_if_fail (uri != NULL, NULL);
954
955 file = g_file_new_for_uri (uri);
956 item = garcon_menu_item_new (file);
957 g_object_unref (G_OBJECT (file));
958
959 return item;
960 }
961
962
963
964 gboolean
garcon_menu_item_reload(GarconMenuItem * item,gboolean * affects_the_outside,GError ** error)965 garcon_menu_item_reload (GarconMenuItem *item,
966 gboolean *affects_the_outside,
967 GError **error)
968 {
969 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
970 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
971
972 return garcon_menu_item_reload_from_file (item, item->priv->file, affects_the_outside, error);
973 }
974
975
976
977 gboolean
garcon_menu_item_reload_from_file(GarconMenuItem * item,GFile * file,gboolean * affects_the_outside,GError ** error)978 garcon_menu_item_reload_from_file (GarconMenuItem *item,
979 GFile *file,
980 gboolean *affects_the_outside,
981 GError **error)
982 {
983 XfceRc *rc;
984 GarconMenuItemAction *action = NULL;
985 gboolean boolean;
986 GList *categories = NULL;
987 GList *old_categories = NULL;
988 GList *keywords = NULL;
989 GList *old_keywords = NULL;
990 GList *lp;
991 gchar **mt;
992 gchar **str_list;
993 const gchar *string;
994 const gchar *name;
995 const gchar *exec;
996 const gchar *icon;
997 gchar *filename;
998 gchar *action_group;
999 gchar *url_exec = NULL;
1000
1001 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
1002 g_return_val_if_fail (G_IS_FILE (file), FALSE);
1003 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1004 g_return_val_if_fail (g_file_is_native (file), FALSE);
1005
1006 /* Open the rc file */
1007 filename = g_file_get_path (file);
1008 rc = xfce_rc_simple_open (filename, TRUE);
1009 g_free (filename);
1010 if (G_UNLIKELY (rc == NULL))
1011 return FALSE;
1012
1013 xfce_rc_set_group (rc, G_KEY_FILE_DESKTOP_GROUP);
1014
1015 /* Check if there is a name and exec key */
1016 name = xfce_rc_read_entry (rc, G_KEY_FILE_DESKTOP_KEY_NAME, NULL);
1017 exec = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
1018
1019 /* Support Type=Link items */
1020 if (G_UNLIKELY (exec == NULL))
1021 exec = url_exec = garcon_menu_item_url_exec (rc);
1022
1023 if (G_UNLIKELY (name == NULL || exec == NULL))
1024 {
1025 g_set_error_literal (error, G_KEY_FILE_ERROR,
1026 G_KEY_FILE_ERROR_KEY_NOT_FOUND,
1027 "Either the name or exec key was not defined.");
1028 xfce_rc_close (rc);
1029
1030 return FALSE;
1031 }
1032
1033 /* Queue property notifications */
1034 g_object_freeze_notify (G_OBJECT (item));
1035
1036 /* Set the new file if needed */
1037 if (!g_file_equal (file, item->priv->file))
1038 {
1039 if (G_LIKELY (item->priv->file != NULL))
1040 g_object_unref (G_OBJECT (item->priv->file));
1041 item->priv->file = G_FILE (g_object_ref (G_OBJECT (file)));
1042
1043 g_object_notify (G_OBJECT (item), "file");
1044 }
1045
1046 /* Update properties */
1047 garcon_menu_item_set_name (item, name);
1048
1049 garcon_menu_item_set_command (item, exec);
1050
1051 string = xfce_rc_read_entry (rc, G_KEY_FILE_DESKTOP_KEY_GENERIC_NAME, NULL);
1052 garcon_menu_item_set_generic_name (item, string);
1053
1054 string = xfce_rc_read_entry (rc, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL);
1055 garcon_menu_item_set_comment (item, string);
1056
1057 string = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_TRY_EXEC, NULL);
1058 garcon_menu_item_set_try_exec (item, string);
1059
1060 string = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
1061 garcon_menu_item_set_icon_name (item, string);
1062
1063 string = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
1064 garcon_menu_item_set_path (item, string);
1065
1066 boolean = xfce_rc_read_bool_entry (rc, G_KEY_FILE_DESKTOP_KEY_TERMINAL, FALSE);
1067 garcon_menu_item_set_requires_terminal (item, boolean);
1068
1069 boolean = xfce_rc_read_bool_entry (rc, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, FALSE);
1070 garcon_menu_item_set_no_display (item, boolean);
1071
1072 boolean = xfce_rc_read_bool_entry (rc, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, FALSE)
1073 || xfce_rc_read_bool_entry (rc, "X-KDE-StartupNotify", FALSE);
1074 garcon_menu_item_set_supports_startup_notification (item, boolean);
1075
1076 boolean = xfce_rc_read_bool_entry (rc, G_KEY_FILE_DESKTOP_KEY_HIDDEN, FALSE);
1077 garcon_menu_item_set_hidden (item, boolean);
1078
1079 if (affects_the_outside != NULL)
1080 {
1081 /* create a deep copy the old categories list */
1082 old_categories = g_list_copy (item->priv->categories);
1083 for (lp = old_categories; lp != NULL; lp = lp->next)
1084 lp->data = g_strdup (lp->data);
1085 }
1086
1087 /* Determine the categories this application should be shown in */
1088 str_list = xfce_rc_read_list_entry (rc, G_KEY_FILE_DESKTOP_KEY_CATEGORIES, ";");
1089 if (G_LIKELY (str_list != NULL))
1090 {
1091 for (mt = str_list; *mt != NULL; ++mt)
1092 {
1093 /* Try to steal the values */
1094 if (**mt != '\0')
1095 categories = g_list_prepend (categories, *mt);
1096 else
1097 g_free (*mt);
1098 }
1099
1100 /* Cleanup */
1101 g_free (str_list);
1102
1103 /* Assign categories list to the menu item */
1104 garcon_menu_item_set_categories (item, categories);
1105 }
1106 else
1107 {
1108 /* Assign empty categories list to the menu item */
1109 garcon_menu_item_set_categories (item, NULL);
1110 }
1111
1112 if (affects_the_outside != NULL)
1113 {
1114 if (!garcon_menu_item_lists_equal (old_categories, categories))
1115 *affects_the_outside = TRUE;
1116
1117 _garcon_g_list_free_full (old_categories, g_free);
1118 }
1119
1120
1121 if (affects_the_outside != NULL)
1122 {
1123 /* create a deep copy the old keywords list */
1124 old_keywords = g_list_copy (item->priv->keywords);
1125 for (lp = old_keywords; lp != NULL; lp = lp->next)
1126 lp->data = g_strdup (lp->data);
1127 }
1128
1129 /* Determine the keywords this application should be shown in */
1130 str_list = xfce_rc_read_list_entry (rc, G_KEY_FILE_DESKTOP_KEY_KEYWORDS, ";");
1131 if (G_LIKELY (str_list != NULL))
1132 {
1133 for (mt = str_list; *mt != NULL; ++mt)
1134 {
1135 /* Try to steal the values */
1136 if (**mt != '\0')
1137 keywords = g_list_prepend (keywords, *mt);
1138 else
1139 g_free (*mt);
1140 }
1141
1142 /* Cleanup */
1143 g_free (str_list);
1144
1145 /* Assign keywords list to the menu item */
1146 garcon_menu_item_set_keywords (item, keywords);
1147 }
1148 else
1149 {
1150 /* Assign empty keywords list to the menu item */
1151 garcon_menu_item_set_keywords (item, NULL);
1152 }
1153
1154 if (affects_the_outside != NULL)
1155 {
1156 if (!garcon_menu_item_lists_equal (old_keywords, keywords))
1157 *affects_the_outside = TRUE;
1158
1159 _garcon_g_list_free_full (old_keywords, g_free);
1160 }
1161
1162 /* Set the rest of the private data directly */
1163 item->priv->only_show_in = xfce_rc_read_list_entry (rc, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, ";");
1164 item->priv->not_show_in = xfce_rc_read_list_entry (rc, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, ";");
1165
1166 /* Update application actions */
1167 _garcon_g_list_free_full (item->priv->actions, garcon_menu_item_action_unref);
1168 item->priv->actions = NULL;
1169
1170 str_list = xfce_rc_read_list_entry (rc, G_KEY_FILE_DESKTOP_KEY_ACTIONS, ";");
1171 if (G_LIKELY (str_list != NULL))
1172 {
1173 for (mt = str_list; *mt != NULL; ++mt)
1174 {
1175 if (**mt != '\0')
1176 {
1177 /* Set current desktop action group */
1178 action_group = g_strdup_printf ("Desktop Action %s", *mt);
1179 xfce_rc_set_group (rc, action_group);
1180
1181 /* Parse name and exec command */
1182 name = xfce_rc_read_entry (rc, G_KEY_FILE_DESKTOP_KEY_NAME, NULL);
1183 exec = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
1184 icon = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
1185
1186 /* Validate Name and Exec fields, icon is optional */
1187 if (G_LIKELY (exec != NULL && name != NULL))
1188 {
1189 /* Allocate a new action instance */
1190 action = g_object_new (GARCON_TYPE_MENU_ITEM_ACTION,
1191 "name", name,
1192 "command", exec,
1193 "icon-name", icon,
1194 NULL);
1195
1196 garcon_menu_item_set_action (item, *mt, action);
1197 }
1198 g_free (action_group);
1199 }
1200 else
1201 g_free (*mt);
1202 }
1203
1204 /* Cleanup */
1205 g_free (str_list);
1206 }
1207
1208 else
1209 {
1210 str_list = xfce_rc_read_list_entry (rc, "X-Ayatana-Desktop-Shortcuts", ";");
1211 if (G_LIKELY (str_list != NULL))
1212 {
1213 for (mt = str_list; *mt != NULL; ++mt)
1214 {
1215 if (**mt != '\0')
1216 {
1217 action_group = g_strdup_printf ("%s Shortcut Group", *mt);
1218 xfce_rc_set_group (rc, action_group);
1219
1220 name = xfce_rc_read_entry (rc, G_KEY_FILE_DESKTOP_KEY_NAME, NULL);
1221 exec = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
1222 icon = xfce_rc_read_entry_untranslated (rc, G_KEY_FILE_DESKTOP_KEY_ICON, NULL);
1223
1224 /* Validate Name and Exec fields, icon is optional */
1225 if (G_LIKELY (exec != NULL && name != NULL))
1226 {
1227 action = g_object_new (GARCON_TYPE_MENU_ITEM_ACTION,
1228 "name", name,
1229 "command", exec,
1230 "icon-name", icon,
1231 NULL);
1232
1233 garcon_menu_item_set_action (item, *mt, action);
1234 }
1235
1236 g_free (action_group);
1237 }
1238 else
1239 g_free (*mt);
1240 }
1241
1242 g_free (str_list);
1243 }
1244 }
1245
1246 /* Flush property notifications */
1247 g_object_thaw_notify (G_OBJECT (item));
1248
1249 /* Emit signal to everybody knows we reloaded the file */
1250 g_signal_emit (G_OBJECT (item), item_signals[CHANGED], 0);
1251
1252 xfce_rc_close (rc);
1253 g_free (url_exec);
1254
1255 return TRUE;
1256 }
1257
1258
1259
1260 /**
1261 * garcon_menu_item_get_file:
1262 * @item: A #GarconMenuItem
1263 *
1264 * Get the #GFile for @item. The returned object should be
1265 * unreffed with g_object_unref() when no longer needed.
1266 *
1267 * Returns: (transfer full): a #GFile.
1268 */
1269 GFile *
garcon_menu_item_get_file(GarconMenuItem * item)1270 garcon_menu_item_get_file (GarconMenuItem *item)
1271 {
1272 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1273 return g_object_ref (item->priv->file);
1274 }
1275
1276
1277 gchar *
garcon_menu_item_get_uri(GarconMenuItem * item)1278 garcon_menu_item_get_uri (GarconMenuItem *item)
1279 {
1280 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1281 return g_file_get_uri (item->priv->file);
1282 }
1283
1284
1285
1286 const gchar *
garcon_menu_item_get_desktop_id(GarconMenuItem * item)1287 garcon_menu_item_get_desktop_id (GarconMenuItem *item)
1288 {
1289 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1290 return item->priv->desktop_id;
1291 }
1292
1293
1294
1295 void
garcon_menu_item_set_desktop_id(GarconMenuItem * item,const gchar * desktop_id)1296 garcon_menu_item_set_desktop_id (GarconMenuItem *item,
1297 const gchar *desktop_id)
1298 {
1299 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1300 g_return_if_fail (desktop_id != NULL);
1301
1302 /* Abort if old and new desktop_id are equal */
1303 if (g_strcmp0 (item->priv->desktop_id, desktop_id) == 0)
1304 return;
1305
1306 /* Assign the new desktop_id */
1307 g_free (item->priv->desktop_id);
1308 item->priv->desktop_id = g_strdup (desktop_id);
1309
1310 /* Notify listeners */
1311 g_object_notify (G_OBJECT (item), "desktop-id");
1312 }
1313
1314
1315 /**
1316 * garcon_menu_item_get_categories:
1317 * @item: a #GarconMenuItem
1318 *
1319 * Returns list of categories
1320 *
1321 * Returns: (element-type utf8) (transfer full):
1322 */
1323 GList*
garcon_menu_item_get_categories(GarconMenuItem * item)1324 garcon_menu_item_get_categories (GarconMenuItem *item)
1325 {
1326 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1327 return item->priv->categories;
1328 }
1329
1330
1331 /**
1332 * garcon_menu_item_set_categories:
1333 * @item: a #GarconMenuItem
1334 * @categories: (element-type utf8): list of categories
1335 */
1336 void
garcon_menu_item_set_categories(GarconMenuItem * item,GList * categories)1337 garcon_menu_item_set_categories (GarconMenuItem *item,
1338 GList *categories)
1339 {
1340 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1341
1342 /* Abort if lists are equal */
1343 if (G_UNLIKELY (item->priv->categories == categories))
1344 return;
1345
1346 /* Free old list */
1347 _garcon_g_list_free_full (item->priv->categories, g_free);
1348
1349 /* Assign new list */
1350 item->priv->categories = categories;
1351 }
1352
1353
1354 /**
1355 * garcon_menu_item_get_keywords:
1356 * @item: a #GarconMenuItem
1357 *
1358 * Returns: (element-type utf8) (transfer full):
1359 */
1360 GList*
garcon_menu_item_get_keywords(GarconMenuItem * item)1361 garcon_menu_item_get_keywords (GarconMenuItem *item)
1362 {
1363 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1364 return item->priv->keywords;
1365 }
1366
1367
1368 /**
1369 * garcon_menu_item_set_keywords:
1370 * @item: a #GarconMenuItem
1371 * @keywords: (element-type utf8): list of keywords
1372 */
1373 void
garcon_menu_item_set_keywords(GarconMenuItem * item,GList * keywords)1374 garcon_menu_item_set_keywords (GarconMenuItem *item,
1375 GList *keywords)
1376 {
1377 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1378
1379 /* Abort if lists are equal */
1380 if (G_UNLIKELY (item->priv->keywords == keywords))
1381 return;
1382
1383 /* Free old list */
1384 _garcon_g_list_free_full (item->priv->keywords, g_free);
1385
1386 /* Assign new list */
1387 item->priv->keywords = keywords;
1388 }
1389
1390
1391 const gchar*
garcon_menu_item_get_command(GarconMenuItem * item)1392 garcon_menu_item_get_command (GarconMenuItem *item)
1393 {
1394 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1395 return item->priv->command;
1396 }
1397
1398
1399 void
garcon_menu_item_set_command(GarconMenuItem * item,const gchar * command)1400 garcon_menu_item_set_command (GarconMenuItem *item,
1401 const gchar *command)
1402 {
1403 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1404 g_return_if_fail (command != NULL);
1405
1406 /* Abort if old and new command are equal */
1407 if (g_strcmp0 (item->priv->command, command) == 0)
1408 return;
1409
1410 /* Assign new command */
1411 g_free (item->priv->command);
1412 item->priv->command = g_strdup (command);
1413
1414 /* Notify listeners */
1415 g_object_notify (G_OBJECT (item), "command");
1416 }
1417
1418
1419
1420 const gchar*
garcon_menu_item_get_try_exec(GarconMenuItem * item)1421 garcon_menu_item_get_try_exec (GarconMenuItem *item)
1422 {
1423 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1424 return item->priv->try_exec;
1425 }
1426
1427
1428
1429 void
garcon_menu_item_set_try_exec(GarconMenuItem * item,const gchar * try_exec)1430 garcon_menu_item_set_try_exec (GarconMenuItem *item,
1431 const gchar *try_exec)
1432 {
1433 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1434
1435 /* Abort if old and new try_exec are equal */
1436 if (g_strcmp0 (item->priv->try_exec, try_exec) == 0)
1437 return;
1438
1439 /* Assign new try_exec */
1440 g_free (item->priv->try_exec);
1441 item->priv->try_exec = g_strdup (try_exec);
1442
1443 /* Notify listeners */
1444 g_object_notify (G_OBJECT (item), "try-exec");
1445 }
1446
1447
1448
1449 const gchar*
garcon_menu_item_get_name(GarconMenuItem * item)1450 garcon_menu_item_get_name (GarconMenuItem *item)
1451 {
1452 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1453 return item->priv->name;
1454 }
1455
1456
1457
1458 void
garcon_menu_item_set_name(GarconMenuItem * item,const gchar * name)1459 garcon_menu_item_set_name (GarconMenuItem *item,
1460 const gchar *name)
1461 {
1462 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1463 g_return_if_fail (g_utf8_validate (name, -1, NULL));
1464
1465 /* Abort if old and new name are equal */
1466 if (g_strcmp0 (item->priv->name, name) == 0)
1467 return;
1468
1469 /* Assign new name */
1470 g_free (item->priv->name);
1471 item->priv->name = g_strdup (name);
1472
1473 /* Notify listeners */
1474 g_object_notify (G_OBJECT (item), "name");
1475 }
1476
1477
1478
1479 const gchar*
garcon_menu_item_get_generic_name(GarconMenuItem * item)1480 garcon_menu_item_get_generic_name (GarconMenuItem *item)
1481 {
1482 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1483 return item->priv->generic_name;
1484 }
1485
1486
1487
1488 void
garcon_menu_item_set_generic_name(GarconMenuItem * item,const gchar * generic_name)1489 garcon_menu_item_set_generic_name (GarconMenuItem *item,
1490 const gchar *generic_name)
1491 {
1492 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1493 g_return_if_fail (generic_name == NULL || g_utf8_validate (generic_name, -1, NULL));
1494
1495 /* Abort if old and new generic name are equal */
1496 if (g_strcmp0 (item->priv->generic_name, generic_name) == 0)
1497 return;
1498
1499 /* Assign new generic_name */
1500 g_free (item->priv->generic_name);
1501 item->priv->generic_name = g_strdup (generic_name);
1502
1503 /* Notify listeners */
1504 g_object_notify (G_OBJECT (item), "generic-name");
1505 }
1506
1507
1508
1509 const gchar*
garcon_menu_item_get_comment(GarconMenuItem * item)1510 garcon_menu_item_get_comment (GarconMenuItem *item)
1511 {
1512 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1513 return item->priv->comment;
1514 }
1515
1516
1517
1518 void
garcon_menu_item_set_comment(GarconMenuItem * item,const gchar * comment)1519 garcon_menu_item_set_comment (GarconMenuItem *item,
1520 const gchar *comment)
1521 {
1522 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1523 g_return_if_fail (comment == NULL || g_utf8_validate (comment, -1, NULL));
1524
1525 /* Abort if old and new comment are equal */
1526 if (g_strcmp0 (item->priv->comment, comment) == 0)
1527 return;
1528
1529 /* Assign new comment */
1530 g_free (item->priv->comment);
1531 item->priv->comment = g_strdup (comment);
1532
1533 /* Notify listeners */
1534 g_object_notify (G_OBJECT (item), "comment");
1535 }
1536
1537
1538
1539 const gchar*
garcon_menu_item_get_icon_name(GarconMenuItem * item)1540 garcon_menu_item_get_icon_name (GarconMenuItem *item)
1541 {
1542 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1543 return item->priv->icon_name;
1544 }
1545
1546
1547
1548 void
garcon_menu_item_set_icon_name(GarconMenuItem * item,const gchar * icon_name)1549 garcon_menu_item_set_icon_name (GarconMenuItem *item,
1550 const gchar *icon_name)
1551 {
1552 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1553
1554 /* Abort if old and new icon name are equal */
1555 if (g_strcmp0 (item->priv->icon_name, icon_name) == 0)
1556 return;
1557
1558 /* Assign new icon name */
1559 g_free (item->priv->icon_name);
1560 item->priv->icon_name = g_strdup (icon_name);
1561
1562 /* Notify listeners */
1563 g_object_notify (G_OBJECT (item), "icon-name");
1564 }
1565
1566
1567
1568 const gchar*
garcon_menu_item_get_path(GarconMenuItem * item)1569 garcon_menu_item_get_path (GarconMenuItem *item)
1570 {
1571 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1572 return item->priv->path;
1573 }
1574
1575
1576
1577 void
garcon_menu_item_set_path(GarconMenuItem * item,const gchar * path)1578 garcon_menu_item_set_path (GarconMenuItem *item,
1579 const gchar *path)
1580 {
1581 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1582
1583 /* Abort if old and new path are equal */
1584 if (g_strcmp0 (item->priv->path, path) == 0)
1585 return;
1586
1587 /* Assign new path */
1588 g_free (item->priv->path);
1589 item->priv->path = g_strdup (path);
1590
1591 /* Notify listeners */
1592 g_object_notify (G_OBJECT (item), "path");
1593 }
1594
1595
1596
1597 gboolean
garcon_menu_item_get_hidden(GarconMenuItem * item)1598 garcon_menu_item_get_hidden (GarconMenuItem *item)
1599 {
1600 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), TRUE);
1601 return item->priv->hidden;
1602 }
1603
1604
1605
1606 void
garcon_menu_item_set_hidden(GarconMenuItem * item,gboolean hidden)1607 garcon_menu_item_set_hidden (GarconMenuItem *item,
1608 gboolean hidden)
1609 {
1610 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1611
1612 /* Abort if old and new value are equal */
1613 if (item->priv->hidden == hidden)
1614 return;
1615
1616 /* Assign new value */
1617 item->priv->hidden = !!hidden;
1618
1619 /* Notify listeners */
1620 g_object_notify (G_OBJECT (item), "hidden");
1621 }
1622
1623
1624
1625 gboolean
garcon_menu_item_requires_terminal(GarconMenuItem * item)1626 garcon_menu_item_requires_terminal (GarconMenuItem *item)
1627 {
1628 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
1629 return item->priv->requires_terminal;
1630 }
1631
1632
1633
1634 void
garcon_menu_item_set_requires_terminal(GarconMenuItem * item,gboolean requires_terminal)1635 garcon_menu_item_set_requires_terminal (GarconMenuItem *item,
1636 gboolean requires_terminal)
1637 {
1638 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1639
1640 /* Abort if old and new value are equal */
1641 if (item->priv->requires_terminal == requires_terminal)
1642 return;
1643
1644 /* Assign new value */
1645 item->priv->requires_terminal = !!requires_terminal;
1646
1647 /* Notify listeners */
1648 g_object_notify (G_OBJECT (item), "requires-terminal");
1649 }
1650
1651
1652
1653 gboolean
garcon_menu_item_get_no_display(GarconMenuItem * item)1654 garcon_menu_item_get_no_display (GarconMenuItem *item)
1655 {
1656 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
1657 return item->priv->no_display;
1658 }
1659
1660
1661
1662 void
garcon_menu_item_set_no_display(GarconMenuItem * item,gboolean no_display)1663 garcon_menu_item_set_no_display (GarconMenuItem *item,
1664 gboolean no_display)
1665 {
1666 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1667
1668 /* Abort if old and new value are equal */
1669 if (item->priv->no_display == no_display)
1670 return;
1671
1672 /* Assign new value */
1673 item->priv->no_display = !!no_display;
1674
1675 /* Notify listeners */
1676 g_object_notify (G_OBJECT (item), "no-display");
1677 }
1678
1679
1680
1681 gboolean
garcon_menu_item_supports_startup_notification(GarconMenuItem * item)1682 garcon_menu_item_supports_startup_notification (GarconMenuItem *item)
1683 {
1684 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
1685 return item->priv->supports_startup_notification;
1686 }
1687
1688
1689
1690 void
garcon_menu_item_set_supports_startup_notification(GarconMenuItem * item,gboolean supports_startup_notification)1691 garcon_menu_item_set_supports_startup_notification (GarconMenuItem *item,
1692 gboolean supports_startup_notification)
1693 {
1694 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1695
1696 /* Abort if old and new value are equal */
1697 if (item->priv->supports_startup_notification == supports_startup_notification)
1698 return;
1699
1700 /* Assign new value */
1701 item->priv->supports_startup_notification = !!supports_startup_notification;
1702
1703 /* Notify listeners */
1704 g_object_notify (G_OBJECT (item), "supports-startup-notification");
1705 }
1706
1707
1708
1709 gboolean
garcon_menu_item_has_category(GarconMenuItem * item,const gchar * category)1710 garcon_menu_item_has_category (GarconMenuItem *item,
1711 const gchar *category)
1712 {
1713 GList *iter;
1714 gboolean found = FALSE;
1715
1716 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
1717 g_return_val_if_fail (category != NULL, FALSE);
1718
1719 for (iter = item->priv->categories; !found && iter != NULL; iter = g_list_next (iter))
1720 if (g_strcmp0 (iter->data, category) == 0)
1721 found = TRUE;
1722
1723 return found;
1724 }
1725
1726
1727
1728 gboolean
garcon_menu_item_has_keyword(GarconMenuItem * item,const gchar * keyword)1729 garcon_menu_item_has_keyword (GarconMenuItem *item,
1730 const gchar *keyword)
1731 {
1732 GList *iter;
1733 gboolean found = FALSE;
1734
1735 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
1736 g_return_val_if_fail (keyword != NULL, FALSE);
1737
1738 for (iter = item->priv->keywords; !found && iter != NULL; iter = g_list_next (iter))
1739 if (g_strcmp0 (iter->data, keyword) == 0)
1740 found = TRUE;
1741
1742 return found;
1743 }
1744
1745
1746 /**
1747 * garcon_menu_item_get_actions:
1748 * @item: a #GarconMenuItem
1749 *
1750 * Returns: (element-type GarconMenuItemAction) (transfer full):
1751 */
1752 GList *
garcon_menu_item_get_actions(GarconMenuItem * item)1753 garcon_menu_item_get_actions (GarconMenuItem *item)
1754 {
1755 GList *action_names = NULL;
1756 GList *iter;
1757 GarconMenuItemAction *action;
1758
1759 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1760
1761 for (iter = item->priv->actions; iter != NULL ; iter = g_list_next (iter))
1762 {
1763 action = GARCON_MENU_ITEM_ACTION (iter->data);
1764 action_names = g_list_prepend (action_names, (gchar*)garcon_menu_item_action_get_name (action));
1765 }
1766 action_names = g_list_reverse (action_names);
1767
1768 return action_names;
1769 }
1770
1771
1772 /**
1773 * garcon_menu_item_get_action:
1774 * @item: a #GarconMenuItem
1775 * @action_name:
1776 *
1777 * Returns: (nullable) (transfer full): a #GarconMenuItemAction
1778 */
1779 GarconMenuItemAction *
garcon_menu_item_get_action(GarconMenuItem * item,const gchar * action_name)1780 garcon_menu_item_get_action (GarconMenuItem *item,
1781 const gchar *action_name)
1782 {
1783 GList *iter;
1784 GarconMenuItemAction *action;
1785
1786 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), NULL);
1787 g_return_val_if_fail (action_name != NULL, NULL);
1788
1789 for (iter = item->priv->actions; iter != NULL; iter = g_list_next (iter))
1790 {
1791 action = GARCON_MENU_ITEM_ACTION (iter->data);
1792 if (g_strcmp0 (garcon_menu_item_action_get_name (action), action_name) == 0)
1793 return (action);
1794 }
1795
1796 return NULL;
1797 }
1798
1799
1800
1801
1802 void
garcon_menu_item_set_action(GarconMenuItem * item,const gchar * action_name,GarconMenuItemAction * action)1803 garcon_menu_item_set_action (GarconMenuItem *item,
1804 const gchar *action_name,
1805 GarconMenuItemAction *action)
1806 {
1807 GList *iter;
1808 GarconMenuItemAction *old_action;
1809 gboolean found = FALSE;
1810
1811 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1812 g_return_if_fail (GARCON_IS_MENU_ITEM_ACTION (action));
1813
1814 /* If action name is found in list, then insert new action into the list and
1815 * remove old action */
1816 for (iter = item->priv->actions; !found && iter != NULL; iter = g_list_next (iter))
1817 {
1818 old_action = GARCON_MENU_ITEM_ACTION (iter->data);
1819 if (g_strcmp0 (garcon_menu_item_action_get_name (old_action), action_name) == 0)
1820 {
1821 /* Release reference on action currently stored at action name */
1822 garcon_menu_item_action_unref (old_action);
1823
1824 /* Replace action in list at action name and grab a reference */
1825 iter->data = action;
1826 garcon_menu_item_action_ref (action);
1827
1828 /* Set flag that action was found */
1829 found = TRUE;
1830 }
1831 }
1832
1833 /* If action name was not found in list, then simply add it to list */
1834 if (found == FALSE)
1835 {
1836 /* Add action to list and grab a reference */
1837 item->priv->actions=g_list_append (item->priv->actions, action);
1838 garcon_menu_item_action_ref (action);
1839 }
1840 }
1841
1842
1843
1844 gboolean
garcon_menu_item_has_action(GarconMenuItem * item,const gchar * action_name)1845 garcon_menu_item_has_action (GarconMenuItem *item,
1846 const gchar *action_name)
1847 {
1848 GList *iter;
1849 GarconMenuItemAction *action;
1850 gboolean found = FALSE;
1851
1852 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
1853 g_return_val_if_fail (action_name != NULL, FALSE);
1854
1855 for (iter = item->priv->actions; !found && iter != NULL; iter = g_list_next (iter))
1856 {
1857 action = GARCON_MENU_ITEM_ACTION (iter->data);
1858 if (g_strcmp0 (garcon_menu_item_action_get_name (action), action_name) == 0)
1859 found = TRUE;
1860 }
1861
1862 return found;
1863 }
1864
1865
1866
1867 gboolean
garcon_menu_item_get_show_in_environment(GarconMenuItem * item)1868 garcon_menu_item_get_show_in_environment (GarconMenuItem *item)
1869 {
1870 const gchar *env;
1871 guint i, j;
1872 gboolean show = TRUE;
1873 gchar** path = NULL;
1874
1875 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
1876
1877 /* Determine current environment */
1878 env = garcon_get_environment ();
1879
1880 /* If no environment has been set, the menu is displayed no matter what
1881 * OnlyShowIn or NotShowIn contain */
1882 if (G_UNLIKELY (env == NULL))
1883 return TRUE;
1884
1885 /* According to the spec there is either a OnlyShowIn or a NotShowIn list
1886 * The environment can be multiple Desktop Names separated by a colons */
1887 if (G_UNLIKELY (item->priv->only_show_in != NULL))
1888 {
1889 /* Check if your environemnt is in OnlyShowIn list */
1890 show = FALSE;
1891 path = g_strsplit(env, ":", 0);
1892 for (j = 0; path[j] != NULL; j++)
1893 for (i = 0; !show && item->priv->only_show_in[i] != NULL; i++)
1894 if (g_strcmp0 (item->priv->only_show_in[i], path[j]) == 0)
1895 show = TRUE;
1896 g_strfreev(path);
1897 }
1898 else if (G_UNLIKELY (item->priv->not_show_in != NULL))
1899 {
1900 /* Check if your environemnt is in NotShowIn list */
1901 show = TRUE;
1902 path = g_strsplit(env, ":", 0);
1903 for (j = 0; path[j] != NULL; j++)
1904 for (i = 0; show && item->priv->not_show_in[i] != NULL; i++)
1905 if (g_strcmp0 (item->priv->not_show_in[i], path[j]) == 0)
1906 show = FALSE;
1907 g_strfreev(path);
1908 }
1909
1910 return show;
1911 }
1912
1913
1914
1915 gboolean
garcon_menu_item_only_show_in_environment(GarconMenuItem * item)1916 garcon_menu_item_only_show_in_environment (GarconMenuItem *item)
1917 {
1918 const gchar *env;
1919 guint i, j;
1920 gboolean show = FALSE;
1921 gchar** path = NULL;
1922
1923 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
1924
1925 /* Determine current environment */
1926 env = garcon_get_environment ();
1927
1928 /* If no environment has been set, the contents of OnlyShowIn don't matter */
1929 if (G_LIKELY (env == NULL))
1930 return FALSE;
1931
1932 /* Check if we have an OnlyShowIn list */
1933 if (G_UNLIKELY (item->priv->only_show_in != NULL))
1934 {
1935 /* Check if your environemnt is in OnlyShowIn list */
1936 show = FALSE;
1937 path = g_strsplit(env, ":", 0);
1938 for (j= 0; path[j] != NULL; j++)
1939 for (i = 0; !show && item->priv->only_show_in[i] != NULL; i++)
1940 if (g_strcmp0 (item->priv->only_show_in[i], path[j]) == 0)
1941 show = TRUE;
1942 }
1943
1944 return show;
1945 }
1946
1947
1948
1949 void
garcon_menu_item_ref(GarconMenuItem * item)1950 garcon_menu_item_ref (GarconMenuItem *item)
1951 {
1952 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1953
1954 /* Increment the allocation counter */
1955 garcon_menu_item_increment_allocated (item);
1956
1957 /* Grab a reference on the object */
1958 g_object_ref (G_OBJECT (item));
1959 }
1960
1961
1962
1963 void
garcon_menu_item_unref(GarconMenuItem * item)1964 garcon_menu_item_unref (GarconMenuItem *item)
1965 {
1966 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1967
1968 garcon_menu_item_decrement_allocated (item);
1969
1970 /* Decrement the reference counter */
1971 g_object_unref (G_OBJECT (item));
1972 }
1973
1974
1975
1976 gint
garcon_menu_item_get_allocated(GarconMenuItem * item)1977 garcon_menu_item_get_allocated (GarconMenuItem *item)
1978 {
1979 g_return_val_if_fail (GARCON_IS_MENU_ITEM (item), FALSE);
1980 return item->priv->num_allocated;
1981 }
1982
1983
1984
1985 void
garcon_menu_item_increment_allocated(GarconMenuItem * item)1986 garcon_menu_item_increment_allocated (GarconMenuItem *item)
1987 {
1988 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1989 item->priv->num_allocated++;
1990 }
1991
1992
1993
1994 void
garcon_menu_item_decrement_allocated(GarconMenuItem * item)1995 garcon_menu_item_decrement_allocated (GarconMenuItem *item)
1996 {
1997 g_return_if_fail (GARCON_IS_MENU_ITEM (item));
1998
1999 if (item->priv->num_allocated > 0)
2000 item->priv->num_allocated--;
2001 }
2002