1 /*
2 * Copyright (C) 2003, 2004 Red Hat, Inc.
3 * Copyright (C) 2012-2021 MATE Developers
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include <config.h>
22
23 #include "matemenu-tree.h"
24
25 #include <gio/gio.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <stdlib.h>
29
30 #include "menu-layout.h"
31 #include "menu-monitor.h"
32 #include "menu-util.h"
33
34 /* private */
35 typedef struct MateMenuTreeItem MateMenuTreeItem;
36 #define MATEMENU_TREE_ITEM(i) ((MateMenuTreeItem *)(i))
37 #define MATEMENU_TREE_DIRECTORY(i) ((MateMenuTreeDirectory *)(i))
38 #define MATEMENU_TREE_ENTRY(i) ((MateMenuTreeEntry *)(i))
39 #define MATEMENU_TREE_SEPARATOR(i) ((MateMenuTreeSeparator *)(i))
40 #define MATEMENU_TREE_HEADER(i) ((MateMenuTreeHeader *)(i))
41 #define MATEMENU_TREE_ALIAS(i) ((MateMenuTreeAlias *)(i))
42
43 enum {
44 PROP_0,
45
46 PROP_MENU_BASENAME,
47 PROP_MENU_PATH,
48 PROP_FLAGS
49 };
50
51 typedef enum
52 {
53 OBJECT_DRAWER,
54 OBJECT_MENU,
55 OBJECT_LAUNCHER,
56 OBJECT_APPLET,
57 OBJECT_ACTION,
58 OBJECT_MENU_BAR,
59 OBJECT_SEPARATOR,
60 } ObjectType;
61
62 /* Signals */
63 enum
64 {
65 CHANGED,
66 LAST_SIGNAL
67 };
68
69 static guint matemenu_tree_signals [LAST_SIGNAL] = { 0 };
70
71 struct _MateMenuTree
72 {
73 GObject parent_instance;
74
75 char *basename;
76 char *non_prefixed_basename;
77 char *path;
78 char *canonical_path;
79 GPtrArray *collection_applet;
80 MateMenuTreeFlags flags;
81
82 GSList *menu_file_monitors;
83
84 MenuLayoutNode *layout;
85 MateMenuTreeDirectory *root;
86 GHashTable *entries_by_id;
87
88 guint canonical : 1;
89 guint loaded : 1;
90 GSettings *settings;
91 };
92
93 G_DEFINE_TYPE (MateMenuTree, matemenu_tree, G_TYPE_OBJECT)
94
95 struct MateMenuTreeItem
96 {
97 volatile gint refcount;
98
99 MateMenuTreeItemType type;
100
101 MateMenuTreeDirectory *parent;
102 MateMenuTree *tree;
103 };
104
105 struct MateMenuTreeIter
106 {
107 volatile gint refcount;
108
109 MateMenuTreeItem *item;
110 GSList *contents;
111 GSList *contents_iter;
112 };
113
114 struct MateMenuTreeDirectory
115 {
116 MateMenuTreeItem item;
117
118 DesktopEntry *directory_entry;
119 char *name;
120
121 GSList *entries;
122 GSList *subdirs;
123
124 MenuLayoutValues default_layout_values;
125 GSList *default_layout_info;
126 GSList *layout_info;
127 GSList *contents;
128
129 guint only_unallocated : 1;
130 guint is_nodisplay : 1;
131 guint layout_pending_separator : 1;
132 guint preprocessed : 1;
133
134 /* 16 bits should be more than enough; G_MAXUINT16 means no inline header */
135 guint will_inline_header : 16;
136 };
137
138 struct MateMenuTreeEntry
139 {
140 MateMenuTreeItem item;
141
142 DesktopEntry *desktop_entry;
143 char *desktop_file_id;
144
145 guint is_excluded : 1;
146 guint is_unallocated : 1;
147 };
148
149 struct MateMenuTreeSeparator
150 {
151 MateMenuTreeItem item;
152 };
153
154 struct MateMenuTreeHeader
155 {
156 MateMenuTreeItem item;
157
158 MateMenuTreeDirectory *directory;
159 };
160
161 struct MateMenuTreeAlias
162 {
163 MateMenuTreeItem item;
164
165 MateMenuTreeDirectory *directory;
166 MateMenuTreeItem *aliased_item;
167 };
168
169 static gboolean matemenu_tree_load_layout (MateMenuTree *tree,
170 GError **error);
171 static void matemenu_tree_force_reload (MateMenuTree *tree);
172 static gboolean matemenu_tree_build_from_layout (MateMenuTree *tree,
173 GError **error);
174 static void matemenu_tree_force_rebuild (MateMenuTree *tree);
175 static void matemenu_tree_resolve_files (MateMenuTree *tree,
176 GHashTable *loaded_menu_files,
177 MenuLayoutNode *layout);
178 static void matemenu_tree_force_recanonicalize (MateMenuTree *tree);
179 static void matemenu_tree_invoke_monitors (MateMenuTree *tree);
180
181 static void matemenu_tree_item_unref_and_unset_parent (gpointer itemp);
182
183 static void collection_applet_changed (GSettings *settings,
184 gchar *key,
185 MateMenuTree *self);
186
187 typedef enum
188 {
189 MENU_FILE_MONITOR_INVALID = 0,
190 MENU_FILE_MONITOR_FILE,
191 MENU_FILE_MONITOR_NONEXISTENT_FILE,
192 MENU_FILE_MONITOR_DIRECTORY
193 } MenuFileMonitorType;
194
195 typedef struct
196 {
197 MenuFileMonitorType type;
198 MenuMonitor *monitor;
199 } MenuFileMonitor;
200
201 static void
handle_nonexistent_menu_file_changed(MenuMonitor * monitor,MenuMonitorEvent event,const char * path,MateMenuTree * tree)202 handle_nonexistent_menu_file_changed (MenuMonitor *monitor,
203 MenuMonitorEvent event,
204 const char *path,
205 MateMenuTree *tree)
206 {
207 if (event == MENU_MONITOR_EVENT_CHANGED ||
208 event == MENU_MONITOR_EVENT_CREATED)
209 {
210 menu_verbose ("\"%s\" %s, marking tree for recanonicalization\n",
211 path,
212 event == MENU_MONITOR_EVENT_CREATED ? "created" : "changed");
213
214 matemenu_tree_force_recanonicalize (tree);
215 matemenu_tree_invoke_monitors (tree);
216 }
217 }
218
219 static void
handle_menu_file_changed(MenuMonitor * monitor,MenuMonitorEvent event,const char * path,MateMenuTree * tree)220 handle_menu_file_changed (MenuMonitor *monitor,
221 MenuMonitorEvent event,
222 const char *path,
223 MateMenuTree *tree)
224 {
225 menu_verbose ("\"%s\" %s, marking tree for recanicalization\n",
226 path,
227 event == MENU_MONITOR_EVENT_CREATED ? "created" :
228 event == MENU_MONITOR_EVENT_CHANGED ? "changed" : "deleted");
229
230 matemenu_tree_force_recanonicalize (tree);
231 matemenu_tree_invoke_monitors (tree);
232 }
233
234 static void
handle_menu_file_directory_changed(MenuMonitor * monitor,MenuMonitorEvent event,const char * path,MateMenuTree * tree)235 handle_menu_file_directory_changed (MenuMonitor *monitor,
236 MenuMonitorEvent event,
237 const char *path,
238 MateMenuTree *tree)
239 {
240 if (!g_str_has_suffix (path, ".menu"))
241 return;
242
243 menu_verbose ("\"%s\" %s, marking tree for recanicalization\n",
244 path,
245 event == MENU_MONITOR_EVENT_CREATED ? "created" :
246 event == MENU_MONITOR_EVENT_CHANGED ? "changed" : "deleted");
247
248 matemenu_tree_force_recanonicalize (tree);
249 matemenu_tree_invoke_monitors (tree);
250 }
251
252 static void
matemenu_tree_add_menu_file_monitor(MateMenuTree * tree,const char * path,MenuFileMonitorType type)253 matemenu_tree_add_menu_file_monitor (MateMenuTree *tree,
254 const char *path,
255 MenuFileMonitorType type)
256 {
257 MenuFileMonitor *monitor;
258
259 monitor = g_slice_new0 (MenuFileMonitor);
260
261 monitor->type = type;
262
263 switch (type)
264 {
265 case MENU_FILE_MONITOR_FILE:
266 menu_verbose ("Adding a menu file monitor for \"%s\"\n", path);
267
268 monitor->monitor = menu_get_file_monitor (path);
269 menu_monitor_add_notify (monitor->monitor,
270 (MenuMonitorNotifyFunc) handle_menu_file_changed,
271 tree);
272 break;
273
274 case MENU_FILE_MONITOR_NONEXISTENT_FILE:
275 menu_verbose ("Adding a menu file monitor for non-existent \"%s\"\n", path);
276
277 monitor->monitor = menu_get_file_monitor (path);
278 menu_monitor_add_notify (monitor->monitor,
279 (MenuMonitorNotifyFunc) handle_nonexistent_menu_file_changed,
280 tree);
281 break;
282
283 case MENU_FILE_MONITOR_DIRECTORY:
284 menu_verbose ("Adding a menu directory monitor for \"%s\"\n", path);
285
286 monitor->monitor = menu_get_directory_monitor (path);
287 menu_monitor_add_notify (monitor->monitor,
288 (MenuMonitorNotifyFunc) handle_menu_file_directory_changed,
289 tree);
290 break;
291
292 default:
293 g_assert_not_reached ();
294 break;
295 }
296
297 tree->menu_file_monitors = g_slist_prepend (tree->menu_file_monitors, monitor);
298 }
299
300 static void
remove_menu_file_monitor(MenuFileMonitor * monitor,MateMenuTree * tree)301 remove_menu_file_monitor (MenuFileMonitor *monitor,
302 MateMenuTree *tree)
303 {
304 switch (monitor->type)
305 {
306 case MENU_FILE_MONITOR_FILE:
307 menu_monitor_remove_notify (monitor->monitor,
308 (MenuMonitorNotifyFunc) handle_menu_file_changed,
309 tree);
310 break;
311
312 case MENU_FILE_MONITOR_NONEXISTENT_FILE:
313 menu_monitor_remove_notify (monitor->monitor,
314 (MenuMonitorNotifyFunc) handle_nonexistent_menu_file_changed,
315 tree);
316 break;
317
318 case MENU_FILE_MONITOR_DIRECTORY:
319 menu_monitor_remove_notify (monitor->monitor,
320 (MenuMonitorNotifyFunc) handle_menu_file_directory_changed,
321 tree);
322 break;
323
324 default:
325 g_assert_not_reached ();
326 break;
327 }
328
329 menu_monitor_unref (monitor->monitor);
330 monitor->monitor = NULL;
331
332 monitor->type = MENU_FILE_MONITOR_INVALID;
333
334 g_slice_free (MenuFileMonitor, monitor);
335 }
336
337 static void
matemenu_tree_remove_menu_file_monitors(MateMenuTree * tree)338 matemenu_tree_remove_menu_file_monitors (MateMenuTree *tree)
339 {
340 menu_verbose ("Removing all menu file monitors\n");
341
342 g_slist_foreach (tree->menu_file_monitors,
343 (GFunc) remove_menu_file_monitor,
344 tree);
345 g_slist_free (tree->menu_file_monitors);
346 tree->menu_file_monitors = NULL;
347 }
348
349 static gboolean
canonicalize_path(MateMenuTree * tree,const char * path)350 canonicalize_path (MateMenuTree *tree,
351 const char *path)
352 {
353 tree->canonical_path = realpath (path, NULL);
354 if (tree->canonical_path)
355 {
356 tree->canonical = TRUE;
357 matemenu_tree_add_menu_file_monitor (tree,
358 tree->canonical_path,
359 MENU_FILE_MONITOR_FILE);
360 }
361 else
362 {
363 matemenu_tree_add_menu_file_monitor (tree,
364 path,
365 MENU_FILE_MONITOR_NONEXISTENT_FILE);
366 }
367
368 return tree->canonical;
369 }
370
371 static gboolean
canonicalize_basename_with_config_dir(MateMenuTree * tree,const char * basename,const char * config_dir)372 canonicalize_basename_with_config_dir (MateMenuTree *tree,
373 const char *basename,
374 const char *config_dir)
375 {
376 gboolean ret;
377 char *path;
378
379 path = g_build_filename (config_dir, "menus", basename, NULL);
380 ret = canonicalize_path (tree, path);
381 g_free (path);
382
383 return ret;
384 }
385
386 static void
canonicalize_basename(MateMenuTree * tree,const char * basename)387 canonicalize_basename (MateMenuTree *tree,
388 const char *basename)
389 {
390 if (!canonicalize_basename_with_config_dir (tree,
391 basename,
392 g_get_user_config_dir ()))
393 {
394 const char * const *system_config_dirs;
395 int i;
396
397 system_config_dirs = g_get_system_config_dirs ();
398
399 i = 0;
400 while (system_config_dirs[i] != NULL)
401 {
402 if (canonicalize_basename_with_config_dir (tree,
403 basename,
404 system_config_dirs[i]))
405 break;
406
407 ++i;
408 }
409 }
410 }
411
matemenu_tree_canonicalize_path(MateMenuTree * tree,GError ** error)412 static gboolean matemenu_tree_canonicalize_path(MateMenuTree* tree,
413 GError **error)
414 {
415 const char *menu_file = NULL;
416
417 if (tree->canonical)
418 return TRUE;
419
420 g_assert(tree->canonical_path == NULL);
421
422 matemenu_tree_remove_menu_file_monitors (tree);
423
424 if (tree->path)
425 {
426 menu_file = tree->path;
427 canonicalize_path (tree, tree->path);
428 }
429 else
430 {
431 const gchar *xdg_menu_prefix;
432
433 menu_file = tree->basename;
434 xdg_menu_prefix = g_getenv ("XDG_MENU_PREFIX");
435
436 if (xdg_menu_prefix != NULL)
437 {
438 gchar *prefixed_basename;
439
440 prefixed_basename = g_strdup_printf ("%sapplications.menu",
441 xdg_menu_prefix);
442
443 /* Some gnome-menus using applications just use "applications.menu"
444 * as the basename and expect gnome-menus to prefix it. Others (e.g.
445 * Alacarte) explicitly use "${XDG_MENU_PREFIX}applications.menu" as
446 * the basename, because they want to save changes to the right files
447 * in ~. In both cases, we want to use "applications-merged" as the
448 * merge directory (as required by the fd.o menu spec), so we save
449 * the non-prefixed basename and use it later when calling
450 * menu_layout_load().
451 */
452 if (!g_strcmp0 (tree->basename, "mate-applications.menu") ||
453 !g_strcmp0 (tree->basename, prefixed_basename))
454 {
455 canonicalize_basename (tree, prefixed_basename);
456 g_free (tree->non_prefixed_basename);
457 tree->non_prefixed_basename = g_strdup ("mate-applications.menu");
458 }
459 g_free (prefixed_basename);
460 }
461
462 if (!tree->canonical)
463 canonicalize_basename (tree, tree->basename);
464 }
465
466 if (tree->canonical)
467 {
468 menu_verbose ("Successfully looked up menu_file for \"%s\": %s\n",
469 menu_file, tree->canonical_path);
470 return TRUE;
471 }
472 else
473 {
474 g_set_error (error,
475 G_IO_ERROR,
476 G_IO_ERROR_FAILED,
477 "Failed to look up menu_file for \"%s\"\n",
478 menu_file);
479 return FALSE;
480 }
481 }
482
483 static void
matemenu_tree_force_recanonicalize(MateMenuTree * tree)484 matemenu_tree_force_recanonicalize (MateMenuTree *tree)
485 {
486 matemenu_tree_remove_menu_file_monitors (tree);
487
488 if (tree->canonical)
489 {
490 matemenu_tree_force_reload (tree);
491
492 g_free (tree->canonical_path);
493 tree->canonical_path = NULL;
494
495 tree->canonical = FALSE;
496 }
497 }
498
499 /**
500 * matemenu_tree_new:
501 * @menu_basename: Basename of menu file
502 * @flags: Flags controlling menu content
503 *
504 * Returns: (transfer full): A new #MateMenuTree instance
505 */
506 MateMenuTree *
matemenu_tree_new(const char * menu_basename,MateMenuTreeFlags flags)507 matemenu_tree_new (const char *menu_basename,
508 MateMenuTreeFlags flags)
509 {
510 g_return_val_if_fail (menu_basename != NULL, NULL);
511
512 return g_object_new (MATEMENU_TYPE_TREE,
513 "menu-basename", menu_basename,
514 "flags", flags,
515 NULL);
516 }
517
518 /**
519 * matemenu_tree_new_fo_path:
520 * @menu_path: Path of menu file
521 * @flags: Flags controlling menu content
522 *
523 * Returns: (transfer full): A new #MateMenuTree instance
524 */
525 MateMenuTree *
matemenu_tree_new_for_path(const char * menu_path,MateMenuTreeFlags flags)526 matemenu_tree_new_for_path (const char *menu_path,
527 MateMenuTreeFlags flags)
528 {
529 g_return_val_if_fail (menu_path != NULL, NULL);
530
531 return g_object_new (MATEMENU_TYPE_TREE,
532 "menu-path", menu_path,
533 "flags", flags,
534 NULL);
535 }
536
537 static GObject *
matemenu_tree_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)538 matemenu_tree_constructor (GType type,
539 guint n_construct_properties,
540 GObjectConstructParam *construct_properties)
541 {
542 GObject *obj;
543 MateMenuTree *self;
544
545 obj = G_OBJECT_CLASS (matemenu_tree_parent_class)->constructor (type,
546 n_construct_properties,
547 construct_properties);
548
549 /* If MateMenuTree:menu-path is set, then we should make sure that
550 * MateMenuTree:menu-basename is unset (especially as it has a default
551 * value). This has to be done here, in the constructor, since the
552 * properties are construct-only. */
553
554 self = MATEMENU_TREE (obj);
555
556 if (self->path != NULL)
557 g_object_set (self, "menu-basename", NULL, NULL);
558
559 return obj;
560 }
561
562 static void
matemenu_tree_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)563 matemenu_tree_set_property (GObject *object,
564 guint prop_id,
565 const GValue *value,
566 GParamSpec *pspec)
567 {
568 MateMenuTree *self = MATEMENU_TREE (object);
569
570 switch (prop_id)
571 {
572 case PROP_MENU_BASENAME:
573 self->basename = g_value_dup_string (value);
574 break;
575
576 case PROP_MENU_PATH:
577 self->path = g_value_dup_string (value);
578 break;
579
580 case PROP_FLAGS:
581 self->flags = g_value_get_flags (value);
582 break;
583
584 default:
585 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
586 break;
587 }
588 }
589
590 static void
matemenu_tree_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)591 matemenu_tree_get_property (GObject *object,
592 guint prop_id,
593 GValue *value,
594 GParamSpec *pspec)
595 {
596 MateMenuTree *self = MATEMENU_TREE (object);
597
598 switch (prop_id)
599 {
600 case PROP_MENU_BASENAME:
601 g_value_set_string (value, self->basename);
602 break;
603 case PROP_MENU_PATH:
604 g_value_set_string (value, self->path);
605 break;
606 case PROP_FLAGS:
607 g_value_set_flags (value, self->flags);
608 break;
609 default:
610 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
611 break;
612 }
613 }
614
615 static void
matemenu_tree_finalize(GObject * object)616 matemenu_tree_finalize (GObject *object)
617 {
618 MateMenuTree *tree = MATEMENU_TREE (object);
619
620 matemenu_tree_force_recanonicalize (tree);
621
622 if (tree->basename != NULL)
623 g_free (tree->basename);
624 tree->basename = NULL;
625
626 g_free (tree->non_prefixed_basename);
627 tree->non_prefixed_basename = NULL;
628
629 if (tree->path != NULL)
630 g_free (tree->path);
631 tree->path = NULL;
632
633 if (tree->canonical_path != NULL)
634 g_free (tree->canonical_path);
635 tree->canonical_path = NULL;
636
637 g_hash_table_destroy (tree->entries_by_id);
638 tree->entries_by_id = NULL;
639 if (tree->collection_applet != NULL)
640 {
641 g_ptr_array_foreach (tree->collection_applet, (GFunc) g_free, NULL);
642 g_ptr_array_free (tree->collection_applet, TRUE);
643 tree->collection_applet = NULL;
644 }
645 g_signal_handlers_disconnect_by_func (tree->settings,
646 G_CALLBACK (collection_applet_changed),
647 tree);
648
649 g_object_unref (tree->settings);
650 G_OBJECT_CLASS (matemenu_tree_parent_class)->finalize (object);
651 }
652
653 static void
load_object(char * id,MateMenuTree * self)654 load_object (char *id,
655 MateMenuTree *self)
656 {
657 ObjectType object_type;
658 char *object_path;
659 GSettings *settings;
660
661 object_path = g_strdup_printf ("/org/mate/panel/objects/%s/", id);
662 settings = g_settings_new_with_path ("org.mate.panel.object", object_path);
663
664 object_type = g_settings_get_enum (settings, "object-type");
665 if (object_type == OBJECT_LAUNCHER)
666 {
667 char *location;
668 char *desktop_name;
669
670 if (self->collection_applet == NULL)
671 self->collection_applet = g_ptr_array_new ();
672
673 location = g_settings_get_string (settings, "launcher-location");
674 desktop_name = g_path_get_basename (location);
675 if (strstr (desktop_name, "-1.") != NULL )
676 {
677 char **str;
678
679 str = g_strsplit (desktop_name, "-1.", -1);
680 g_free (desktop_name);
681 desktop_name = g_strdup_printf ("%s.%s", str[0], str[1]);
682 g_strfreev (str);
683 }
684 g_ptr_array_add (self->collection_applet, desktop_name);
685 g_free (location);
686 }
687 g_free (object_path);
688 g_object_unref (settings);
689 }
690
691 static gboolean
emit_changed_signal(gpointer data)692 emit_changed_signal (gpointer data)
693 {
694 MateMenuTree *self = data;
695 matemenu_tree_force_rebuild (self);
696 matemenu_tree_invoke_monitors (self);
697
698 return FALSE;
699 }
700
701 static void
get_panel_collection_applet(MateMenuTree * self)702 get_panel_collection_applet (MateMenuTree *self)
703 {
704 gchar **list;
705 guint i;
706
707 list = g_settings_get_strv (self->settings, "object-id-list");
708 for (i = 0; list[i]; i++)
709 {
710 load_object (list[i], self);
711 }
712 g_strfreev (list);
713 }
714
715 static void
collection_applet_changed(GSettings * settings,gchar * key,MateMenuTree * self)716 collection_applet_changed (GSettings *settings,
717 gchar *key,
718 MateMenuTree *self)
719 {
720 if (self->collection_applet != NULL)
721 {
722 g_ptr_array_foreach (self->collection_applet, (GFunc) g_free, NULL);
723 g_ptr_array_free (self->collection_applet, TRUE);
724 self->collection_applet = NULL;
725 }
726 get_panel_collection_applet (self);
727 g_idle_add (emit_changed_signal, (gpointer)self);
728 }
729
730 static void
matemenu_tree_init(MateMenuTree * self)731 matemenu_tree_init (MateMenuTree *self)
732 {
733 self->entries_by_id = g_hash_table_new (g_str_hash, g_str_equal);
734 self->collection_applet = NULL;
735 self->settings = g_settings_new ("org.mate.panel");
736 get_panel_collection_applet (self);
737 g_signal_connect (self->settings, "changed::object-id-list",
738 G_CALLBACK (collection_applet_changed),
739 self);
740 }
741
742 static void
matemenu_tree_class_init(MateMenuTreeClass * klass)743 matemenu_tree_class_init (MateMenuTreeClass *klass)
744 {
745 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
746
747 gobject_class->constructor = matemenu_tree_constructor;
748 gobject_class->get_property = matemenu_tree_get_property;
749 gobject_class->set_property = matemenu_tree_set_property;
750 gobject_class->finalize = matemenu_tree_finalize;
751
752 /**
753 * MateMenuTree:menu-basename:
754 *
755 * The name of the menu file; must be a basename or a relative path. The file
756 * will be looked up in $XDG_CONFIG_DIRS/menus/. See the Desktop Menu
757 * specification.
758 */
759 g_object_class_install_property (gobject_class,
760 PROP_MENU_BASENAME,
761 g_param_spec_string ("menu-basename", "", "",
762 "applications.menu",
763 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
764 /**
765 * MateMenuTree:menu-path:
766 *
767 * The full path of the menu file. If set, MateMenuTree:menu-basename will get
768 * ignored.
769 */
770 g_object_class_install_property (gobject_class,
771 PROP_MENU_PATH,
772 g_param_spec_string ("menu-path", "", "",
773 NULL,
774 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
775 /**
776 * MateMenuTree:flags:
777 *
778 * Flags controlling the content of the menu.
779 */
780 g_object_class_install_property (gobject_class,
781 PROP_FLAGS,
782 g_param_spec_flags ("flags", "", "",
783 MATEMENU_TYPE_TREE_FLAGS,
784 MATEMENU_TREE_FLAGS_NONE,
785 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
786
787 /**
788 * MateMenuTree:changed:
789 *
790 * This signal is emitted when applications are added, removed, or
791 * upgraded. But note the new data will only be visible after
792 * matemenu_tree_load_sync() or a variant thereof is invoked.
793 */
794 matemenu_tree_signals[CHANGED] =
795 g_signal_new ("changed",
796 G_TYPE_FROM_CLASS (klass),
797 G_SIGNAL_RUN_LAST,
798 0,
799 NULL, NULL,
800 g_cclosure_marshal_VOID__VOID,
801 G_TYPE_NONE, 0);
802 }
803
804 /**
805 * matemenu_tree_get_canonical_menu_path:
806 * @tree: a #MateMenuTree
807 *
808 * This function is only available if the tree has been loaded via
809 * matemenu_tree_load_sync() or a variant thereof.
810 *
811 * Returns: The absolute and canonicalized path to the loaded menu file
812 */
813 const char *
matemenu_tree_get_canonical_menu_path(MateMenuTree * tree)814 matemenu_tree_get_canonical_menu_path (MateMenuTree *tree)
815 {
816 g_return_val_if_fail (MATEMENU_IS_TREE (tree), NULL);
817 g_return_val_if_fail (tree->loaded, NULL);
818
819 return tree->canonical_path;
820 }
821
822 /**
823 * matemenu_tree_load_sync:
824 * @tree: a #MateMenuTree
825 * @error: a #GError
826 *
827 * Synchronously load the menu contents. This function
828 * performs a significant amount of blocking I/O if the
829 * tree has not been loaded yet.
830 *
831 * Returns: %TRUE on success, %FALSE on error
832 */
833 gboolean
matemenu_tree_load_sync(MateMenuTree * tree,GError ** error)834 matemenu_tree_load_sync (MateMenuTree *tree,
835 GError **error)
836 {
837 GError *local_error = NULL;
838
839 if (tree->loaded)
840 return TRUE;
841
842 if (!matemenu_tree_build_from_layout (tree, &local_error))
843 {
844 if (local_error)
845 g_propagate_error (error, local_error);
846 return FALSE;
847 }
848
849 tree->loaded = TRUE;
850
851 return TRUE;
852 }
853
854 /**
855 * matemenu_tree_get_root_directory:
856 * @tree: a #MateMenuTree
857 *
858 * Get the root directory; you must have loaded the tree first (at
859 * least once) via matemenu_tree_load_sync() or a variant thereof.
860 *
861 * Returns: (transfer full): Root of the tree
862 */
863 MateMenuTreeDirectory *
matemenu_tree_get_root_directory(MateMenuTree * tree)864 matemenu_tree_get_root_directory (MateMenuTree *tree)
865 {
866 g_return_val_if_fail (tree != NULL, NULL);
867 g_return_val_if_fail (tree->loaded, NULL);
868
869 return matemenu_tree_item_ref (tree->root);
870 }
871
872 static MateMenuTreeDirectory *
find_path(MateMenuTreeDirectory * directory,const char * path)873 find_path (MateMenuTreeDirectory *directory,
874 const char *path)
875 {
876 const char *name;
877 char *slash;
878 char *freeme;
879 GSList *tmp;
880
881 while (path[0] == G_DIR_SEPARATOR) path++;
882
883 if (path[0] == '\0')
884 return directory;
885
886 freeme = NULL;
887 slash = strchr (path, G_DIR_SEPARATOR);
888 if (slash)
889 {
890 name = freeme = g_strndup (path, (gsize)(slash - path));
891 path = slash + 1;
892 }
893 else
894 {
895 name = path;
896 path = NULL;
897 }
898
899 tmp = directory->contents;
900 while (tmp != NULL)
901 {
902 MateMenuTreeItem *item = tmp->data;
903
904 if (item->type != MATEMENU_TREE_ITEM_DIRECTORY)
905 {
906 tmp = tmp->next;
907 continue;
908 }
909
910 if (!strcmp (name, MATEMENU_TREE_DIRECTORY (item)->name))
911 {
912 g_free (freeme);
913
914 if (path)
915 return find_path (MATEMENU_TREE_DIRECTORY (item), path);
916 else
917 return MATEMENU_TREE_DIRECTORY (item);
918 }
919
920 tmp = tmp->next;
921 }
922
923 g_free (freeme);
924
925 return NULL;
926 }
927
928 MateMenuTreeDirectory *
matemenu_tree_get_directory_from_path(MateMenuTree * tree,const char * path)929 matemenu_tree_get_directory_from_path (MateMenuTree *tree,
930 const char *path)
931 {
932 MateMenuTreeDirectory *root;
933 MateMenuTreeDirectory *directory;
934
935 g_return_val_if_fail (tree != NULL, NULL);
936 g_return_val_if_fail (path != NULL, NULL);
937
938 if (path[0] != G_DIR_SEPARATOR)
939 return NULL;
940
941 if (!(root = matemenu_tree_get_root_directory (tree)))
942 return NULL;
943
944 directory = find_path (root, path);
945
946 matemenu_tree_item_unref (root);
947
948 return directory ? matemenu_tree_item_ref (directory) : NULL;
949 }
950
951 /**
952 * matemenu_tree_get_entry_by_id:
953 * @tree: a #MateMenuTree
954 * @id: a desktop file ID
955 *
956 * Look up the entry corresponding to the given "desktop file id".
957 *
958 * Returns: (transfer full): A newly referenced #MateMenuTreeEntry, or %NULL if none
959 */
960 MateMenuTreeEntry *
matemenu_tree_get_entry_by_id(MateMenuTree * tree,const char * id)961 matemenu_tree_get_entry_by_id (MateMenuTree *tree,
962 const char *id)
963 {
964 MateMenuTreeEntry *entry;
965
966 g_return_val_if_fail (tree->loaded, NULL);
967
968 entry = g_hash_table_lookup (tree->entries_by_id, id);
969 if (entry != NULL)
970 matemenu_tree_item_ref (entry);
971
972 return entry;
973 }
974
975 static void
matemenu_tree_invoke_monitors(MateMenuTree * tree)976 matemenu_tree_invoke_monitors (MateMenuTree *tree)
977 {
978 g_signal_emit (tree, matemenu_tree_signals[CHANGED], 0);
979 }
980
981 static MateMenuTreeDirectory *
get_parent(MateMenuTreeItem * item)982 get_parent (MateMenuTreeItem *item)
983 {
984 g_return_val_if_fail (item != NULL, NULL);
985 return item->parent ? matemenu_tree_item_ref (item->parent) : NULL;
986 }
987
988 /**
989 * matemenu_tree_directory_get_parent:
990 * @directory: a #MateMenuTreeDirectory
991 *
992 * Returns: (transfer full): The parent directory, or %NULL if none
993 */
994 MateMenuTreeDirectory *
matemenu_tree_directory_get_parent(MateMenuTreeDirectory * directory)995 matemenu_tree_directory_get_parent (MateMenuTreeDirectory *directory)
996 {
997 return get_parent ((MateMenuTreeItem *)directory);
998 }
999
1000 /**
1001 * matemenu_tree_entry_get_parent:
1002 * @entry: a #MateMenuTreeEntry
1003 *
1004 * Returns: (transfer full): The parent directory, or %NULL if none
1005 */
1006 MateMenuTreeDirectory *
matemenu_tree_entry_get_parent(MateMenuTreeEntry * entry)1007 matemenu_tree_entry_get_parent (MateMenuTreeEntry *entry)
1008 {
1009 return get_parent ((MateMenuTreeItem *)entry);
1010 }
1011
1012 /**
1013 * matemenu_tree_alias_get_parent:
1014 * @alias: a #MateMenuTreeAlias
1015 *
1016 * Returns: (transfer full): The parent directory, or %NULL if none
1017 */
1018 MateMenuTreeDirectory *
matemenu_tree_alias_get_parent(MateMenuTreeAlias * alias)1019 matemenu_tree_alias_get_parent (MateMenuTreeAlias *alias)
1020 {
1021 return get_parent ((MateMenuTreeItem *)alias);
1022 }
1023
1024 /**
1025 * matemenu_tree_header_get_parent:
1026 * @header: a #MateMenuTreeHeader
1027 *
1028 * Returns: (transfer full): The parent directory, or %NULL if none
1029 */
1030 MateMenuTreeDirectory *
matemenu_tree_header_get_parent(MateMenuTreeHeader * header)1031 matemenu_tree_header_get_parent (MateMenuTreeHeader *header)
1032 {
1033 return get_parent ((MateMenuTreeItem *)header);
1034 }
1035
1036 /**
1037 * matemenu_tree_separator_get_parent:
1038 * @separator: a #MateMenuTreeSeparator
1039 *
1040 * Returns: (transfer full): The parent directory, or %NULL if none
1041 */
1042 MateMenuTreeDirectory *
matemenu_tree_separator_get_parent(MateMenuTreeSeparator * separator)1043 matemenu_tree_separator_get_parent (MateMenuTreeSeparator *separator)
1044 {
1045 return get_parent ((MateMenuTreeItem *)separator);
1046 }
1047
1048 static void
matemenu_tree_item_set_parent(MateMenuTreeItem * item,MateMenuTreeDirectory * parent)1049 matemenu_tree_item_set_parent (MateMenuTreeItem *item,
1050 MateMenuTreeDirectory *parent)
1051 {
1052 g_return_if_fail (item != NULL);
1053
1054 item->parent = parent;
1055 }
1056
1057 /**
1058 * matemenu_tree_iter_ref: (skip)
1059 * @iter: iter
1060 *
1061 * Increment the reference count of @iter
1062 */
1063 MateMenuTreeIter *
matemenu_tree_iter_ref(MateMenuTreeIter * iter)1064 matemenu_tree_iter_ref (MateMenuTreeIter *iter)
1065 {
1066 g_atomic_int_inc (&iter->refcount);
1067 return iter;
1068 }
1069
1070 /**
1071 * matemenu_tree_iter_unref: (skip)
1072 * @iter: iter
1073 *
1074 * Decrement the reference count of @iter
1075 */
1076 void
matemenu_tree_iter_unref(MateMenuTreeIter * iter)1077 matemenu_tree_iter_unref (MateMenuTreeIter *iter)
1078 {
1079 if (!g_atomic_int_dec_and_test (&iter->refcount))
1080 return;
1081
1082 g_slist_foreach (iter->contents, (GFunc)matemenu_tree_item_unref, NULL);
1083 g_slist_free (iter->contents);
1084
1085 g_slice_free (MateMenuTreeIter, iter);
1086 }
1087
1088 /**
1089 * matemenu_tree_directory_iter:
1090 * @directory: directory
1091 *
1092 * Returns: (transfer full): A new iterator over the directory contents
1093 */
1094 MateMenuTreeIter *
matemenu_tree_directory_iter(MateMenuTreeDirectory * directory)1095 matemenu_tree_directory_iter (MateMenuTreeDirectory *directory)
1096 {
1097 MateMenuTreeIter *iter;
1098
1099 g_return_val_if_fail (directory != NULL, NULL);
1100
1101 iter = g_slice_new0 (MateMenuTreeIter);
1102 iter->refcount = 1;
1103
1104 iter->contents = g_slist_copy (directory->contents);
1105 iter->contents_iter = iter->contents;
1106 g_slist_foreach (iter->contents, (GFunc) matemenu_tree_item_ref, NULL);
1107
1108 return iter;
1109 }
1110
1111 /**
1112 * matemenu_tree_iter_next:
1113 * @iter: iter
1114 *
1115 * Change the iterator to the next item, and return its type. If
1116 * there are no more items, %MATEMENU_TREE_ITEM_INVALID is returned.
1117 *
1118 * Returns: The type of the next item that can be retrived from the iterator
1119 */
1120 MateMenuTreeItemType
matemenu_tree_iter_next(MateMenuTreeIter * iter)1121 matemenu_tree_iter_next (MateMenuTreeIter *iter)
1122 {
1123 g_return_val_if_fail (iter != NULL, MATEMENU_TREE_ITEM_INVALID);
1124
1125 if (iter->contents_iter)
1126 {
1127 iter->item = iter->contents_iter->data;
1128 iter->contents_iter = iter->contents_iter->next;
1129 return iter->item->type;
1130 }
1131 else
1132 return MATEMENU_TREE_ITEM_INVALID;
1133 }
1134
1135 /**
1136 * matemenu_tree_iter_get_directory:
1137 * @iter: iter
1138 *
1139 * This method may only be called if matemenu_tree_iter_next()
1140 * returned MATEMENU_TREE_ITEM_DIRECTORY.
1141 *
1142 * Returns: (transfer full): A directory
1143 */
1144 MateMenuTreeDirectory *
matemenu_tree_iter_get_directory(MateMenuTreeIter * iter)1145 matemenu_tree_iter_get_directory (MateMenuTreeIter *iter)
1146 {
1147 g_return_val_if_fail (iter != NULL, NULL);
1148 g_return_val_if_fail (iter->item != NULL, NULL);
1149 g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_DIRECTORY, NULL);
1150
1151 return (MateMenuTreeDirectory*)matemenu_tree_item_ref (iter->item);
1152 }
1153
1154 /**
1155 * matemenu_tree_iter_get_entry:
1156 * @iter: iter
1157 *
1158 * This method may only be called if matemenu_tree_iter_next()
1159 * returned MATEMENU_TREE_ITEM_ENTRY.
1160 *
1161 * Returns: (transfer full): An entry
1162 */
1163 MateMenuTreeEntry *
matemenu_tree_iter_get_entry(MateMenuTreeIter * iter)1164 matemenu_tree_iter_get_entry (MateMenuTreeIter *iter)
1165 {
1166 g_return_val_if_fail (iter != NULL, NULL);
1167 g_return_val_if_fail (iter->item != NULL, NULL);
1168 g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_ENTRY, NULL);
1169
1170 return (MateMenuTreeEntry*)matemenu_tree_item_ref (iter->item);
1171 }
1172
1173 /**
1174 * matemenu_tree_iter_get_header:
1175 * @iter: iter
1176 *
1177 * This method may only be called if matemenu_tree_iter_next()
1178 * returned MATEMENU_TREE_ITEM_HEADER.
1179 *
1180 * Returns: (transfer full): A header
1181 */
1182 MateMenuTreeHeader *
matemenu_tree_iter_get_header(MateMenuTreeIter * iter)1183 matemenu_tree_iter_get_header (MateMenuTreeIter *iter)
1184 {
1185 g_return_val_if_fail (iter != NULL, NULL);
1186 g_return_val_if_fail (iter->item != NULL, NULL);
1187 g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_HEADER, NULL);
1188
1189 return (MateMenuTreeHeader*)matemenu_tree_item_ref (iter->item);
1190 }
1191
1192 /**
1193 * matemenu_tree_iter_get_alias:
1194 * @iter: iter
1195 *
1196 * This method may only be called if matemenu_tree_iter_next()
1197 * returned MATEMENU_TREE_ITEM_ALIAS.
1198 *
1199 * Returns: (transfer full): An alias
1200 */
1201 MateMenuTreeAlias *
matemenu_tree_iter_get_alias(MateMenuTreeIter * iter)1202 matemenu_tree_iter_get_alias (MateMenuTreeIter *iter)
1203 {
1204 g_return_val_if_fail (iter != NULL, NULL);
1205 g_return_val_if_fail (iter->item != NULL, NULL);
1206 g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_ALIAS, NULL);
1207
1208 return (MateMenuTreeAlias*)matemenu_tree_item_ref (iter->item);
1209 }
1210
1211 /**
1212 * matemenu_tree_iter_get_separator:
1213 * @iter: iter
1214 *
1215 * This method may only be called if matemenu_tree_iter_next()
1216 * returned #MATEMENU_TREE_ITEM_SEPARATOR.
1217 *
1218 * Returns: (transfer full): A separator
1219 */
1220 MateMenuTreeSeparator *
matemenu_tree_iter_get_separator(MateMenuTreeIter * iter)1221 matemenu_tree_iter_get_separator (MateMenuTreeIter *iter)
1222 {
1223 g_return_val_if_fail (iter != NULL, NULL);
1224 g_return_val_if_fail (iter->item != NULL, NULL);
1225 g_return_val_if_fail (iter->item->type == MATEMENU_TREE_ITEM_SEPARATOR, NULL);
1226
1227 return (MateMenuTreeSeparator*)matemenu_tree_item_ref (iter->item);
1228 }
1229
1230 const char *
matemenu_tree_directory_get_name(MateMenuTreeDirectory * directory)1231 matemenu_tree_directory_get_name (MateMenuTreeDirectory *directory)
1232 {
1233 g_return_val_if_fail (directory != NULL, NULL);
1234
1235 if (!directory->directory_entry)
1236 return directory->name;
1237
1238 return desktop_entry_get_name (directory->directory_entry);
1239 }
1240
1241 const char *
matemenu_tree_directory_get_generic_name(MateMenuTreeDirectory * directory)1242 matemenu_tree_directory_get_generic_name (MateMenuTreeDirectory *directory)
1243 {
1244 g_return_val_if_fail (directory != NULL, NULL);
1245
1246 if (!directory->directory_entry)
1247 return NULL;
1248
1249 return desktop_entry_get_generic_name (directory->directory_entry);
1250 }
1251
1252 const char *
matemenu_tree_directory_get_comment(MateMenuTreeDirectory * directory)1253 matemenu_tree_directory_get_comment (MateMenuTreeDirectory *directory)
1254 {
1255 g_return_val_if_fail (directory != NULL, NULL);
1256
1257 if (!directory->directory_entry)
1258 return NULL;
1259
1260 return desktop_entry_get_comment (directory->directory_entry);
1261 }
1262
1263 /**
1264 * matemenu_tree_directory_get_icon:
1265 * @directory: a #MateMenuTreeDirectory
1266 *
1267 * Gets the icon for the directory.
1268 *
1269 * Returns: (transfer none): The #GIcon for this directory
1270 */
1271 GIcon *
matemenu_tree_directory_get_icon(MateMenuTreeDirectory * directory)1272 matemenu_tree_directory_get_icon (MateMenuTreeDirectory *directory)
1273 {
1274 g_return_val_if_fail(directory != NULL, NULL);
1275
1276 if (!directory->directory_entry)
1277 return NULL;
1278
1279 return desktop_entry_get_icon(directory->directory_entry);
1280 }
1281
1282 const char *
matemenu_tree_directory_get_desktop_file_path(MateMenuTreeDirectory * directory)1283 matemenu_tree_directory_get_desktop_file_path (MateMenuTreeDirectory *directory)
1284 {
1285 g_return_val_if_fail (directory != NULL, NULL);
1286
1287 if (!directory->directory_entry)
1288 return NULL;
1289
1290 return desktop_entry_get_path (directory->directory_entry);
1291 }
1292
1293 const char *
matemenu_tree_directory_get_menu_id(MateMenuTreeDirectory * directory)1294 matemenu_tree_directory_get_menu_id (MateMenuTreeDirectory *directory)
1295 {
1296 g_return_val_if_fail (directory != NULL, NULL);
1297
1298 return directory->name;
1299 }
1300
1301 gboolean
matemenu_tree_directory_get_is_nodisplay(MateMenuTreeDirectory * directory)1302 matemenu_tree_directory_get_is_nodisplay (MateMenuTreeDirectory *directory)
1303 {
1304 g_return_val_if_fail (directory != NULL, FALSE);
1305
1306 return directory->is_nodisplay;
1307 }
1308
1309 /**
1310 * matemenu_tree_directory_get_tree:
1311 * @directory: A #MateMenuTreeDirectory
1312 *
1313 * Grab the tree associated with a #MateMenuTreeItem.
1314 *
1315 * Returns: (transfer full): The #MateMenuTree
1316 */
1317 MateMenuTree *
matemenu_tree_directory_get_tree(MateMenuTreeDirectory * directory)1318 matemenu_tree_directory_get_tree (MateMenuTreeDirectory *directory)
1319 {
1320 g_return_val_if_fail (directory != NULL, NULL);
1321
1322 return g_object_ref (directory->item.tree);
1323 }
1324
1325 static void
append_directory_path(MateMenuTreeDirectory * directory,GString * path)1326 append_directory_path (MateMenuTreeDirectory *directory,
1327 GString *path)
1328 {
1329
1330 if (!directory->item.parent)
1331 {
1332 g_string_append_c (path, G_DIR_SEPARATOR);
1333 return;
1334 }
1335
1336 append_directory_path (directory->item.parent, path);
1337
1338 g_string_append (path, directory->name);
1339 g_string_append_c (path, G_DIR_SEPARATOR);
1340 }
1341
1342 char *
matemenu_tree_directory_make_path(MateMenuTreeDirectory * directory,MateMenuTreeEntry * entry)1343 matemenu_tree_directory_make_path (MateMenuTreeDirectory *directory,
1344 MateMenuTreeEntry *entry)
1345 {
1346 GString *path;
1347
1348 g_return_val_if_fail (directory != NULL, NULL);
1349
1350 path = g_string_new (NULL);
1351
1352 append_directory_path (directory, path);
1353
1354 if (entry != NULL)
1355 g_string_append (path,
1356 desktop_entry_get_basename (entry->desktop_entry));
1357
1358 return g_string_free (path, FALSE);
1359 }
1360
1361 /**
1362 * matemenu_tree_entry_get_app_info:
1363 * @entry: a #MateMenuTreeEntry
1364 *
1365 * Returns: (transfer none): The #GDesktopAppInfo for this entry
1366 */
1367 GDesktopAppInfo *
matemenu_tree_entry_get_app_info(MateMenuTreeEntry * entry)1368 matemenu_tree_entry_get_app_info (MateMenuTreeEntry *entry)
1369 {
1370 g_return_val_if_fail (entry != NULL, NULL);
1371
1372 return desktop_entry_get_app_info (entry->desktop_entry);
1373 }
1374
1375 const char *
matemenu_tree_entry_get_desktop_file_path(MateMenuTreeEntry * entry)1376 matemenu_tree_entry_get_desktop_file_path (MateMenuTreeEntry *entry)
1377 {
1378 g_return_val_if_fail (entry != NULL, NULL);
1379
1380 return desktop_entry_get_path (entry->desktop_entry);
1381 }
1382
1383 const char *
matemenu_tree_entry_get_desktop_file_id(MateMenuTreeEntry * entry)1384 matemenu_tree_entry_get_desktop_file_id (MateMenuTreeEntry *entry)
1385 {
1386 g_return_val_if_fail (entry != NULL, NULL);
1387
1388 return entry->desktop_file_id;
1389 }
1390
1391 gboolean
matemenu_tree_entry_get_is_nodisplay_recurse(MateMenuTreeEntry * entry)1392 matemenu_tree_entry_get_is_nodisplay_recurse (MateMenuTreeEntry *entry)
1393 {
1394 MateMenuTreeDirectory *directory;
1395 GDesktopAppInfo *app_info;
1396
1397 g_return_val_if_fail (entry != NULL, FALSE);
1398
1399 app_info = matemenu_tree_entry_get_app_info (entry);
1400
1401 if (g_desktop_app_info_get_nodisplay (app_info))
1402 return TRUE;
1403
1404 directory = entry->item.parent;
1405 while (directory != NULL)
1406 {
1407 if (directory->is_nodisplay)
1408 return TRUE;
1409
1410 directory = directory->item.parent;
1411 }
1412
1413 return FALSE;
1414 }
1415
1416 gboolean
matemenu_tree_entry_get_is_excluded(MateMenuTreeEntry * entry)1417 matemenu_tree_entry_get_is_excluded (MateMenuTreeEntry *entry)
1418 {
1419 g_return_val_if_fail(entry != NULL, FALSE);
1420
1421 return entry->is_excluded;
1422 }
1423
1424 gboolean
matemenu_tree_entry_get_is_unallocated(MateMenuTreeEntry * entry)1425 matemenu_tree_entry_get_is_unallocated (MateMenuTreeEntry *entry)
1426 {
1427 g_return_val_if_fail (entry != NULL, FALSE);
1428
1429 return entry->is_unallocated;
1430 }
1431
1432 /**
1433 * matemenu_tree_entry_get_tree:
1434 * @entry: A #MateMenuTreeEntry
1435 *
1436 * Grab the tree associated with a #MateMenuTreeEntry.
1437 *
1438 * Returns: (transfer full): The #MateMenuTree
1439 */
1440 MateMenuTree *
matemenu_tree_entry_get_tree(MateMenuTreeEntry * entry)1441 matemenu_tree_entry_get_tree (MateMenuTreeEntry *entry)
1442 {
1443 g_return_val_if_fail(entry != NULL, NULL);
1444
1445 return g_object_ref (entry->item.tree);
1446 }
1447
1448 MateMenuTreeDirectory *
matemenu_tree_header_get_directory(MateMenuTreeHeader * header)1449 matemenu_tree_header_get_directory (MateMenuTreeHeader *header)
1450 {
1451 g_return_val_if_fail (header != NULL, NULL);
1452
1453 return matemenu_tree_item_ref (header->directory);
1454 }
1455
1456 /**
1457 * matemenu_tree_header_get_tree:
1458 * @header: A #MateMenuTreeHeader
1459 *
1460 * Grab the tree associated with a #MateMenuTreeHeader.
1461 *
1462 * Returns: (transfer full): The #MateMenuTree
1463 */
1464 MateMenuTree *
matemenu_tree_header_get_tree(MateMenuTreeHeader * header)1465 matemenu_tree_header_get_tree (MateMenuTreeHeader *header)
1466 {
1467 g_return_val_if_fail (header != NULL, NULL);
1468
1469 return g_object_ref (header->item.tree);
1470 }
1471
1472 MateMenuTreeItemType
matemenu_tree_alias_get_aliased_item_type(MateMenuTreeAlias * alias)1473 matemenu_tree_alias_get_aliased_item_type (MateMenuTreeAlias *alias)
1474 {
1475 g_return_val_if_fail (alias != NULL, MATEMENU_TREE_ITEM_INVALID);
1476
1477 g_assert (alias->aliased_item != NULL);
1478 return alias->aliased_item->type;
1479 }
1480
matemenu_tree_alias_get_directory(MateMenuTreeAlias * alias)1481 MateMenuTreeDirectory* matemenu_tree_alias_get_directory(MateMenuTreeAlias* alias)
1482 {
1483 g_return_val_if_fail (alias != NULL, NULL);
1484
1485 return matemenu_tree_item_ref(alias->directory);
1486 }
1487
1488 /**
1489 * matemenu_tree_alias_get_tree:
1490 * @alias: A #MateMenuTreeAlias
1491 *
1492 * Grab the tree associated with a #MateMenuTreeAlias.
1493 *
1494 * Returns: (transfer full): The #MateMenuTree
1495 */
1496 MateMenuTree *
matemenu_tree_alias_get_tree(MateMenuTreeAlias * alias)1497 matemenu_tree_alias_get_tree (MateMenuTreeAlias *alias)
1498 {
1499 g_return_val_if_fail (alias != NULL, NULL);
1500
1501 return g_object_ref (alias->item.tree);
1502 }
1503
1504 /**
1505 * matemenu_tree_separator_get_tree:
1506 * @separator: A #MateMenuTreeSeparator
1507 *
1508 * Grab the tree associated with a #MateMenuTreeSeparator.
1509 *
1510 * Returns: (transfer full): The #MateMenuTree
1511 */
1512 MateMenuTree *
matemenu_tree_separator_get_tree(MateMenuTreeSeparator * separator)1513 matemenu_tree_separator_get_tree (MateMenuTreeSeparator *separator)
1514 {
1515 g_return_val_if_fail (separator != NULL, NULL);
1516
1517 return g_object_ref (separator->item.tree);
1518 }
1519
1520 /**
1521 * matemenu_tree_alias_get_aliased_directory:
1522 * @alias: alias
1523 *
1524 * Returns: (transfer full): The aliased directory entry
1525 */
1526 MateMenuTreeDirectory *
matemenu_tree_alias_get_aliased_directory(MateMenuTreeAlias * alias)1527 matemenu_tree_alias_get_aliased_directory (MateMenuTreeAlias *alias)
1528 {
1529 g_return_val_if_fail (alias != NULL, NULL);
1530 g_return_val_if_fail (alias->aliased_item->type == MATEMENU_TREE_ITEM_DIRECTORY, NULL);
1531
1532 return (MateMenuTreeDirectory *) matemenu_tree_item_ref (alias->aliased_item);
1533 }
1534
1535 /**
1536 * matemenu_tree_alias_get_aliased_entry:
1537 * @alias: alias
1538 *
1539 * Returns: (transfer full): The aliased entry
1540 */
1541 MateMenuTreeEntry *
matemenu_tree_alias_get_aliased_entry(MateMenuTreeAlias * alias)1542 matemenu_tree_alias_get_aliased_entry (MateMenuTreeAlias *alias)
1543 {
1544 g_return_val_if_fail (alias != NULL, NULL);
1545 g_return_val_if_fail (alias->aliased_item->type == MATEMENU_TREE_ITEM_ENTRY, NULL);
1546
1547 return (MateMenuTreeEntry *) matemenu_tree_item_ref (alias->aliased_item);
1548 }
1549
1550 static MateMenuTreeDirectory *
matemenu_tree_directory_new(MateMenuTree * tree,MateMenuTreeDirectory * parent,const char * name)1551 matemenu_tree_directory_new (MateMenuTree *tree,
1552 MateMenuTreeDirectory *parent,
1553 const char *name)
1554 {
1555 MateMenuTreeDirectory *retval;
1556
1557 retval = g_slice_new0 (MateMenuTreeDirectory);
1558
1559 retval->item.type = MATEMENU_TREE_ITEM_DIRECTORY;
1560 retval->item.parent = parent;
1561 retval->item.refcount = 1;
1562 retval->item.tree = tree;
1563
1564 retval->name = g_strdup (name);
1565 retval->directory_entry = NULL;
1566 retval->entries = NULL;
1567 retval->subdirs = NULL;
1568 retval->default_layout_info = NULL;
1569 retval->layout_info = NULL;
1570 retval->contents = NULL;
1571 retval->only_unallocated = FALSE;
1572 retval->is_nodisplay = FALSE;
1573 retval->layout_pending_separator = FALSE;
1574 retval->preprocessed = FALSE;
1575 retval->will_inline_header = G_MAXUINT16;
1576
1577 retval->default_layout_values.mask = MENU_LAYOUT_VALUES_NONE;
1578 retval->default_layout_values.show_empty = FALSE;
1579 retval->default_layout_values.inline_menus = FALSE;
1580 retval->default_layout_values.inline_limit = 4;
1581 retval->default_layout_values.inline_header = FALSE;
1582 retval->default_layout_values.inline_alias = FALSE;
1583
1584 return retval;
1585 }
1586
1587 static void
matemenu_tree_directory_finalize(MateMenuTreeDirectory * directory)1588 matemenu_tree_directory_finalize (MateMenuTreeDirectory *directory)
1589 {
1590 g_assert (directory->item.refcount == 0);
1591
1592 g_slist_foreach (directory->contents,
1593 (GFunc) matemenu_tree_item_unref_and_unset_parent,
1594 NULL);
1595 g_slist_free (directory->contents);
1596 directory->contents = NULL;
1597
1598 g_slist_foreach (directory->default_layout_info,
1599 (GFunc) menu_layout_node_unref,
1600 NULL);
1601 g_slist_free (directory->default_layout_info);
1602 directory->default_layout_info = NULL;
1603
1604 g_slist_foreach (directory->layout_info,
1605 (GFunc) menu_layout_node_unref,
1606 NULL);
1607 g_slist_free (directory->layout_info);
1608 directory->layout_info = NULL;
1609
1610 g_slist_foreach (directory->subdirs,
1611 (GFunc) matemenu_tree_item_unref_and_unset_parent,
1612 NULL);
1613 g_slist_free (directory->subdirs);
1614 directory->subdirs = NULL;
1615
1616 g_slist_foreach (directory->entries,
1617 (GFunc) matemenu_tree_item_unref_and_unset_parent,
1618 NULL);
1619 g_slist_free (directory->entries);
1620 directory->entries = NULL;
1621
1622 if (directory->directory_entry)
1623 desktop_entry_unref (directory->directory_entry);
1624 directory->directory_entry = NULL;
1625
1626 g_free (directory->name);
1627 directory->name = NULL;
1628
1629 g_slice_free (MateMenuTreeDirectory, directory);
1630 }
1631
1632 static MateMenuTreeSeparator *
matemenu_tree_separator_new(MateMenuTreeDirectory * parent)1633 matemenu_tree_separator_new (MateMenuTreeDirectory *parent)
1634 {
1635 MateMenuTreeSeparator *retval;
1636
1637 retval = g_slice_new0 (MateMenuTreeSeparator);
1638
1639 retval->item.type = MATEMENU_TREE_ITEM_SEPARATOR;
1640 retval->item.parent = parent;
1641 retval->item.refcount = 1;
1642 retval->item.tree = parent->item.tree;
1643
1644 return retval;
1645 }
1646
1647 static void
matemenu_tree_separator_finalize(MateMenuTreeSeparator * separator)1648 matemenu_tree_separator_finalize (MateMenuTreeSeparator *separator)
1649 {
1650 g_assert (separator->item.refcount == 0);
1651
1652 g_slice_free (MateMenuTreeSeparator, separator);
1653 }
1654
1655 static MateMenuTreeHeader *
matemenu_tree_header_new(MateMenuTreeDirectory * parent,MateMenuTreeDirectory * directory)1656 matemenu_tree_header_new (MateMenuTreeDirectory *parent,
1657 MateMenuTreeDirectory *directory)
1658 {
1659 MateMenuTreeHeader *retval;
1660
1661 retval = g_slice_new0 (MateMenuTreeHeader);
1662
1663 retval->item.type = MATEMENU_TREE_ITEM_HEADER;
1664 retval->item.parent = parent;
1665 retval->item.refcount = 1;
1666 retval->item.tree = parent->item.tree;
1667
1668 retval->directory = matemenu_tree_item_ref (directory);
1669
1670 matemenu_tree_item_set_parent (MATEMENU_TREE_ITEM (retval->directory), NULL);
1671
1672 return retval;
1673 }
1674
1675 static void
matemenu_tree_header_finalize(MateMenuTreeHeader * header)1676 matemenu_tree_header_finalize (MateMenuTreeHeader *header)
1677 {
1678 g_assert (header->item.refcount == 0);
1679
1680 if (header->directory != NULL)
1681 matemenu_tree_item_unref (header->directory);
1682 header->directory = NULL;
1683
1684 g_slice_free (MateMenuTreeHeader, header);
1685 }
1686
1687 static MateMenuTreeAlias *
matemenu_tree_alias_new(MateMenuTreeDirectory * parent,MateMenuTreeDirectory * directory,MateMenuTreeItem * item)1688 matemenu_tree_alias_new (MateMenuTreeDirectory *parent,
1689 MateMenuTreeDirectory *directory,
1690 MateMenuTreeItem *item)
1691 {
1692 MateMenuTreeAlias *retval;
1693
1694 retval = g_slice_new0 (MateMenuTreeAlias);
1695
1696 retval->item.type = MATEMENU_TREE_ITEM_ALIAS;
1697 retval->item.parent = parent;
1698 retval->item.refcount = 1;
1699 retval->item.tree = parent->item.tree;
1700
1701 retval->directory = matemenu_tree_item_ref (directory);
1702 if (item->type != MATEMENU_TREE_ITEM_ALIAS)
1703 retval->aliased_item = matemenu_tree_item_ref (item);
1704 else
1705 {
1706 MateMenuTreeAlias *alias = MATEMENU_TREE_ALIAS (item);
1707 retval->aliased_item = matemenu_tree_item_ref (alias->aliased_item);
1708 }
1709
1710 matemenu_tree_item_set_parent (MATEMENU_TREE_ITEM (retval->directory), NULL);
1711 matemenu_tree_item_set_parent (retval->aliased_item, NULL);
1712
1713 return retval;
1714 }
1715
1716 static void
matemenu_tree_alias_finalize(MateMenuTreeAlias * alias)1717 matemenu_tree_alias_finalize (MateMenuTreeAlias *alias)
1718 {
1719 g_assert (alias->item.refcount == 0);
1720
1721 if (alias->directory != NULL)
1722 matemenu_tree_item_unref (alias->directory);
1723 alias->directory = NULL;
1724
1725 if (alias->aliased_item != NULL)
1726 matemenu_tree_item_unref (alias->aliased_item);
1727 alias->aliased_item = NULL;
1728
1729 g_slice_free (MateMenuTreeAlias, alias);
1730 }
1731
1732 static MateMenuTreeEntry *
matemenu_tree_entry_new(MateMenuTreeDirectory * parent,DesktopEntry * desktop_entry,const char * desktop_file_id,gboolean is_excluded,gboolean is_unallocated)1733 matemenu_tree_entry_new (MateMenuTreeDirectory *parent,
1734 DesktopEntry *desktop_entry,
1735 const char *desktop_file_id,
1736 gboolean is_excluded,
1737 gboolean is_unallocated)
1738 {
1739 MateMenuTreeEntry *retval;
1740
1741 retval = g_slice_new0 (MateMenuTreeEntry);
1742
1743 retval->item.type = MATEMENU_TREE_ITEM_ENTRY;
1744 retval->item.parent = parent;
1745 retval->item.refcount = 1;
1746 retval->item.tree = parent->item.tree;
1747
1748 retval->desktop_entry = desktop_entry_ref (desktop_entry);
1749 retval->desktop_file_id = g_strdup (desktop_file_id);
1750 retval->is_excluded = is_excluded != FALSE;
1751 retval->is_unallocated = is_unallocated != FALSE;
1752
1753 return retval;
1754 }
1755
1756 static void
matemenu_tree_entry_finalize(MateMenuTreeEntry * entry)1757 matemenu_tree_entry_finalize (MateMenuTreeEntry *entry)
1758 {
1759 g_assert (entry->item.refcount == 0);
1760
1761 g_free (entry->desktop_file_id);
1762 entry->desktop_file_id = NULL;
1763
1764 if (entry->desktop_entry)
1765 desktop_entry_unref (entry->desktop_entry);
1766 entry->desktop_entry = NULL;
1767
1768 g_slice_free (MateMenuTreeEntry, entry);
1769 }
1770
1771 static int
matemenu_tree_entry_compare_by_id(MateMenuTreeItem * a,MateMenuTreeItem * b)1772 matemenu_tree_entry_compare_by_id (MateMenuTreeItem *a,
1773 MateMenuTreeItem *b)
1774 {
1775 if (a->type == MATEMENU_TREE_ITEM_ALIAS)
1776 a = MATEMENU_TREE_ALIAS (a)->aliased_item;
1777
1778 if (b->type == MATEMENU_TREE_ITEM_ALIAS)
1779 b = MATEMENU_TREE_ALIAS (b)->aliased_item;
1780
1781 return strcmp (MATEMENU_TREE_ENTRY (a)->desktop_file_id,
1782 MATEMENU_TREE_ENTRY (b)->desktop_file_id);
1783 }
1784
1785 /**
1786 * matemenu_tree_item_ref:
1787 * @item: a #MateMenuTreeItem
1788 *
1789 * Returns: (transfer full): The same @item, or %NULL if @item is not a valid #MateMenuTreeItem
1790 */
1791 gpointer
matemenu_tree_item_ref(gpointer itemp)1792 matemenu_tree_item_ref (gpointer itemp)
1793 {
1794 MateMenuTreeItem* item = (MateMenuTreeItem*) itemp;
1795
1796 g_return_val_if_fail(item != NULL, NULL);
1797 g_return_val_if_fail(item->refcount > 0, NULL);
1798
1799 g_atomic_int_inc (&item->refcount);
1800
1801 return item;
1802 }
1803
1804 void
matemenu_tree_item_unref(gpointer itemp)1805 matemenu_tree_item_unref (gpointer itemp)
1806 {
1807 MateMenuTreeItem *item;
1808
1809 item = (MateMenuTreeItem *) itemp;
1810
1811 g_return_if_fail (item != NULL);
1812 g_return_if_fail (item->refcount > 0);
1813
1814 if (g_atomic_int_dec_and_test (&(item->refcount)))
1815 {
1816 switch (item->type)
1817 {
1818 case MATEMENU_TREE_ITEM_DIRECTORY:
1819 matemenu_tree_directory_finalize (MATEMENU_TREE_DIRECTORY (item));
1820 break;
1821
1822 case MATEMENU_TREE_ITEM_ENTRY:
1823 matemenu_tree_entry_finalize (MATEMENU_TREE_ENTRY (item));
1824 break;
1825
1826 case MATEMENU_TREE_ITEM_SEPARATOR:
1827 matemenu_tree_separator_finalize (MATEMENU_TREE_SEPARATOR (item));
1828 break;
1829
1830 case MATEMENU_TREE_ITEM_HEADER:
1831 matemenu_tree_header_finalize (MATEMENU_TREE_HEADER (item));
1832 break;
1833
1834 case MATEMENU_TREE_ITEM_ALIAS:
1835 matemenu_tree_alias_finalize (MATEMENU_TREE_ALIAS (item));
1836 break;
1837
1838 default:
1839 g_assert_not_reached ();
1840 break;
1841 }
1842 }
1843 }
1844
1845 static void
matemenu_tree_item_unref_and_unset_parent(gpointer itemp)1846 matemenu_tree_item_unref_and_unset_parent (gpointer itemp)
1847 {
1848 MateMenuTreeItem *item;
1849
1850 item = (MateMenuTreeItem *) itemp;
1851
1852 g_return_if_fail (item != NULL);
1853
1854 matemenu_tree_item_set_parent (item, NULL);
1855 matemenu_tree_item_unref (item);
1856 }
1857
1858 static inline const char *
matemenu_tree_item_compare_get_name_helper(MateMenuTreeItem * item,MateMenuTreeFlags flags)1859 matemenu_tree_item_compare_get_name_helper (MateMenuTreeItem *item,
1860 MateMenuTreeFlags flags)
1861 {
1862 const char *name;
1863
1864 name = NULL;
1865
1866 switch (item->type)
1867 {
1868 case MATEMENU_TREE_ITEM_DIRECTORY:
1869 if (MATEMENU_TREE_DIRECTORY (item)->directory_entry)
1870 name = desktop_entry_get_name (MATEMENU_TREE_DIRECTORY (item)->directory_entry);
1871 else
1872 name = MATEMENU_TREE_DIRECTORY (item)->name;
1873 break;
1874
1875 case MATEMENU_TREE_ITEM_ENTRY:
1876 if (flags & MATEMENU_TREE_FLAGS_SORT_DISPLAY_NAME)
1877 name = g_app_info_get_display_name (G_APP_INFO (matemenu_tree_entry_get_app_info (MATEMENU_TREE_ENTRY (item))));
1878 else
1879 name = desktop_entry_get_name (MATEMENU_TREE_ENTRY (item)->desktop_entry);
1880 break;
1881
1882 case MATEMENU_TREE_ITEM_ALIAS:
1883 {
1884 MateMenuTreeItem *dir;
1885 dir = MATEMENU_TREE_ITEM (MATEMENU_TREE_ALIAS (item)->directory);
1886 name = matemenu_tree_item_compare_get_name_helper (dir, flags);
1887 }
1888 break;
1889
1890 case MATEMENU_TREE_ITEM_SEPARATOR:
1891 case MATEMENU_TREE_ITEM_HEADER:
1892 default:
1893 g_assert_not_reached ();
1894 break;
1895 }
1896
1897 return name;
1898 }
1899
1900 static int
matemenu_tree_item_compare(MateMenuTreeItem * a,MateMenuTreeItem * b,gpointer flags_p)1901 matemenu_tree_item_compare (MateMenuTreeItem *a,
1902 MateMenuTreeItem *b,
1903 gpointer flags_p)
1904 {
1905 const char *name_a;
1906 const char *name_b;
1907 MateMenuTreeFlags flags;
1908
1909 flags = GPOINTER_TO_INT (flags_p);
1910
1911 name_a = matemenu_tree_item_compare_get_name_helper (a, flags);
1912 name_b = matemenu_tree_item_compare_get_name_helper (b, flags);
1913
1914 return g_utf8_collate (name_a, name_b);
1915 }
1916
1917 static MenuLayoutNode *
find_menu_child(MenuLayoutNode * layout)1918 find_menu_child (MenuLayoutNode *layout)
1919 {
1920 MenuLayoutNode *child;
1921
1922 child = menu_layout_node_get_children (layout);
1923 while (child && menu_layout_node_get_type (child) != MENU_LAYOUT_NODE_MENU)
1924 child = menu_layout_node_get_next (child);
1925
1926 return child;
1927 }
1928
1929 static void
merge_resolved_children(MateMenuTree * tree,GHashTable * loaded_menu_files,MenuLayoutNode * where,MenuLayoutNode * from)1930 merge_resolved_children (MateMenuTree *tree,
1931 GHashTable *loaded_menu_files,
1932 MenuLayoutNode *where,
1933 MenuLayoutNode *from)
1934 {
1935 MenuLayoutNode *insert_after;
1936 MenuLayoutNode *menu_child;
1937 MenuLayoutNode *from_child;
1938
1939 matemenu_tree_resolve_files (tree, loaded_menu_files, from);
1940
1941 insert_after = where;
1942 g_assert (menu_layout_node_get_type (insert_after) != MENU_LAYOUT_NODE_ROOT);
1943 g_assert (menu_layout_node_get_parent (insert_after) != NULL);
1944
1945 /* skip root node */
1946 menu_child = find_menu_child (from);
1947 g_assert (menu_child != NULL);
1948 g_assert (menu_layout_node_get_type (menu_child) == MENU_LAYOUT_NODE_MENU);
1949
1950 /* merge children of toplevel <Menu> */
1951 from_child = menu_layout_node_get_children (menu_child);
1952 while (from_child != NULL)
1953 {
1954 MenuLayoutNode *next;
1955
1956 next = menu_layout_node_get_next (from_child);
1957
1958 menu_verbose ("Merging ");
1959 menu_debug_print_layout (from_child, FALSE);
1960 menu_verbose (" after ");
1961 menu_debug_print_layout (insert_after, FALSE);
1962
1963 switch (menu_layout_node_get_type (from_child))
1964 {
1965 case MENU_LAYOUT_NODE_NAME:
1966 menu_layout_node_unlink (from_child); /* delete this */
1967 break;
1968
1969 default:
1970 menu_layout_node_steal (from_child);
1971 menu_layout_node_insert_after (insert_after, from_child);
1972 menu_layout_node_unref (from_child);
1973
1974 insert_after = from_child;
1975 break;
1976 }
1977
1978 from_child = next;
1979 }
1980 }
1981
1982 static gboolean
load_merge_file(MateMenuTree * tree,GHashTable * loaded_menu_files,const char * filename,gboolean is_canonical,gboolean add_monitor,MenuLayoutNode * where)1983 load_merge_file (MateMenuTree *tree,
1984 GHashTable *loaded_menu_files,
1985 const char *filename,
1986 gboolean is_canonical,
1987 gboolean add_monitor,
1988 MenuLayoutNode *where)
1989 {
1990 MenuLayoutNode *to_merge;
1991 const char *canonical;
1992 char *freeme;
1993 gboolean retval;
1994
1995 freeme = NULL;
1996 retval = FALSE;
1997
1998 if (!is_canonical)
1999 {
2000 canonical = freeme = realpath (filename, NULL);
2001 if (canonical == NULL)
2002 {
2003 if (add_monitor)
2004 matemenu_tree_add_menu_file_monitor (tree,
2005 filename,
2006 MENU_FILE_MONITOR_NONEXISTENT_FILE);
2007
2008 menu_verbose ("Failed to canonicalize merge file path \"%s\": %s\n",
2009 filename, g_strerror (errno));
2010 goto out;
2011 }
2012 }
2013 else
2014 {
2015 canonical = filename;
2016 }
2017
2018 if (g_hash_table_lookup (loaded_menu_files, canonical) != NULL)
2019 {
2020 g_warning ("Not loading \"%s\": recursive loop detected in .menu files",
2021 canonical);
2022 retval = TRUE;
2023 goto out;
2024 }
2025
2026 menu_verbose ("Merging file \"%s\"\n", canonical);
2027
2028 to_merge = menu_layout_load (canonical, tree->non_prefixed_basename, NULL);
2029 if (to_merge == NULL)
2030 {
2031 menu_verbose ("No menu for file \"%s\" found when merging\n",
2032 canonical);
2033 goto out;
2034 }
2035
2036 retval = TRUE;
2037
2038 g_hash_table_insert (loaded_menu_files, (char *) canonical, GUINT_TO_POINTER (TRUE));
2039
2040 if (add_monitor)
2041 matemenu_tree_add_menu_file_monitor (tree,
2042 canonical,
2043 MENU_FILE_MONITOR_FILE);
2044
2045 merge_resolved_children (tree, loaded_menu_files, where, to_merge);
2046
2047 g_hash_table_remove (loaded_menu_files, canonical);
2048
2049 menu_layout_node_unref (to_merge);
2050
2051 out:
2052 if (freeme)
2053 g_free (freeme);
2054
2055 return retval;
2056 }
2057
2058 static gboolean
load_merge_file_with_config_dir(MateMenuTree * tree,GHashTable * loaded_menu_files,const char * menu_file,const char * config_dir,MenuLayoutNode * where)2059 load_merge_file_with_config_dir (MateMenuTree *tree,
2060 GHashTable *loaded_menu_files,
2061 const char *menu_file,
2062 const char *config_dir,
2063 MenuLayoutNode *where)
2064 {
2065 char *merge_file;
2066 gboolean loaded;
2067
2068 loaded = FALSE;
2069
2070 merge_file = g_build_filename (config_dir, "menus", menu_file, NULL);
2071
2072 if (load_merge_file (tree, loaded_menu_files, merge_file, FALSE, TRUE, where))
2073 loaded = TRUE;
2074
2075 g_free (merge_file);
2076
2077 return loaded;
2078 }
2079
2080 static gboolean
compare_basedir_to_config_dir(const char * canonical_basedir,const char * config_dir)2081 compare_basedir_to_config_dir (const char *canonical_basedir,
2082 const char *config_dir)
2083 {
2084 char *dirname;
2085 char *canonical_menus_dir;
2086 gboolean retval;
2087
2088 menu_verbose ("Checking to see if basedir '%s' is in '%s'\n",
2089 canonical_basedir, config_dir);
2090
2091 dirname = g_build_filename (config_dir, "menus", NULL);
2092
2093 retval = FALSE;
2094
2095 canonical_menus_dir = realpath (dirname, NULL);
2096 if (canonical_menus_dir != NULL &&
2097 strcmp (canonical_basedir, canonical_menus_dir) == 0)
2098 {
2099 retval = TRUE;
2100 }
2101
2102 g_free (canonical_menus_dir);
2103 g_free (dirname);
2104
2105 return retval;
2106 }
2107
2108 static gboolean
load_parent_merge_file_from_basename(MateMenuTree * tree,GHashTable * loaded_menu_files,MenuLayoutNode * layout,const char * menu_file,const char * canonical_basedir)2109 load_parent_merge_file_from_basename (MateMenuTree *tree,
2110 GHashTable *loaded_menu_files,
2111 MenuLayoutNode *layout,
2112 const char *menu_file,
2113 const char *canonical_basedir)
2114 {
2115 gboolean found_basedir;
2116 const char * const *system_config_dirs;
2117 int i;
2118
2119 /* We're not interested in menu files that are in directories which are not a
2120 * parent of the base directory of this menu file */
2121 found_basedir = compare_basedir_to_config_dir (canonical_basedir,
2122 g_get_user_config_dir ());
2123
2124 system_config_dirs = g_get_system_config_dirs ();
2125
2126 i = 0;
2127 while (system_config_dirs[i] != NULL)
2128 {
2129 if (!found_basedir)
2130 {
2131 found_basedir = compare_basedir_to_config_dir (canonical_basedir,
2132 system_config_dirs[i]);
2133 }
2134 else
2135 {
2136 menu_verbose ("Looking for parent menu file '%s' in '%s'\n",
2137 menu_file, system_config_dirs[i]);
2138
2139 if (load_merge_file_with_config_dir (tree,
2140 loaded_menu_files,
2141 menu_file,
2142 system_config_dirs[i],
2143 layout))
2144 {
2145 break;
2146 }
2147 }
2148
2149 ++i;
2150 }
2151
2152 return system_config_dirs[i] != NULL;
2153 }
2154
load_parent_merge_file(MateMenuTree * tree,GHashTable * loaded_menu_files,MenuLayoutNode * layout)2155 static gboolean load_parent_merge_file(MateMenuTree* tree, GHashTable* loaded_menu_files, MenuLayoutNode* layout)
2156 {
2157 MenuLayoutNode* root;
2158 const char* basedir;
2159 const char* menu_name;
2160 char* canonical_basedir;
2161 char* menu_file;
2162 gboolean found;
2163
2164 root = menu_layout_node_get_root(layout);
2165
2166 basedir = menu_layout_node_root_get_basedir(root);
2167 menu_name = menu_layout_node_root_get_name(root);
2168
2169 canonical_basedir = realpath (basedir, NULL);
2170
2171 if (canonical_basedir == NULL)
2172 {
2173 menu_verbose("Menu basedir '%s' no longer exists, not merging parent\n", basedir);
2174 return FALSE;
2175 }
2176
2177 found = FALSE;
2178 menu_file = g_strconcat(menu_name, ".menu", NULL);
2179
2180 if (strcmp(menu_file, "mate-applications.menu") == 0 && g_getenv("XDG_MENU_PREFIX"))
2181 {
2182 char* prefixed_basename;
2183 prefixed_basename = g_strdup_printf("%s%s", g_getenv("XDG_MENU_PREFIX"), menu_file);
2184 found = load_parent_merge_file_from_basename(tree, loaded_menu_files, layout, prefixed_basename, canonical_basedir);
2185 g_free(prefixed_basename);
2186 }
2187
2188 if (!found)
2189 {
2190 found = load_parent_merge_file_from_basename(tree, loaded_menu_files, layout, menu_file, canonical_basedir);
2191 }
2192
2193 g_free(menu_file);
2194 g_free(canonical_basedir);
2195
2196 return found;
2197 }
2198
2199 static void
load_merge_dir(MateMenuTree * tree,GHashTable * loaded_menu_files,const char * dirname,MenuLayoutNode * where)2200 load_merge_dir (MateMenuTree *tree,
2201 GHashTable *loaded_menu_files,
2202 const char *dirname,
2203 MenuLayoutNode *where)
2204 {
2205 GDir *dir;
2206 const char *menu_file;
2207
2208 menu_verbose ("Loading merge dir \"%s\"\n", dirname);
2209
2210 matemenu_tree_add_menu_file_monitor (tree,
2211 dirname,
2212 MENU_FILE_MONITOR_DIRECTORY);
2213
2214 if ((dir = g_dir_open (dirname, 0, NULL)) == NULL)
2215 return;
2216
2217 while ((menu_file = g_dir_read_name (dir)))
2218 {
2219 if (g_str_has_suffix (menu_file, ".menu"))
2220 {
2221 char *full_path;
2222
2223 full_path = g_build_filename (dirname, menu_file, NULL);
2224
2225 load_merge_file (tree, loaded_menu_files, full_path, TRUE, FALSE, where);
2226
2227 g_free (full_path);
2228 }
2229 }
2230
2231 g_dir_close (dir);
2232 }
2233
2234 static void
load_merge_dir_with_config_dir(MateMenuTree * tree,GHashTable * loaded_menu_files,const char * config_dir,const char * dirname,MenuLayoutNode * where)2235 load_merge_dir_with_config_dir (MateMenuTree *tree,
2236 GHashTable *loaded_menu_files,
2237 const char *config_dir,
2238 const char *dirname,
2239 MenuLayoutNode *where)
2240 {
2241 char *path;
2242
2243 path = g_build_filename (config_dir, "menus", dirname, NULL);
2244
2245 load_merge_dir (tree, loaded_menu_files, path, where);
2246
2247 g_free (path);
2248 }
2249
2250 static void
resolve_merge_file(MateMenuTree * tree,GHashTable * loaded_menu_files,MenuLayoutNode * layout)2251 resolve_merge_file (MateMenuTree *tree,
2252 GHashTable *loaded_menu_files,
2253 MenuLayoutNode *layout)
2254 {
2255 char *filename;
2256
2257 if (menu_layout_node_merge_file_get_type (layout) == MENU_MERGE_FILE_TYPE_PARENT)
2258 {
2259 if (load_parent_merge_file (tree, loaded_menu_files, layout))
2260 return;
2261 }
2262
2263 filename = menu_layout_node_get_content_as_path (layout);
2264 if (filename == NULL)
2265 {
2266 menu_verbose ("didn't get node content as a path, not merging file\n");
2267 }
2268 else
2269 {
2270 load_merge_file (tree, loaded_menu_files, filename, FALSE, TRUE, layout);
2271
2272 g_free (filename);
2273 }
2274
2275 /* remove the now-replaced node */
2276 menu_layout_node_unlink (layout);
2277 }
2278
2279 static void
resolve_merge_dir(MateMenuTree * tree,GHashTable * loaded_menu_files,MenuLayoutNode * layout)2280 resolve_merge_dir (MateMenuTree *tree,
2281 GHashTable *loaded_menu_files,
2282 MenuLayoutNode *layout)
2283 {
2284 char *path;
2285
2286 path = menu_layout_node_get_content_as_path (layout);
2287 if (path == NULL)
2288 {
2289 menu_verbose ("didn't get layout node content as a path, not merging dir\n");
2290 }
2291 else
2292 {
2293 load_merge_dir (tree, loaded_menu_files, path, layout);
2294
2295 g_free (path);
2296 }
2297
2298 /* remove the now-replaced node */
2299 menu_layout_node_unlink (layout);
2300 }
2301
2302 static MenuLayoutNode *
add_app_dir(MateMenuTree * tree,MenuLayoutNode * before,const char * data_dir)2303 add_app_dir (MateMenuTree *tree,
2304 MenuLayoutNode *before,
2305 const char *data_dir)
2306 {
2307 MenuLayoutNode *tmp;
2308 char *dirname;
2309
2310 tmp = menu_layout_node_new (MENU_LAYOUT_NODE_APP_DIR);
2311 dirname = g_build_filename (data_dir, "applications", NULL);
2312 menu_layout_node_set_content (tmp, dirname);
2313 menu_layout_node_insert_before (before, tmp);
2314 menu_layout_node_unref (before);
2315
2316 menu_verbose ("Adding <AppDir>%s</AppDir> in <DefaultAppDirs/>\n",
2317 dirname);
2318
2319 g_free (dirname);
2320
2321 return tmp;
2322 }
2323
2324 static void
resolve_default_app_dirs(MateMenuTree * tree,MenuLayoutNode * layout)2325 resolve_default_app_dirs (MateMenuTree *tree,
2326 MenuLayoutNode *layout)
2327 {
2328 MenuLayoutNode *before;
2329 const char * const *system_data_dirs;
2330 int i;
2331
2332 system_data_dirs = g_get_system_data_dirs ();
2333
2334 before = add_app_dir (tree,
2335 menu_layout_node_ref (layout),
2336 g_get_user_data_dir ());
2337
2338 i = 0;
2339 while (system_data_dirs[i] != NULL)
2340 {
2341 before = add_app_dir (tree, before, system_data_dirs[i]);
2342
2343 ++i;
2344 }
2345
2346 menu_layout_node_unref (before);
2347
2348 /* remove the now-replaced node */
2349 menu_layout_node_unlink (layout);
2350 }
2351
add_directory_dir(MateMenuTree * tree,MenuLayoutNode * before,const char * data_dir)2352 static MenuLayoutNode* add_directory_dir(MateMenuTree* tree, MenuLayoutNode* before, const char* data_dir)
2353 {
2354 MenuLayoutNode* tmp;
2355 char* dirname;
2356
2357 tmp = menu_layout_node_new(MENU_LAYOUT_NODE_DIRECTORY_DIR);
2358 dirname = g_build_filename(data_dir, "desktop-directories", NULL);
2359 menu_layout_node_set_content(tmp, dirname);
2360 menu_layout_node_insert_before(before, tmp);
2361 menu_layout_node_unref(before);
2362
2363 menu_verbose("Adding <DirectoryDir>%s</DirectoryDir> in <DefaultDirectoryDirs/>\n", dirname);
2364
2365 g_free(dirname);
2366
2367 return tmp;
2368 }
2369
2370 /* According to desktop spec, since our menu file is called 'mate-applications', our
2371 * merged menu folders need to be called 'mate-applications-merged'. We'll setup the folder
2372 * 'applications-merged' if it doesn't exist yet, and a symlink pointing to it in the
2373 * ~/.config/menus directory
2374 */
2375 static void
setup_merge_dir_symlink(void)2376 setup_merge_dir_symlink(void)
2377 {
2378 gchar *user_config = (gchar *) g_get_user_config_dir();
2379 gchar *merge_path = g_build_filename (user_config, "menus", "applications-merged", NULL);
2380 GFile *merge_file = g_file_new_for_path (merge_path);
2381 gchar *sym_path;
2382 GFile *sym_file;
2383
2384 g_file_make_directory_with_parents (merge_file, NULL, NULL);
2385
2386 sym_path = g_build_filename (user_config, "menus", "mate-applications-merged", NULL);
2387 sym_file = g_file_new_for_path (sym_path);
2388 if (!g_file_query_exists (sym_file, NULL)) {
2389 g_file_make_symbolic_link (sym_file, merge_path, NULL, NULL);
2390 }
2391
2392 g_free (merge_path);
2393 g_free (sym_path);
2394 g_object_unref (merge_file);
2395 g_object_unref (sym_file);
2396 }
2397
2398 static void
resolve_default_directory_dirs(MateMenuTree * tree,MenuLayoutNode * layout)2399 resolve_default_directory_dirs (MateMenuTree *tree,
2400 MenuLayoutNode *layout)
2401 {
2402 MenuLayoutNode *before;
2403 const char * const *system_data_dirs;
2404 int i;
2405
2406 system_data_dirs = g_get_system_data_dirs ();
2407
2408 before = add_directory_dir (tree,
2409 menu_layout_node_ref (layout),
2410 g_get_user_data_dir ());
2411
2412 i = 0;
2413 while (system_data_dirs[i] != NULL)
2414 {
2415 /* Parche para tomar las carpetas /mate/ */
2416 char* path = g_build_filename(system_data_dirs[i], "mate", NULL);
2417 before = add_directory_dir(tree, before, path);
2418 g_free(path);
2419 /* /fin parche */
2420 before = add_directory_dir (tree, before, system_data_dirs[i]);
2421
2422 ++i;
2423 }
2424
2425 menu_layout_node_unref (before);
2426
2427 /* remove the now-replaced node */
2428 menu_layout_node_unlink (layout);
2429 }
2430
2431 static void
resolve_default_merge_dirs(MateMenuTree * tree,GHashTable * loaded_menu_files,MenuLayoutNode * layout)2432 resolve_default_merge_dirs (MateMenuTree *tree,
2433 GHashTable *loaded_menu_files,
2434 MenuLayoutNode *layout)
2435 {
2436 MenuLayoutNode *root;
2437 const char *menu_name;
2438 char *merge_name;
2439 const char * const *system_config_dirs;
2440 int i;
2441
2442 setup_merge_dir_symlink();
2443
2444 root = menu_layout_node_get_root (layout);
2445 menu_name = menu_layout_node_root_get_name (root);
2446
2447 merge_name = g_strconcat (menu_name, "-merged", NULL);
2448
2449 system_config_dirs = g_get_system_config_dirs ();
2450
2451 /* Merge in reverse order */
2452 i = 0;
2453 while (system_config_dirs[i] != NULL) i++;
2454 while (i > 0)
2455 {
2456 i--;
2457 load_merge_dir_with_config_dir (tree,
2458 loaded_menu_files,
2459 system_config_dirs[i],
2460 merge_name,
2461 layout);
2462 }
2463
2464 load_merge_dir_with_config_dir (tree,
2465 loaded_menu_files,
2466 g_get_user_config_dir (),
2467 merge_name,
2468 layout);
2469
2470 g_free (merge_name);
2471
2472 /* remove the now-replaced node */
2473 menu_layout_node_unlink (layout);
2474 }
2475
2476 static void
add_filename_include(const char * desktop_file_id,DesktopEntry * entry,MenuLayoutNode * include)2477 add_filename_include (const char *desktop_file_id,
2478 DesktopEntry *entry,
2479 MenuLayoutNode *include)
2480 {
2481 if (!desktop_entry_has_categories (entry))
2482 {
2483 MenuLayoutNode *node;
2484
2485 node = menu_layout_node_new (MENU_LAYOUT_NODE_FILENAME);
2486 menu_layout_node_set_content (node, desktop_file_id);
2487
2488 menu_layout_node_append_child (include, node);
2489 menu_layout_node_unref (node);
2490 }
2491 }
2492
2493 static void
is_dot_directory(const char * basename,DesktopEntry * entry,gboolean * has_dot_directory)2494 is_dot_directory (const char *basename,
2495 DesktopEntry *entry,
2496 gboolean *has_dot_directory)
2497 {
2498 if (!strcmp (basename, ".directory"))
2499 *has_dot_directory = TRUE;
2500 }
2501
2502 static gboolean
add_menu_for_legacy_dir(MenuLayoutNode * parent,const char * legacy_dir,const char * relative_path,const char * legacy_prefix,const char * menu_name)2503 add_menu_for_legacy_dir (MenuLayoutNode *parent,
2504 const char *legacy_dir,
2505 const char *relative_path,
2506 const char *legacy_prefix,
2507 const char *menu_name)
2508 {
2509 EntryDirectory *ed;
2510 DesktopEntrySet *desktop_entries;
2511 DesktopEntrySet *directory_entries;
2512 GSList *subdirs;
2513 gboolean menu_added;
2514 gboolean has_dot_directory;
2515
2516 ed = entry_directory_new_legacy (DESKTOP_ENTRY_INVALID, legacy_dir, legacy_prefix);
2517 if (!ed)
2518 return FALSE;
2519
2520 subdirs = NULL;
2521 desktop_entries = desktop_entry_set_new ();
2522 directory_entries = desktop_entry_set_new ();
2523
2524 entry_directory_get_flat_contents (ed,
2525 desktop_entries,
2526 directory_entries,
2527 &subdirs);
2528 entry_directory_unref (ed);
2529
2530 has_dot_directory = FALSE;
2531 desktop_entry_set_foreach (directory_entries,
2532 (DesktopEntrySetForeachFunc) is_dot_directory,
2533 &has_dot_directory);
2534 desktop_entry_set_unref (directory_entries);
2535
2536 menu_added = FALSE;
2537 if (desktop_entry_set_get_count (desktop_entries) > 0 || subdirs)
2538 {
2539 MenuLayoutNode *menu;
2540 MenuLayoutNode *node;
2541 GString *subdir_path;
2542 GString *subdir_relative;
2543 GSList *tmp;
2544 size_t legacy_dir_len;
2545 size_t relative_path_len;
2546
2547 menu = menu_layout_node_new (MENU_LAYOUT_NODE_MENU);
2548 menu_layout_node_append_child (parent, menu);
2549
2550 menu_added = TRUE;
2551
2552 g_assert (menu_name != NULL);
2553
2554 node = menu_layout_node_new (MENU_LAYOUT_NODE_NAME);
2555 menu_layout_node_set_content (node, menu_name);
2556 menu_layout_node_append_child (menu, node);
2557 menu_layout_node_unref (node);
2558
2559 if (has_dot_directory)
2560 {
2561 node = menu_layout_node_new (MENU_LAYOUT_NODE_DIRECTORY);
2562 if (relative_path != NULL)
2563 {
2564 char *directory_entry_path;
2565
2566 directory_entry_path = g_strdup_printf ("%s/.directory", relative_path);
2567 menu_layout_node_set_content (node, directory_entry_path);
2568 g_free (directory_entry_path);
2569 }
2570 else
2571 {
2572 menu_layout_node_set_content (node, ".directory");
2573 }
2574 menu_layout_node_append_child (menu, node);
2575 menu_layout_node_unref (node);
2576 }
2577
2578 if (desktop_entry_set_get_count (desktop_entries) > 0)
2579 {
2580 MenuLayoutNode *include;
2581
2582 include = menu_layout_node_new (MENU_LAYOUT_NODE_INCLUDE);
2583 menu_layout_node_append_child (menu, include);
2584
2585 desktop_entry_set_foreach (desktop_entries,
2586 (DesktopEntrySetForeachFunc) add_filename_include,
2587 include);
2588
2589 menu_layout_node_unref (include);
2590 }
2591
2592 subdir_path = g_string_new (legacy_dir);
2593 legacy_dir_len = strlen (legacy_dir);
2594
2595 subdir_relative = g_string_new (relative_path);
2596 relative_path_len = relative_path ? strlen (relative_path) : 0;
2597
2598 tmp = subdirs;
2599 while (tmp != NULL)
2600 {
2601 const char *subdir = tmp->data;
2602
2603 g_string_append_c (subdir_path, G_DIR_SEPARATOR);
2604 g_string_append (subdir_path, subdir);
2605
2606 if (relative_path_len)
2607 {
2608 g_string_append_c (subdir_relative, G_DIR_SEPARATOR);
2609 }
2610 g_string_append (subdir_relative, subdir);
2611
2612 add_menu_for_legacy_dir (menu,
2613 subdir_path->str,
2614 subdir_relative->str,
2615 legacy_prefix,
2616 subdir);
2617
2618 g_string_truncate (subdir_relative, relative_path_len);
2619 g_string_truncate (subdir_path, legacy_dir_len);
2620
2621 tmp = tmp->next;
2622 }
2623
2624 g_string_free (subdir_path, TRUE);
2625 g_string_free (subdir_relative, TRUE);
2626
2627 menu_layout_node_unref (menu);
2628 }
2629
2630 desktop_entry_set_unref (desktop_entries);
2631
2632 g_slist_foreach (subdirs, (GFunc) g_free, NULL);
2633 g_slist_free (subdirs);
2634
2635 return menu_added;
2636 }
2637
2638 static void
resolve_legacy_dir(MateMenuTree * tree,GHashTable * loaded_menu_files,MenuLayoutNode * legacy)2639 resolve_legacy_dir (MateMenuTree *tree,
2640 GHashTable *loaded_menu_files,
2641 MenuLayoutNode *legacy)
2642 {
2643 MenuLayoutNode *to_merge;
2644 MenuLayoutNode *menu;
2645
2646 to_merge = menu_layout_node_new (MENU_LAYOUT_NODE_ROOT);
2647
2648 menu = menu_layout_node_get_parent (legacy);
2649 g_assert (menu_layout_node_get_type (menu) == MENU_LAYOUT_NODE_MENU);
2650
2651 if (add_menu_for_legacy_dir (to_merge,
2652 menu_layout_node_get_content (legacy),
2653 NULL,
2654 menu_layout_node_legacy_dir_get_prefix (legacy),
2655 menu_layout_node_menu_get_name (menu)))
2656 {
2657 merge_resolved_children (tree, loaded_menu_files, legacy, to_merge);
2658 }
2659
2660 menu_layout_node_unref (to_merge);
2661 }
2662
2663 static MenuLayoutNode *
add_legacy_dir(MateMenuTree * tree,GHashTable * loaded_menu_files,MenuLayoutNode * before,const char * data_dir)2664 add_legacy_dir (MateMenuTree *tree,
2665 GHashTable *loaded_menu_files,
2666 MenuLayoutNode *before,
2667 const char *data_dir)
2668 {
2669 MenuLayoutNode *legacy;
2670 char *dirname;
2671
2672 dirname = g_build_filename (data_dir, "applnk", NULL);
2673
2674 legacy = menu_layout_node_new (MENU_LAYOUT_NODE_LEGACY_DIR);
2675 menu_layout_node_set_content (legacy, dirname);
2676 menu_layout_node_legacy_dir_set_prefix (legacy, "kde");
2677 menu_layout_node_insert_before (before, legacy);
2678 menu_layout_node_unref (before);
2679
2680 menu_verbose ("Adding <LegacyDir>%s</LegacyDir> in <KDELegacyDirs/>\n",
2681 dirname);
2682
2683 resolve_legacy_dir (tree, loaded_menu_files, legacy);
2684
2685 g_free (dirname);
2686
2687 return legacy;
2688 }
2689
2690 static void
resolve_kde_legacy_dirs(MateMenuTree * tree,GHashTable * loaded_menu_files,MenuLayoutNode * layout)2691 resolve_kde_legacy_dirs (MateMenuTree *tree,
2692 GHashTable *loaded_menu_files,
2693 MenuLayoutNode *layout)
2694 {
2695 MenuLayoutNode *before;
2696 const char * const *system_data_dirs;
2697 int i;
2698
2699 system_data_dirs = g_get_system_data_dirs ();
2700
2701 before = add_legacy_dir (tree,
2702 loaded_menu_files,
2703 menu_layout_node_ref (layout),
2704 g_get_user_data_dir ());
2705
2706 i = 0;
2707 while (system_data_dirs[i] != NULL)
2708 {
2709 before = add_legacy_dir (tree, loaded_menu_files, before, system_data_dirs[i]);
2710
2711 ++i;
2712 }
2713
2714 menu_layout_node_unref (before);
2715
2716 /* remove the now-replaced node */
2717 menu_layout_node_unlink (layout);
2718 }
2719
2720 static void
matemenu_tree_resolve_files(MateMenuTree * tree,GHashTable * loaded_menu_files,MenuLayoutNode * layout)2721 matemenu_tree_resolve_files (MateMenuTree *tree,
2722 GHashTable *loaded_menu_files,
2723 MenuLayoutNode *layout)
2724 {
2725 MenuLayoutNode *child;
2726
2727 menu_verbose ("Resolving files in: ");
2728 menu_debug_print_layout (layout, TRUE);
2729
2730 switch (menu_layout_node_get_type (layout))
2731 {
2732 case MENU_LAYOUT_NODE_MERGE_FILE:
2733 resolve_merge_file (tree, loaded_menu_files, layout);
2734 break;
2735
2736 case MENU_LAYOUT_NODE_MERGE_DIR:
2737 resolve_merge_dir (tree, loaded_menu_files, layout);
2738 break;
2739
2740 case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
2741 resolve_default_app_dirs (tree, layout);
2742 break;
2743
2744 case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
2745 resolve_default_directory_dirs (tree, layout);
2746 break;
2747
2748 case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
2749 resolve_default_merge_dirs (tree, loaded_menu_files, layout);
2750 break;
2751
2752 case MENU_LAYOUT_NODE_LEGACY_DIR:
2753 resolve_legacy_dir (tree, loaded_menu_files, layout);
2754 break;
2755
2756 case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
2757 resolve_kde_legacy_dirs (tree, loaded_menu_files, layout);
2758 break;
2759
2760 case MENU_LAYOUT_NODE_PASSTHROUGH:
2761 /* Just get rid of these, we don't need the memory usage */
2762 menu_layout_node_unlink (layout);
2763 break;
2764
2765 default:
2766 /* Recurse */
2767 child = menu_layout_node_get_children (layout);
2768 while (child != NULL)
2769 {
2770 MenuLayoutNode *next = menu_layout_node_get_next (child);
2771
2772 matemenu_tree_resolve_files (tree, loaded_menu_files, child);
2773
2774 child = next;
2775 }
2776 break;
2777 }
2778 }
2779
2780 static void
move_children(MenuLayoutNode * from,MenuLayoutNode * to)2781 move_children (MenuLayoutNode *from,
2782 MenuLayoutNode *to)
2783 {
2784 MenuLayoutNode *from_child;
2785 MenuLayoutNode *insert_before;
2786
2787 insert_before = menu_layout_node_get_children (to);
2788 from_child = menu_layout_node_get_children (from);
2789
2790 while (from_child != NULL)
2791 {
2792 MenuLayoutNode *next;
2793
2794 next = menu_layout_node_get_next (from_child);
2795
2796 menu_layout_node_steal (from_child);
2797
2798 if (menu_layout_node_get_type (from_child) == MENU_LAYOUT_NODE_NAME)
2799 {
2800 ; /* just drop the Name in the old <Menu> */
2801 }
2802 else if (insert_before)
2803 {
2804 menu_layout_node_insert_before (insert_before, from_child);
2805 g_assert (menu_layout_node_get_next (from_child) == insert_before);
2806 }
2807 else
2808 {
2809 menu_layout_node_append_child (to, from_child);
2810 }
2811
2812 menu_layout_node_unref (from_child);
2813
2814 from_child = next;
2815 }
2816 }
2817
2818 static int
null_safe_strcmp(const char * a,const char * b)2819 null_safe_strcmp (const char *a,
2820 const char *b)
2821 {
2822 if (a == NULL && b == NULL)
2823 return 0;
2824 else if (a == NULL)
2825 return -1;
2826 else if (b == NULL)
2827 return 1;
2828 else
2829 return strcmp (a, b);
2830 }
2831
2832 static int
node_compare_func(const void * a,const void * b)2833 node_compare_func (const void *a,
2834 const void *b)
2835 {
2836 MenuLayoutNode *node_a = (MenuLayoutNode*) a;
2837 MenuLayoutNode *node_b = (MenuLayoutNode*) b;
2838 MenuLayoutNodeType t_a = menu_layout_node_get_type (node_a);
2839 MenuLayoutNodeType t_b = menu_layout_node_get_type (node_b);
2840
2841 if (t_a < t_b)
2842 return -1;
2843 else if (t_a > t_b)
2844 return 1;
2845 else
2846 {
2847 const char *c_a = menu_layout_node_get_content (node_a);
2848 const char *c_b = menu_layout_node_get_content (node_b);
2849
2850 return null_safe_strcmp (c_a, c_b);
2851 }
2852 }
2853
2854 static int
node_menu_compare_func(const void * a,const void * b)2855 node_menu_compare_func (const void *a,
2856 const void *b)
2857 {
2858 MenuLayoutNode *node_a = (MenuLayoutNode*) a;
2859 MenuLayoutNode *node_b = (MenuLayoutNode*) b;
2860 MenuLayoutNode *parent_a = menu_layout_node_get_parent (node_a);
2861 MenuLayoutNode *parent_b = menu_layout_node_get_parent (node_b);
2862
2863 if (parent_a < parent_b)
2864 return -1;
2865 else if (parent_a > parent_b)
2866 return 1;
2867 else
2868 return null_safe_strcmp (menu_layout_node_menu_get_name (node_a),
2869 menu_layout_node_menu_get_name (node_b));
2870 }
2871
2872 static void
matemenu_tree_strip_duplicate_children(MateMenuTree * tree,MenuLayoutNode * layout)2873 matemenu_tree_strip_duplicate_children (MateMenuTree *tree,
2874 MenuLayoutNode *layout)
2875 {
2876 MenuLayoutNode *child;
2877 GSList *simple_nodes;
2878 GSList *menu_layout_nodes;
2879 GSList *prev;
2880 GSList *tmp;
2881
2882 /* to strip dups, we find all the child nodes where
2883 * we want to kill dups, sort them,
2884 * then nuke the adjacent nodes that are equal
2885 */
2886
2887 simple_nodes = NULL;
2888 menu_layout_nodes = NULL;
2889
2890 child = menu_layout_node_get_children (layout);
2891 while (child != NULL)
2892 {
2893 switch (menu_layout_node_get_type (child))
2894 {
2895 /* These are dups if their content is the same */
2896 case MENU_LAYOUT_NODE_APP_DIR:
2897 case MENU_LAYOUT_NODE_DIRECTORY_DIR:
2898 case MENU_LAYOUT_NODE_DIRECTORY:
2899 simple_nodes = g_slist_prepend (simple_nodes, child);
2900 break;
2901
2902 /* These have to be merged in a more complicated way,
2903 * and then recursed
2904 */
2905 case MENU_LAYOUT_NODE_MENU:
2906 menu_layout_nodes = g_slist_prepend (menu_layout_nodes, child);
2907 break;
2908
2909 default:
2910 break;
2911 }
2912
2913 child = menu_layout_node_get_next (child);
2914 }
2915
2916 /* Note that the lists are all backward. So we want to keep
2917 * the items that are earlier in the list, because they were
2918 * later in the file
2919 */
2920
2921 /* stable sort the simple nodes */
2922 simple_nodes = g_slist_sort (simple_nodes,
2923 node_compare_func);
2924
2925 prev = NULL;
2926 tmp = simple_nodes;
2927 while (tmp != NULL)
2928 {
2929 GSList *next = tmp->next;
2930
2931 if (prev)
2932 {
2933 MenuLayoutNode *p = prev->data;
2934 MenuLayoutNode *n = tmp->data;
2935
2936 if (node_compare_func (p, n) == 0)
2937 {
2938 /* nuke it! */
2939 menu_layout_node_unlink (n);
2940 simple_nodes = g_slist_delete_link (simple_nodes, tmp);
2941 tmp = prev;
2942 }
2943 }
2944
2945 prev = tmp;
2946 tmp = next;
2947 }
2948
2949 g_slist_free (simple_nodes);
2950 simple_nodes = NULL;
2951
2952 /* stable sort the menu nodes (the sort includes the
2953 * parents of the nodes in the comparison). Remember
2954 * the list is backward.
2955 */
2956 menu_layout_nodes = g_slist_sort (menu_layout_nodes,
2957 node_menu_compare_func);
2958
2959 prev = NULL;
2960 tmp = menu_layout_nodes;
2961 while (tmp != NULL)
2962 {
2963 GSList *next = tmp->next;
2964
2965 if (prev)
2966 {
2967 MenuLayoutNode *p = prev->data;
2968 MenuLayoutNode *n = tmp->data;
2969
2970 if (node_menu_compare_func (p, n) == 0)
2971 {
2972 /* Move children of first menu to the start of second
2973 * menu and nuke the first menu
2974 */
2975 move_children (n, p);
2976 menu_layout_node_unlink (n);
2977 menu_layout_nodes = g_slist_delete_link (menu_layout_nodes, tmp);
2978 tmp = prev;
2979 }
2980 }
2981
2982 prev = tmp;
2983 tmp = next;
2984 }
2985
2986 g_slist_free (menu_layout_nodes);
2987 menu_layout_nodes = NULL;
2988
2989 /* Recursively clean up all children */
2990 child = menu_layout_node_get_children (layout);
2991 while (child != NULL)
2992 {
2993 if (menu_layout_node_get_type (child) == MENU_LAYOUT_NODE_MENU)
2994 matemenu_tree_strip_duplicate_children (tree, child);
2995
2996 child = menu_layout_node_get_next (child);
2997 }
2998 }
2999
3000 static MenuLayoutNode *
find_submenu(MenuLayoutNode * layout,const char * path,gboolean create_if_not_found)3001 find_submenu (MenuLayoutNode *layout,
3002 const char *path,
3003 gboolean create_if_not_found)
3004 {
3005 MenuLayoutNode *child;
3006 const char *slash;
3007 const char *next_path;
3008 char *name;
3009
3010 menu_verbose (" (splitting \"%s\")\n", path);
3011
3012 if (path[0] == '\0' || path[0] == G_DIR_SEPARATOR)
3013 return NULL;
3014
3015 slash = strchr (path, G_DIR_SEPARATOR);
3016 if (slash != NULL)
3017 {
3018 name = g_strndup (path, (gsize)(slash - path));
3019 next_path = slash + 1;
3020 if (*next_path == '\0')
3021 next_path = NULL;
3022 }
3023 else
3024 {
3025 name = g_strdup (path);
3026 next_path = NULL;
3027 }
3028
3029 child = menu_layout_node_get_children (layout);
3030 while (child != NULL)
3031 {
3032 switch (menu_layout_node_get_type (child))
3033 {
3034 case MENU_LAYOUT_NODE_MENU:
3035 {
3036 if (strcmp (name, menu_layout_node_menu_get_name (child)) == 0)
3037 {
3038 menu_verbose ("MenuNode %p found for path component \"%s\"\n",
3039 child, name);
3040
3041 g_free (name);
3042
3043 if (!next_path)
3044 {
3045 menu_verbose (" Found menu node %p parent is %p\n",
3046 child, layout);
3047 return child;
3048 }
3049
3050 return find_submenu (child, next_path, create_if_not_found);
3051 }
3052 }
3053 break;
3054
3055 default:
3056 break;
3057 }
3058
3059 child = menu_layout_node_get_next (child);
3060 }
3061
3062 if (create_if_not_found)
3063 {
3064 MenuLayoutNode *name_node;
3065
3066 child = menu_layout_node_new (MENU_LAYOUT_NODE_MENU);
3067 menu_layout_node_append_child (layout, child);
3068
3069 name_node = menu_layout_node_new (MENU_LAYOUT_NODE_NAME);
3070 menu_layout_node_set_content (name_node, name);
3071 menu_layout_node_append_child (child, name_node);
3072 menu_layout_node_unref (name_node);
3073
3074 menu_verbose (" Created menu node %p parent is %p\n",
3075 child, layout);
3076
3077 menu_layout_node_unref (child);
3078 g_free (name);
3079
3080 if (!next_path)
3081 return child;
3082
3083 return find_submenu (child, next_path, create_if_not_found);
3084 }
3085 else
3086 {
3087 g_free (name);
3088 return NULL;
3089 }
3090 }
3091
3092 /* To call this you first have to strip duplicate children once,
3093 * otherwise when you move a menu Foo to Bar then you may only
3094 * move one of Foo, not all the merged Foo.
3095 */
3096 static void
matemenu_tree_execute_moves(MateMenuTree * tree,MenuLayoutNode * layout,gboolean * need_remove_dups_p)3097 matemenu_tree_execute_moves (MateMenuTree *tree,
3098 MenuLayoutNode *layout,
3099 gboolean *need_remove_dups_p)
3100 {
3101 MenuLayoutNode *child;
3102 gboolean need_remove_dups;
3103 GSList *move_nodes;
3104 GSList *tmp;
3105
3106 need_remove_dups = FALSE;
3107
3108 move_nodes = NULL;
3109
3110 child = menu_layout_node_get_children (layout);
3111 while (child != NULL)
3112 {
3113 switch (menu_layout_node_get_type (child))
3114 {
3115 case MENU_LAYOUT_NODE_MENU:
3116 /* Recurse - we recurse first and process the current node
3117 * second, as the spec dictates.
3118 */
3119 matemenu_tree_execute_moves (tree, child, &need_remove_dups);
3120 break;
3121
3122 case MENU_LAYOUT_NODE_MOVE:
3123 move_nodes = g_slist_prepend (move_nodes, child);
3124 break;
3125
3126 default:
3127 break;
3128 }
3129
3130 child = menu_layout_node_get_next (child);
3131 }
3132
3133 /* We need to execute the move operations in the order that they appear */
3134 move_nodes = g_slist_reverse (move_nodes);
3135
3136 tmp = move_nodes;
3137 while (tmp != NULL)
3138 {
3139 MenuLayoutNode *move_node = tmp->data;
3140 MenuLayoutNode *old_node;
3141 GSList *next = tmp->next;
3142 const char *old;
3143 const char *new;
3144
3145 old = menu_layout_node_move_get_old (move_node);
3146 new = menu_layout_node_move_get_new (move_node);
3147 g_assert (old != NULL && new != NULL);
3148
3149 menu_verbose ("executing <Move> old = \"%s\" new = \"%s\"\n",
3150 old, new);
3151
3152 old_node = find_submenu (layout, old, FALSE);
3153 if (old_node != NULL)
3154 {
3155 MenuLayoutNode *new_node;
3156
3157 /* here we can create duplicates anywhere below the
3158 * node
3159 */
3160 need_remove_dups = TRUE;
3161
3162 /* look up new node creating it and its parents if
3163 * required
3164 */
3165 new_node = find_submenu (layout, new, TRUE);
3166 g_assert (new_node != NULL);
3167
3168 move_children (old_node, new_node);
3169
3170 menu_layout_node_unlink (old_node);
3171 }
3172
3173 menu_layout_node_unlink (move_node);
3174
3175 tmp = next;
3176 }
3177
3178 g_slist_free (move_nodes);
3179
3180 /* This oddness is to ensure we only remove dups once,
3181 * at the root, instead of recursing the tree over
3182 * and over.
3183 */
3184 if (need_remove_dups_p)
3185 *need_remove_dups_p = need_remove_dups;
3186 else if (need_remove_dups)
3187 matemenu_tree_strip_duplicate_children (tree, layout);
3188 }
3189
3190 static gboolean
matemenu_tree_load_layout(MateMenuTree * tree,GError ** error)3191 matemenu_tree_load_layout (MateMenuTree *tree,
3192 GError **error)
3193 {
3194 GHashTable *loaded_menu_files;
3195
3196 if (tree->layout)
3197 return TRUE;
3198
3199 if (!matemenu_tree_canonicalize_path (tree, error))
3200 return FALSE;
3201
3202 menu_verbose ("Loading menu layout from \"%s\"\n",
3203 tree->canonical_path);
3204
3205 tree->layout = menu_layout_load (tree->canonical_path,
3206 tree->non_prefixed_basename,
3207 error);
3208 if (!tree->layout)
3209 return FALSE;
3210
3211 loaded_menu_files = g_hash_table_new (g_str_hash, g_str_equal);
3212 g_hash_table_insert (loaded_menu_files, tree->canonical_path, GUINT_TO_POINTER (TRUE));
3213 matemenu_tree_resolve_files (tree, loaded_menu_files, tree->layout);
3214 g_hash_table_destroy (loaded_menu_files);
3215
3216 matemenu_tree_strip_duplicate_children (tree, tree->layout);
3217 matemenu_tree_execute_moves (tree, tree->layout, NULL);
3218
3219 return TRUE;
3220 }
3221
3222 static void
matemenu_tree_force_reload(MateMenuTree * tree)3223 matemenu_tree_force_reload (MateMenuTree *tree)
3224 {
3225 matemenu_tree_force_rebuild (tree);
3226
3227 if (tree->layout)
3228 menu_layout_node_unref (tree->layout);
3229 tree->layout = NULL;
3230 }
3231
3232 typedef struct
3233 {
3234 DesktopEntrySet *set;
3235 const char *category;
3236 } GetByCategoryForeachData;
3237
3238 static void
get_by_category_foreach(const char * file_id,DesktopEntry * entry,GetByCategoryForeachData * data)3239 get_by_category_foreach (const char *file_id,
3240 DesktopEntry *entry,
3241 GetByCategoryForeachData *data)
3242 {
3243 if (desktop_entry_has_category (entry, data->category))
3244 desktop_entry_set_add_entry (data->set, entry, file_id);
3245 }
3246
3247 static void
get_by_desktop_foreach(const char * file_id,DesktopEntry * entry,GetByCategoryForeachData * data)3248 get_by_desktop_foreach (const char *file_id,
3249 DesktopEntry *entry,
3250 GetByCategoryForeachData *data)
3251 {
3252 if (g_strcmp0 (file_id, data->category) == 0)
3253 desktop_entry_set_add_entry (data->set, entry, file_id);
3254 }
3255
3256 static void
get_by_category(DesktopEntrySet * entry_pool,DesktopEntrySet * set,const char * category)3257 get_by_category (DesktopEntrySet *entry_pool,
3258 DesktopEntrySet *set,
3259 const char *category)
3260 {
3261 GetByCategoryForeachData data;
3262
3263 data.set = set;
3264 data.category = category;
3265
3266 desktop_entry_set_foreach (entry_pool,
3267 (DesktopEntrySetForeachFunc) get_by_category_foreach,
3268 &data);
3269 }
3270
3271 static void
get_by_desktop(DesktopEntrySet * entry_pool,DesktopEntrySet * set,const char * desktop_name)3272 get_by_desktop (DesktopEntrySet *entry_pool,
3273 DesktopEntrySet *set,
3274 const char *desktop_name)
3275 {
3276 GetByCategoryForeachData data;
3277
3278 data.set = set;
3279 data.category = desktop_name;
3280
3281 desktop_entry_set_foreach (entry_pool,
3282 (DesktopEntrySetForeachFunc) get_by_desktop_foreach,
3283 &data);
3284 }
3285
3286 static DesktopEntrySet *
process_include_rules(MenuLayoutNode * layout,DesktopEntrySet * entry_pool)3287 process_include_rules (MenuLayoutNode *layout,
3288 DesktopEntrySet *entry_pool)
3289 {
3290 DesktopEntrySet *set = NULL;
3291
3292 switch (menu_layout_node_get_type (layout))
3293 {
3294 case MENU_LAYOUT_NODE_AND:
3295 {
3296 MenuLayoutNode *child;
3297
3298 menu_verbose ("Processing <And>\n");
3299
3300 child = menu_layout_node_get_children (layout);
3301 while (child != NULL)
3302 {
3303 DesktopEntrySet *child_set;
3304
3305 child_set = process_include_rules (child, entry_pool);
3306
3307 if (set == NULL)
3308 {
3309 set = child_set;
3310 }
3311 else
3312 {
3313 desktop_entry_set_intersection (set, child_set);
3314 desktop_entry_set_unref (child_set);
3315 }
3316
3317 /* as soon as we get empty results, we can bail,
3318 * because it's an AND
3319 */
3320 if (desktop_entry_set_get_count (set) == 0)
3321 break;
3322
3323 child = menu_layout_node_get_next (child);
3324 }
3325 menu_verbose ("Processed <And>\n");
3326 }
3327 break;
3328
3329 case MENU_LAYOUT_NODE_OR:
3330 {
3331 MenuLayoutNode *child;
3332
3333 menu_verbose ("Processing <Or>\n");
3334
3335 child = menu_layout_node_get_children (layout);
3336 while (child != NULL)
3337 {
3338 DesktopEntrySet *child_set;
3339
3340 child_set = process_include_rules (child, entry_pool);
3341
3342 if (set == NULL)
3343 {
3344 set = child_set;
3345 }
3346 else
3347 {
3348 desktop_entry_set_union (set, child_set);
3349 desktop_entry_set_unref (child_set);
3350 }
3351
3352 child = menu_layout_node_get_next (child);
3353 }
3354 menu_verbose ("Processed <Or>\n");
3355 }
3356 break;
3357
3358 case MENU_LAYOUT_NODE_NOT:
3359 {
3360 /* First get the OR of all the rules */
3361 MenuLayoutNode *child;
3362
3363 menu_verbose ("Processing <Not>\n");
3364
3365 child = menu_layout_node_get_children (layout);
3366 while (child != NULL)
3367 {
3368 DesktopEntrySet *child_set;
3369
3370 child_set = process_include_rules (child, entry_pool);
3371
3372 if (set == NULL)
3373 {
3374 set = child_set;
3375 }
3376 else
3377 {
3378 desktop_entry_set_union (set, child_set);
3379 desktop_entry_set_unref (child_set);
3380 }
3381
3382 child = menu_layout_node_get_next (child);
3383 }
3384
3385 if (set != NULL)
3386 {
3387 DesktopEntrySet *inverted;
3388
3389 /* Now invert the result */
3390 inverted = desktop_entry_set_new ();
3391 desktop_entry_set_union (inverted, entry_pool);
3392 desktop_entry_set_subtract (inverted, set);
3393 desktop_entry_set_unref (set);
3394 set = inverted;
3395 }
3396 menu_verbose ("Processed <Not>\n");
3397 }
3398 break;
3399
3400 case MENU_LAYOUT_NODE_ALL:
3401 menu_verbose ("Processing <All>\n");
3402 set = desktop_entry_set_new ();
3403 desktop_entry_set_union (set, entry_pool);
3404 menu_verbose ("Processed <All>\n");
3405 break;
3406
3407 case MENU_LAYOUT_NODE_FILENAME:
3408 {
3409 DesktopEntry *entry;
3410
3411 menu_verbose ("Processing <Filename>%s</Filename>\n",
3412 menu_layout_node_get_content (layout));
3413
3414 entry = desktop_entry_set_lookup (entry_pool,
3415 menu_layout_node_get_content (layout));
3416 if (entry != NULL)
3417 {
3418 set = desktop_entry_set_new ();
3419 desktop_entry_set_add_entry (set,
3420 entry,
3421 menu_layout_node_get_content (layout));
3422 }
3423 menu_verbose ("Processed <Filename>%s</Filename>\n",
3424 menu_layout_node_get_content (layout));
3425 }
3426 break;
3427
3428 case MENU_LAYOUT_NODE_CATEGORY:
3429 menu_verbose ("Processing <Category>%s</Category>\n",
3430 menu_layout_node_get_content (layout));
3431 set = desktop_entry_set_new ();
3432 get_by_category (entry_pool, set, menu_layout_node_get_content (layout));
3433 menu_verbose ("Processed <Category>%s</Category>\n",
3434 menu_layout_node_get_content (layout));
3435 break;
3436
3437 default:
3438 break;
3439 }
3440
3441 if (set == NULL)
3442 set = desktop_entry_set_new (); /* create an empty set */
3443
3444 menu_verbose ("Matched %u entries\n", desktop_entry_set_get_count (set));
3445
3446 return set;
3447 }
3448
3449 static void
collect_layout_info(MenuLayoutNode * layout,GSList ** layout_info)3450 collect_layout_info (MenuLayoutNode *layout,
3451 GSList **layout_info)
3452 {
3453 MenuLayoutNode *iter;
3454
3455 g_slist_foreach (*layout_info,
3456 (GFunc) menu_layout_node_unref,
3457 NULL);
3458 g_slist_free (*layout_info);
3459 *layout_info = NULL;
3460
3461 iter = menu_layout_node_get_children (layout);
3462 while (iter != NULL)
3463 {
3464 switch (menu_layout_node_get_type (iter))
3465 {
3466 case MENU_LAYOUT_NODE_MENUNAME:
3467 case MENU_LAYOUT_NODE_FILENAME:
3468 case MENU_LAYOUT_NODE_SEPARATOR:
3469 case MENU_LAYOUT_NODE_MERGE:
3470 *layout_info = g_slist_prepend (*layout_info,
3471 menu_layout_node_ref (iter));
3472 break;
3473
3474 default:
3475 break;
3476 }
3477
3478 iter = menu_layout_node_get_next (iter);
3479 }
3480
3481 *layout_info = g_slist_reverse (*layout_info);
3482 }
3483
3484 static void
entries_listify_foreach(const char * desktop_file_id,DesktopEntry * desktop_entry,MateMenuTreeDirectory * directory)3485 entries_listify_foreach (const char *desktop_file_id,
3486 DesktopEntry *desktop_entry,
3487 MateMenuTreeDirectory *directory)
3488 {
3489 directory->entries =
3490 g_slist_prepend (directory->entries,
3491 matemenu_tree_entry_new (directory,
3492 desktop_entry,
3493 desktop_file_id,
3494 FALSE,
3495 FALSE));
3496 }
3497
3498 static void
excluded_entries_listify_foreach(const char * desktop_file_id,DesktopEntry * desktop_entry,MateMenuTreeDirectory * directory)3499 excluded_entries_listify_foreach (const char *desktop_file_id,
3500 DesktopEntry *desktop_entry,
3501 MateMenuTreeDirectory *directory)
3502 {
3503 directory->entries =
3504 g_slist_prepend (directory->entries,
3505 matemenu_tree_entry_new (directory,
3506 desktop_entry,
3507 desktop_file_id,
3508 TRUE,
3509 FALSE));
3510 }
3511
3512 static void
unallocated_entries_listify_foreach(const char * desktop_file_id,DesktopEntry * desktop_entry,MateMenuTreeDirectory * directory)3513 unallocated_entries_listify_foreach (const char *desktop_file_id,
3514 DesktopEntry *desktop_entry,
3515 MateMenuTreeDirectory *directory)
3516 {
3517 directory->entries =
3518 g_slist_prepend (directory->entries,
3519 matemenu_tree_entry_new (directory,
3520 desktop_entry,
3521 desktop_file_id,
3522 FALSE,
3523 TRUE));
3524 }
3525
3526 static void
set_default_layout_values(MateMenuTreeDirectory * parent,MateMenuTreeDirectory * child)3527 set_default_layout_values (MateMenuTreeDirectory *parent,
3528 MateMenuTreeDirectory *child)
3529 {
3530 GSList *tmp;
3531
3532 /* if the child has a defined default layout, we don't want to override its
3533 * values. The parent might have a non-defined layout info (ie, no child of
3534 * the DefaultLayout node) but it doesn't meant the default layout values
3535 * (ie, DefaultLayout attributes) aren't different from the global defaults.
3536 */
3537 if (child->default_layout_info != NULL ||
3538 child->default_layout_values.mask != MENU_LAYOUT_VALUES_NONE)
3539 return;
3540
3541 child->default_layout_values = parent->default_layout_values;
3542
3543 tmp = child->subdirs;
3544 while (tmp != NULL)
3545 {
3546 MateMenuTreeDirectory *subdir = tmp->data;
3547
3548 set_default_layout_values (child, subdir);
3549
3550 tmp = tmp->next;
3551 }
3552 }
3553
3554 static MateMenuTreeDirectory *
process_layout(MateMenuTree * tree,MateMenuTreeDirectory * parent,MenuLayoutNode * layout,DesktopEntrySet * allocated)3555 process_layout (MateMenuTree *tree,
3556 MateMenuTreeDirectory *parent,
3557 MenuLayoutNode *layout,
3558 DesktopEntrySet *allocated)
3559 {
3560 MenuLayoutNode *layout_iter;
3561 MateMenuTreeDirectory *directory;
3562 DesktopEntrySet *entry_pool;
3563 DesktopEntrySet *entries;
3564 DesktopEntrySet *allocated_set;
3565 DesktopEntrySet *excluded_set;
3566 gboolean deleted;
3567 gboolean only_unallocated;
3568 GSList *tmp;
3569
3570 g_assert (menu_layout_node_get_type (layout) == MENU_LAYOUT_NODE_MENU);
3571 g_assert (menu_layout_node_menu_get_name (layout) != NULL);
3572
3573 directory = matemenu_tree_directory_new (tree, parent,
3574 menu_layout_node_menu_get_name (layout));
3575
3576 menu_verbose ("=== Menu name = %s ===\n", directory->name);
3577
3578
3579 deleted = FALSE;
3580 only_unallocated = FALSE;
3581
3582 entries = desktop_entry_set_new ();
3583 allocated_set = desktop_entry_set_new ();
3584
3585 if (tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_EXCLUDED)
3586 excluded_set = desktop_entry_set_new ();
3587 else
3588 excluded_set = NULL;
3589
3590 entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (layout));
3591
3592 layout_iter = menu_layout_node_get_children (layout);
3593 while (layout_iter != NULL)
3594 {
3595 switch (menu_layout_node_get_type (layout_iter))
3596 {
3597 case MENU_LAYOUT_NODE_MENU:
3598 /* recurse */
3599 {
3600 MateMenuTreeDirectory *child_dir;
3601
3602 menu_verbose ("Processing <Menu>\n");
3603
3604 child_dir = process_layout (tree,
3605 directory,
3606 layout_iter,
3607 allocated);
3608 if (child_dir)
3609 directory->subdirs = g_slist_prepend (directory->subdirs,
3610 child_dir);
3611
3612 menu_verbose ("Processed <Menu>\n");
3613 }
3614 break;
3615
3616 case MENU_LAYOUT_NODE_INCLUDE:
3617 {
3618 /* The match rule children of the <Include> are
3619 * independent (logical OR) so we can process each one by
3620 * itself
3621 */
3622 MenuLayoutNode *rule;
3623
3624 menu_verbose ("Processing <Include> (%u entries)\n",
3625 desktop_entry_set_get_count (entries));
3626
3627 rule = menu_layout_node_get_children (layout_iter);
3628 while (rule != NULL)
3629 {
3630 DesktopEntrySet *rule_set;
3631
3632 rule_set = process_include_rules (rule, entry_pool);
3633 if (rule_set != NULL)
3634 {
3635 desktop_entry_set_union (entries, rule_set);
3636 desktop_entry_set_union (allocated_set, rule_set);
3637 if (excluded_set != NULL)
3638 desktop_entry_set_subtract (excluded_set, rule_set);
3639 desktop_entry_set_unref (rule_set);
3640 }
3641
3642 rule = menu_layout_node_get_next (rule);
3643 }
3644
3645 menu_verbose ("Processed <Include> (%u entries)\n",
3646 desktop_entry_set_get_count (entries));
3647 }
3648 break;
3649
3650 case MENU_LAYOUT_NODE_EXCLUDE:
3651 {
3652 /* The match rule children of the <Exclude> are
3653 * independent (logical OR) so we can process each one by
3654 * itself
3655 */
3656 MenuLayoutNode *rule;
3657
3658 menu_verbose ("Processing <Exclude> (%u entries)\n",
3659 desktop_entry_set_get_count (entries));
3660
3661 rule = menu_layout_node_get_children (layout_iter);
3662 while (rule != NULL)
3663 {
3664 DesktopEntrySet *rule_set;
3665
3666 rule_set = process_include_rules (rule, entry_pool);
3667 if (rule_set != NULL)
3668 {
3669 if (excluded_set != NULL)
3670 desktop_entry_set_union (excluded_set, rule_set);
3671 desktop_entry_set_subtract (entries, rule_set);
3672 desktop_entry_set_unref (rule_set);
3673 }
3674
3675 rule = menu_layout_node_get_next (rule);
3676 }
3677
3678 menu_verbose ("Processed <Exclude> (%u entries)\n",
3679 desktop_entry_set_get_count (entries));
3680 }
3681 break;
3682
3683 case MENU_LAYOUT_NODE_DIRECTORY:
3684 {
3685 DesktopEntry *entry;
3686
3687 menu_verbose ("Processing <Directory>%s</Directory>\n",
3688 menu_layout_node_get_content (layout_iter));
3689
3690 /*
3691 * The last <Directory> to exist wins, so we always try overwriting
3692 */
3693 entry = entry_directory_list_get_directory (menu_layout_node_menu_get_directory_dirs (layout),
3694 menu_layout_node_get_content (layout_iter));
3695
3696 if (entry != NULL)
3697 {
3698 if (!desktop_entry_get_hidden (entry))
3699 {
3700 if (directory->directory_entry)
3701 desktop_entry_unref (directory->directory_entry);
3702 directory->directory_entry = entry; /* pass ref ownership */
3703 }
3704 else
3705 {
3706 desktop_entry_unref (entry);
3707 }
3708 }
3709
3710 menu_verbose ("Processed <Directory> new directory entry = %p (%s)\n",
3711 directory->directory_entry,
3712 directory->directory_entry? desktop_entry_get_path (directory->directory_entry) : "null");
3713 }
3714 break;
3715
3716 case MENU_LAYOUT_NODE_DELETED:
3717 menu_verbose ("Processed <Deleted/>\n");
3718 deleted = TRUE;
3719 break;
3720
3721 case MENU_LAYOUT_NODE_NOT_DELETED:
3722 menu_verbose ("Processed <NotDeleted/>\n");
3723 deleted = FALSE;
3724 break;
3725
3726 case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
3727 menu_verbose ("Processed <OnlyUnallocated/>\n");
3728 only_unallocated = TRUE;
3729 break;
3730
3731 case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
3732 menu_verbose ("Processed <NotOnlyUnallocated/>\n");
3733 only_unallocated = FALSE;
3734 break;
3735
3736 case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
3737 menu_layout_node_default_layout_get_values (layout_iter,
3738 &directory->default_layout_values);
3739 collect_layout_info (layout_iter, &directory->default_layout_info);
3740 menu_verbose ("Processed <DefaultLayout/>\n");
3741 break;
3742
3743 case MENU_LAYOUT_NODE_LAYOUT:
3744 collect_layout_info (layout_iter, &directory->layout_info);
3745 menu_verbose ("Processed <Layout/>\n");
3746 break;
3747
3748 default:
3749 break;
3750 }
3751
3752 layout_iter = menu_layout_node_get_next (layout_iter);
3753 }
3754
3755 desktop_entry_set_unref (entry_pool);
3756
3757 directory->only_unallocated = only_unallocated != FALSE;
3758
3759 if (!directory->only_unallocated)
3760 desktop_entry_set_union (allocated, allocated_set);
3761
3762 desktop_entry_set_unref (allocated_set);
3763
3764 if (directory->directory_entry)
3765 {
3766 if (desktop_entry_get_no_display (directory->directory_entry))
3767 {
3768 directory->is_nodisplay = TRUE;
3769
3770 if (!(tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY))
3771 {
3772 menu_verbose ("Not showing menu %s because NoDisplay=true\n",
3773 desktop_entry_get_name (directory->directory_entry));
3774 deleted = TRUE;
3775 }
3776 }
3777
3778 if (!desktop_entry_get_show_in (directory->directory_entry))
3779 {
3780 menu_verbose ("Not showing menu %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n",
3781 desktop_entry_get_name (directory->directory_entry));
3782 deleted = TRUE;
3783 }
3784 }
3785
3786 if (deleted)
3787 {
3788 if (excluded_set != NULL)
3789 desktop_entry_set_unref (excluded_set);
3790 desktop_entry_set_unref (entries);
3791 matemenu_tree_item_unref (directory);
3792 return NULL;
3793 }
3794 if (tree->collection_applet && !g_strcmp0 (directory->name, "Collection"))
3795 {
3796 guint i;
3797 for (i = 0; i < tree->collection_applet->len; i++)
3798 {
3799 const char *desktop_name = g_ptr_array_index (tree->collection_applet, i);
3800 get_by_desktop (entry_pool, entries, desktop_name);
3801 }
3802 }
3803
3804 desktop_entry_set_foreach (entries,
3805 (DesktopEntrySetForeachFunc) entries_listify_foreach,
3806 directory);
3807 desktop_entry_set_unref (entries);
3808
3809 if (excluded_set != NULL)
3810 {
3811 desktop_entry_set_foreach (excluded_set,
3812 (DesktopEntrySetForeachFunc) excluded_entries_listify_foreach,
3813 directory);
3814 desktop_entry_set_unref (excluded_set);
3815 }
3816
3817 tmp = directory->subdirs;
3818 while (tmp != NULL)
3819 {
3820 MateMenuTreeDirectory *subdir = tmp->data;
3821
3822 set_default_layout_values (directory, subdir);
3823
3824 tmp = tmp->next;
3825 }
3826
3827 tmp = directory->entries;
3828 while (tmp != NULL)
3829 {
3830 MateMenuTreeEntry *entry = tmp->data;
3831 GSList *next = tmp->next;
3832 gboolean delete = FALSE;
3833
3834 /* If adding a new condition to delete here, it has to be added to
3835 * get_still_unallocated_foreach() too */
3836
3837 if (desktop_entry_get_hidden (entry->desktop_entry))
3838 {
3839 menu_verbose ("Deleting %s because Hidden=true\n",
3840 desktop_entry_get_name (entry->desktop_entry));
3841 delete = TRUE;
3842 }
3843
3844 if (!(tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY) &&
3845 desktop_entry_get_no_display (entry->desktop_entry))
3846 {
3847 menu_verbose ("Deleting %s because NoDisplay=true\n",
3848 desktop_entry_get_name (entry->desktop_entry));
3849 delete = TRUE;
3850 }
3851
3852 if (!desktop_entry_get_show_in (entry->desktop_entry))
3853 {
3854 menu_verbose ("Deleting %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n",
3855 desktop_entry_get_name (entry->desktop_entry));
3856 delete = TRUE;
3857 }
3858
3859 /* No need to filter out based on TryExec since GDesktopAppInfo cannot
3860 * deal with .desktop files with a failed TryExec. */
3861
3862 if (delete)
3863 {
3864 directory->entries = g_slist_delete_link (directory->entries,
3865 tmp);
3866 matemenu_tree_item_unref_and_unset_parent (entry);
3867 }
3868
3869 tmp = next;
3870 }
3871
3872 g_assert (directory->name != NULL);
3873
3874 return directory;
3875 }
3876
3877 static void
process_only_unallocated(MateMenuTree * tree,MateMenuTreeDirectory * directory,DesktopEntrySet * allocated,DesktopEntrySet * unallocated_used)3878 process_only_unallocated (MateMenuTree *tree,
3879 MateMenuTreeDirectory *directory,
3880 DesktopEntrySet *allocated,
3881 DesktopEntrySet *unallocated_used)
3882 {
3883 GSList *tmp;
3884
3885 /* For any directory marked only_unallocated, we have to remove any
3886 * entries that were in fact allocated.
3887 */
3888
3889 if (directory->only_unallocated)
3890 {
3891 tmp = directory->entries;
3892 while (tmp != NULL)
3893 {
3894 MateMenuTreeEntry *entry = tmp->data;
3895 GSList *next = tmp->next;
3896
3897 if (desktop_entry_set_lookup (allocated, entry->desktop_file_id))
3898 {
3899 directory->entries = g_slist_delete_link (directory->entries,
3900 tmp);
3901 matemenu_tree_item_unref_and_unset_parent (entry);
3902 }
3903 else
3904 {
3905 desktop_entry_set_add_entry (unallocated_used, entry->desktop_entry, entry->desktop_file_id);
3906 }
3907
3908 tmp = next;
3909 }
3910 }
3911
3912 tmp = directory->subdirs;
3913 while (tmp != NULL)
3914 {
3915 MateMenuTreeDirectory *subdir = tmp->data;
3916
3917 process_only_unallocated (tree, subdir, allocated, unallocated_used);
3918
3919 tmp = tmp->next;
3920 }
3921 }
3922
3923 typedef struct
3924 {
3925 MateMenuTree *tree;
3926 DesktopEntrySet *allocated;
3927 DesktopEntrySet *unallocated_used;
3928 DesktopEntrySet *still_unallocated;
3929 } GetStillUnallocatedForeachData;
3930
3931 static void
get_still_unallocated_foreach(const char * file_id,DesktopEntry * entry,GetStillUnallocatedForeachData * data)3932 get_still_unallocated_foreach (const char *file_id,
3933 DesktopEntry *entry,
3934 GetStillUnallocatedForeachData *data)
3935 {
3936 if (desktop_entry_set_lookup (data->allocated, file_id))
3937 return;
3938
3939 if (desktop_entry_set_lookup (data->unallocated_used, file_id))
3940 return;
3941
3942 /* Same rules than at the end of process_layout() */
3943 if (desktop_entry_get_hidden (entry))
3944 return;
3945
3946 if (!(data->tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY) &&
3947 desktop_entry_get_no_display (entry))
3948 return;
3949
3950 if (!desktop_entry_get_show_in (entry))
3951 return;
3952
3953 desktop_entry_set_add_entry (data->still_unallocated, entry, file_id);
3954 }
3955
3956 static void preprocess_layout_info (MateMenuTree *tree,
3957 MateMenuTreeDirectory *directory);
3958
3959 static GSList *
get_layout_info(MateMenuTreeDirectory * directory,gboolean * is_default_layout)3960 get_layout_info (MateMenuTreeDirectory *directory,
3961 gboolean *is_default_layout)
3962 {
3963 MateMenuTreeDirectory *iter;
3964
3965 if (directory->layout_info != NULL)
3966 {
3967 if (is_default_layout)
3968 {
3969 *is_default_layout = FALSE;
3970 }
3971 return directory->layout_info;
3972 }
3973
3974 /* Even if there's no layout information at all, the result will be an
3975 * implicit default layout */
3976 if (is_default_layout)
3977 {
3978 *is_default_layout = TRUE;
3979 }
3980
3981 iter = directory;
3982 while (iter != NULL)
3983 {
3984 /* FIXME: this is broken: we might skip real parent in the
3985 * XML structure, that are hidden because of inlining. */
3986 if (iter->default_layout_info != NULL)
3987 {
3988 return iter->default_layout_info;
3989 }
3990
3991 iter = MATEMENU_TREE_ITEM (iter)->parent;
3992 }
3993
3994 return NULL;
3995 }
3996
3997 static void
get_values_with_defaults(MenuLayoutNode * node,MenuLayoutValues * layout_values,MenuLayoutValues * default_layout_values)3998 get_values_with_defaults (MenuLayoutNode *node,
3999 MenuLayoutValues *layout_values,
4000 MenuLayoutValues *default_layout_values)
4001 {
4002 menu_layout_node_menuname_get_values (node, layout_values);
4003
4004 if (!(layout_values->mask & MENU_LAYOUT_VALUES_SHOW_EMPTY))
4005 layout_values->show_empty = default_layout_values->show_empty;
4006
4007 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_MENUS))
4008 layout_values->inline_menus = default_layout_values->inline_menus;
4009
4010 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_LIMIT))
4011 layout_values->inline_limit = default_layout_values->inline_limit;
4012
4013 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_HEADER))
4014 layout_values->inline_header = default_layout_values->inline_header;
4015
4016 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_ALIAS))
4017 layout_values->inline_alias = default_layout_values->inline_alias;
4018 }
4019
4020 static guint
get_real_subdirs_len(MateMenuTreeDirectory * directory)4021 get_real_subdirs_len (MateMenuTreeDirectory *directory)
4022 {
4023 guint len;
4024 GSList *tmp;
4025
4026 len = 0;
4027
4028 tmp = directory->subdirs;
4029 while (tmp != NULL)
4030 {
4031 MateMenuTreeDirectory *subdir = tmp->data;
4032
4033 tmp = tmp->next;
4034
4035 if (subdir->will_inline_header != G_MAXUINT16)
4036 {
4037 len += get_real_subdirs_len (subdir) + g_slist_length (subdir->entries) + 1;
4038 }
4039 else
4040 len += 1;
4041 }
4042
4043 return len;
4044 }
4045
4046 static void
preprocess_layout_info_subdir_helper(MateMenuTree * tree,MateMenuTreeDirectory * directory,MateMenuTreeDirectory * subdir,MenuLayoutValues * layout_values,gboolean * contents_added,gboolean * should_remove)4047 preprocess_layout_info_subdir_helper (MateMenuTree *tree,
4048 MateMenuTreeDirectory *directory,
4049 MateMenuTreeDirectory *subdir,
4050 MenuLayoutValues *layout_values,
4051 gboolean *contents_added,
4052 gboolean *should_remove)
4053 {
4054 preprocess_layout_info (tree, subdir);
4055
4056 *should_remove = FALSE;
4057 *contents_added = FALSE;
4058
4059 if (subdir->subdirs == NULL && subdir->entries == NULL)
4060 {
4061 if (!(tree->flags & MATEMENU_TREE_FLAGS_SHOW_EMPTY) &&
4062 !layout_values->show_empty)
4063 {
4064 menu_verbose ("Not showing empty menu '%s'\n", subdir->name);
4065 *should_remove = TRUE;
4066 }
4067 }
4068
4069 else if (layout_values->inline_menus)
4070 {
4071 guint real_subdirs_len;
4072
4073 real_subdirs_len = get_real_subdirs_len (subdir);
4074
4075 if (layout_values->inline_alias &&
4076 real_subdirs_len + g_slist_length (subdir->entries) == 1)
4077 {
4078 MateMenuTreeAlias *alias;
4079 MateMenuTreeItem *item;
4080 GSList *list;
4081
4082 if (subdir->subdirs != NULL)
4083 list = subdir->subdirs;
4084 else
4085 list = subdir->entries;
4086
4087 item = MATEMENU_TREE_ITEM (list->data);
4088
4089 menu_verbose ("Inline aliasing '%s' to '%s'\n",
4090 item->type == MATEMENU_TREE_ITEM_ENTRY ?
4091 g_app_info_get_name (G_APP_INFO (matemenu_tree_entry_get_app_info (MATEMENU_TREE_ENTRY (item)))) :
4092 (item->type == MATEMENU_TREE_ITEM_DIRECTORY ?
4093 matemenu_tree_directory_get_name (MATEMENU_TREE_DIRECTORY (item)) :
4094 matemenu_tree_directory_get_name (MATEMENU_TREE_ALIAS (item)->directory)),
4095 subdir->name);
4096
4097 alias = matemenu_tree_alias_new (directory, subdir, item);
4098
4099 g_slist_foreach (list,
4100 (GFunc) matemenu_tree_item_unref_and_unset_parent,
4101 NULL);
4102 g_slist_free (list);
4103 subdir->subdirs = NULL;
4104 subdir->entries = NULL;
4105
4106 if (item->type == MATEMENU_TREE_ITEM_DIRECTORY)
4107 directory->subdirs = g_slist_append (directory->subdirs, alias);
4108 else
4109 directory->entries = g_slist_append (directory->entries, alias);
4110
4111 *contents_added = TRUE;
4112 *should_remove = TRUE;
4113 }
4114
4115 else if (layout_values->inline_limit == 0 ||
4116 layout_values->inline_limit >= real_subdirs_len + g_slist_length (subdir->entries))
4117 {
4118 if (layout_values->inline_header)
4119 {
4120 menu_verbose ("Creating inline header with name '%s'\n", subdir->name);
4121 /* we're limited to 16-bits to spare some memory; if the limit is
4122 * higher than that (would be crazy), we just consider it's
4123 * unlimited */
4124 if (layout_values->inline_limit < G_MAXUINT16)
4125 subdir->will_inline_header = (guint16) layout_values->inline_limit;
4126 else
4127 subdir->will_inline_header = 0;
4128 }
4129 else
4130 {
4131 g_slist_foreach (subdir->subdirs,
4132 (GFunc) matemenu_tree_item_set_parent,
4133 directory);
4134 directory->subdirs = g_slist_concat (directory->subdirs,
4135 subdir->subdirs);
4136 subdir->subdirs = NULL;
4137
4138 g_slist_foreach (subdir->entries,
4139 (GFunc) matemenu_tree_item_set_parent,
4140 directory);
4141 directory->entries = g_slist_concat (directory->entries,
4142 subdir->entries);
4143 subdir->entries = NULL;
4144
4145 *contents_added = TRUE;
4146 *should_remove = TRUE;
4147 }
4148
4149 menu_verbose ("Inlining directory contents of '%s' to '%s'\n",
4150 subdir->name, directory->name);
4151 }
4152 }
4153 }
4154
4155 static void
preprocess_layout_info(MateMenuTree * tree,MateMenuTreeDirectory * directory)4156 preprocess_layout_info (MateMenuTree *tree,
4157 MateMenuTreeDirectory *directory)
4158 {
4159 GSList *tmp;
4160 GSList *layout_info;
4161 gboolean using_default_layout;
4162 GSList *last_subdir;
4163 gboolean strip_duplicates;
4164 gboolean contents_added;
4165 gboolean should_remove;
4166 GSList *subdirs_sentinel;
4167
4168 /* Note: we need to preprocess all menus, even if the layout mask for a menu
4169 * is MENU_LAYOUT_VALUES_NONE: in this case, we need to remove empty menus;
4170 * and the layout mask can be different for a submenu anyway */
4171
4172 menu_verbose ("Processing menu layout inline hints for %s\n", directory->name);
4173 g_assert (!directory->preprocessed);
4174
4175 strip_duplicates = FALSE;
4176 /* we use last_subdir to track the last non-inlined subdirectory */
4177 last_subdir = g_slist_last (directory->subdirs);
4178
4179 /*
4180 * First process subdirectories with explicit layout
4181 */
4182 layout_info = get_layout_info (directory, &using_default_layout);
4183 tmp = layout_info;
4184 /* see comment below about Menuname to understand why we leave the loop if
4185 * last_subdir is NULL */
4186 while (tmp != NULL && last_subdir != NULL)
4187 {
4188 MenuLayoutNode *node = tmp->data;
4189 MenuLayoutValues layout_values;
4190 const char *name;
4191 MateMenuTreeDirectory *subdir;
4192 GSList *subdir_l;
4193
4194 tmp = tmp->next;
4195
4196 /* only Menuname nodes are relevant here */
4197 if (menu_layout_node_get_type (node) != MENU_LAYOUT_NODE_MENUNAME)
4198 continue;
4199
4200 get_values_with_defaults (node,
4201 &layout_values,
4202 &directory->default_layout_values);
4203
4204 /* find the subdirectory that is affected by those attributes */
4205 name = menu_layout_node_get_content (node);
4206 subdir = NULL;
4207 subdir_l = directory->subdirs;
4208 while (subdir_l != NULL)
4209 {
4210 subdir = subdir_l->data;
4211
4212 if (!strcmp (subdir->name, name))
4213 break;
4214
4215 subdir = NULL;
4216 subdir_l = subdir_l->next;
4217
4218 /* We do not want to use Menuname on a menu that appeared via
4219 * inlining: without inlining, the Menuname wouldn't have matched
4220 * anything, and we want to keep the same behavior.
4221 * Unless the layout is a default layout, in which case the Menuname
4222 * does match the subdirectory. */
4223 if (!using_default_layout && subdir_l == last_subdir)
4224 {
4225 subdir_l = NULL;
4226 break;
4227 }
4228 }
4229
4230 if (subdir == NULL)
4231 continue;
4232
4233 preprocess_layout_info_subdir_helper (tree, directory,
4234 subdir, &layout_values,
4235 &contents_added, &should_remove);
4236 strip_duplicates = strip_duplicates || contents_added;
4237 if (should_remove)
4238 {
4239 if (last_subdir == subdir_l)
4240 {
4241 /* we need to recompute last_subdir since we'll remove it from
4242 * the list */
4243 GSList *buf;
4244
4245 if (subdir_l == directory->subdirs)
4246 last_subdir = NULL;
4247 else
4248 {
4249 buf = directory->subdirs;
4250 while (buf != NULL && buf->next != subdir_l)
4251 buf = buf->next;
4252 last_subdir = buf;
4253 }
4254 }
4255
4256 directory->subdirs = g_slist_remove (directory->subdirs, subdir);
4257 matemenu_tree_item_unref_and_unset_parent (MATEMENU_TREE_ITEM (subdir));
4258 }
4259 }
4260
4261 /*
4262 * Now process the subdirectories with no explicit layout
4263 */
4264 /* this is bogus data, but we just need the pointer anyway */
4265 subdirs_sentinel = g_slist_prepend (directory->subdirs, PACKAGE);
4266 directory->subdirs = subdirs_sentinel;
4267
4268 tmp = directory->subdirs;
4269 while (tmp->next != NULL)
4270 {
4271 MateMenuTreeDirectory *subdir = tmp->next->data;
4272
4273 if (subdir->preprocessed)
4274 {
4275 tmp = tmp->next;
4276 continue;
4277 }
4278
4279 preprocess_layout_info_subdir_helper (tree, directory,
4280 subdir, &directory->default_layout_values,
4281 &contents_added, &should_remove);
4282 strip_duplicates = strip_duplicates || contents_added;
4283 if (should_remove)
4284 {
4285 tmp = g_slist_delete_link (tmp, tmp->next);
4286 matemenu_tree_item_unref_and_unset_parent (MATEMENU_TREE_ITEM (subdir));
4287 }
4288 else
4289 tmp = tmp->next;
4290 }
4291
4292 /* remove the sentinel */
4293 directory->subdirs = g_slist_delete_link (directory->subdirs,
4294 directory->subdirs);
4295
4296 /*
4297 * Finally, remove duplicates if needed
4298 */
4299 if (strip_duplicates)
4300 {
4301 /* strip duplicate entries; there should be no duplicate directories */
4302 directory->entries = g_slist_sort (directory->entries,
4303 (GCompareFunc) matemenu_tree_entry_compare_by_id);
4304 tmp = directory->entries;
4305 while (tmp != NULL && tmp->next != NULL)
4306 {
4307 MateMenuTreeItem *a = tmp->data;
4308 MateMenuTreeItem *b = tmp->next->data;
4309
4310 if (a->type == MATEMENU_TREE_ITEM_ALIAS)
4311 a = MATEMENU_TREE_ALIAS (a)->aliased_item;
4312
4313 if (b->type == MATEMENU_TREE_ITEM_ALIAS)
4314 b = MATEMENU_TREE_ALIAS (b)->aliased_item;
4315
4316 if (strcmp (MATEMENU_TREE_ENTRY (a)->desktop_file_id,
4317 MATEMENU_TREE_ENTRY (b)->desktop_file_id) == 0)
4318 {
4319 tmp = g_slist_delete_link (tmp, tmp->next);
4320 matemenu_tree_item_unref (b);
4321 }
4322 else
4323 tmp = tmp->next;
4324 }
4325 }
4326
4327 directory->preprocessed = TRUE;
4328 }
4329
4330 static void process_layout_info (MateMenuTree *tree,
4331 MateMenuTreeDirectory *directory);
4332
4333 static void
check_pending_separator(MateMenuTreeDirectory * directory)4334 check_pending_separator (MateMenuTreeDirectory *directory)
4335 {
4336 if (directory->layout_pending_separator)
4337 {
4338 menu_verbose ("Adding pending separator in '%s'\n", directory->name);
4339
4340 directory->contents = g_slist_append (directory->contents,
4341 matemenu_tree_separator_new (directory));
4342 directory->layout_pending_separator = FALSE;
4343 }
4344 }
4345
4346 static void
merge_alias(MateMenuTree * tree,MateMenuTreeDirectory * directory,MateMenuTreeAlias * alias)4347 merge_alias (MateMenuTree *tree,
4348 MateMenuTreeDirectory *directory,
4349 MateMenuTreeAlias *alias)
4350 {
4351 menu_verbose ("Merging alias '%s' in directory '%s'\n",
4352 alias->directory->name, directory->name);
4353
4354 if (alias->aliased_item->type == MATEMENU_TREE_ITEM_DIRECTORY)
4355 {
4356 process_layout_info (tree, MATEMENU_TREE_DIRECTORY (alias->aliased_item));
4357 }
4358
4359 check_pending_separator (directory);
4360
4361 directory->contents = g_slist_append (directory->contents,
4362 matemenu_tree_item_ref (alias));
4363 }
4364
4365 static void
merge_subdir(MateMenuTree * tree,MateMenuTreeDirectory * directory,MateMenuTreeDirectory * subdir)4366 merge_subdir (MateMenuTree *tree,
4367 MateMenuTreeDirectory *directory,
4368 MateMenuTreeDirectory *subdir)
4369 {
4370 menu_verbose ("Merging subdir '%s' in directory '%s'\n",
4371 subdir->name, directory->name);
4372
4373 process_layout_info (tree, subdir);
4374
4375 check_pending_separator (directory);
4376
4377 if (subdir->will_inline_header == 0 ||
4378 (subdir->will_inline_header != G_MAXUINT16 &&
4379 g_slist_length (subdir->contents) <= subdir->will_inline_header))
4380 {
4381 MateMenuTreeHeader *header;
4382
4383 header = matemenu_tree_header_new (directory, subdir);
4384 directory->contents = g_slist_append (directory->contents, header);
4385
4386 g_slist_foreach (subdir->contents,
4387 (GFunc) matemenu_tree_item_set_parent,
4388 directory);
4389 directory->contents = g_slist_concat (directory->contents,
4390 subdir->contents);
4391 subdir->contents = NULL;
4392 subdir->will_inline_header = G_MAXUINT16;
4393
4394 matemenu_tree_item_set_parent (MATEMENU_TREE_ITEM (subdir), NULL);
4395 }
4396 else
4397 {
4398 directory->contents = g_slist_append (directory->contents,
4399 matemenu_tree_item_ref (subdir));
4400 }
4401 }
4402
4403 static void
merge_subdir_by_name(MateMenuTree * tree,MateMenuTreeDirectory * directory,const char * subdir_name)4404 merge_subdir_by_name (MateMenuTree *tree,
4405 MateMenuTreeDirectory *directory,
4406 const char *subdir_name)
4407 {
4408 GSList *tmp;
4409
4410 menu_verbose ("Attempting to merge subdir '%s' in directory '%s'\n",
4411 subdir_name, directory->name);
4412
4413 tmp = directory->subdirs;
4414 while (tmp != NULL)
4415 {
4416 MateMenuTreeDirectory *subdir = tmp->data;
4417 GSList *next = tmp->next;
4418
4419 /* if it's an alias, then it cannot be affected by
4420 * the Merge nodes in the layout */
4421 if (MATEMENU_TREE_ITEM (subdir)->type == MATEMENU_TREE_ITEM_ALIAS)
4422 continue;
4423
4424 if (!strcmp (subdir->name, subdir_name))
4425 {
4426 directory->subdirs = g_slist_delete_link (directory->subdirs, tmp);
4427 merge_subdir (tree, directory, subdir);
4428 matemenu_tree_item_unref (subdir);
4429 }
4430
4431 tmp = next;
4432 }
4433 }
4434
4435 static void
merge_entry(MateMenuTree * tree,MateMenuTreeDirectory * directory,MateMenuTreeEntry * entry)4436 merge_entry (MateMenuTree *tree,
4437 MateMenuTreeDirectory *directory,
4438 MateMenuTreeEntry *entry)
4439 {
4440 menu_verbose ("Merging entry '%s' in directory '%s'\n",
4441 entry->desktop_file_id, directory->name);
4442
4443 check_pending_separator (directory);
4444 directory->contents = g_slist_append (directory->contents,
4445 matemenu_tree_item_ref (entry));
4446 }
4447
4448 static void
merge_entry_by_id(MateMenuTree * tree,MateMenuTreeDirectory * directory,const char * file_id)4449 merge_entry_by_id (MateMenuTree *tree,
4450 MateMenuTreeDirectory *directory,
4451 const char *file_id)
4452 {
4453 GSList *tmp;
4454
4455 menu_verbose ("Attempting to merge entry '%s' in directory '%s'\n",
4456 file_id, directory->name);
4457
4458 tmp = directory->entries;
4459 while (tmp != NULL)
4460 {
4461 MateMenuTreeEntry *entry = tmp->data;
4462 GSList *next = tmp->next;
4463
4464 /* if it's an alias, then it cannot be affected by
4465 * the Merge nodes in the layout */
4466 if (MATEMENU_TREE_ITEM (entry)->type == MATEMENU_TREE_ITEM_ALIAS)
4467 continue;
4468
4469 if (!strcmp (entry->desktop_file_id, file_id))
4470 {
4471 directory->entries = g_slist_delete_link (directory->entries, tmp);
4472 merge_entry (tree, directory, entry);
4473 matemenu_tree_item_unref (entry);
4474 }
4475
4476 tmp = next;
4477 }
4478 }
4479
4480 static inline gboolean
find_name_in_list(const char * name,GSList * list)4481 find_name_in_list (const char *name,
4482 GSList *list)
4483 {
4484 while (list != NULL)
4485 {
4486 if (!strcmp (name, list->data))
4487 return TRUE;
4488
4489 list = list->next;
4490 }
4491
4492 return FALSE;
4493 }
4494
4495 static void
merge_subdirs(MateMenuTree * tree,MateMenuTreeDirectory * directory,GSList * except)4496 merge_subdirs (MateMenuTree *tree,
4497 MateMenuTreeDirectory *directory,
4498 GSList *except)
4499 {
4500 GSList *subdirs;
4501 GSList *tmp;
4502
4503 menu_verbose ("Merging subdirs in directory '%s'\n", directory->name);
4504
4505 subdirs = directory->subdirs;
4506 directory->subdirs = NULL;
4507
4508 subdirs = g_slist_sort_with_data (subdirs,
4509 (GCompareDataFunc) matemenu_tree_item_compare,
4510 GINT_TO_POINTER (MATEMENU_TREE_FLAGS_NONE));
4511
4512 tmp = subdirs;
4513 while (tmp != NULL)
4514 {
4515 MateMenuTreeDirectory *subdir = tmp->data;
4516
4517 if (MATEMENU_TREE_ITEM (subdir)->type == MATEMENU_TREE_ITEM_ALIAS)
4518 {
4519 merge_alias (tree, directory, MATEMENU_TREE_ALIAS (subdir));
4520 matemenu_tree_item_unref (subdir);
4521 }
4522 else if (!find_name_in_list (subdir->name, except))
4523 {
4524 merge_subdir (tree, directory, subdir);
4525 matemenu_tree_item_unref (subdir);
4526 }
4527 else
4528 {
4529 menu_verbose ("Not merging directory '%s' yet\n", subdir->name);
4530 directory->subdirs = g_slist_append (directory->subdirs, subdir);
4531 }
4532
4533 tmp = tmp->next;
4534 }
4535
4536 g_slist_free (subdirs);
4537 g_slist_free (except);
4538 }
4539
4540 static void
merge_entries(MateMenuTree * tree,MateMenuTreeDirectory * directory,GSList * except)4541 merge_entries (MateMenuTree *tree,
4542 MateMenuTreeDirectory *directory,
4543 GSList *except)
4544 {
4545 GSList *entries;
4546 GSList *tmp;
4547
4548 menu_verbose ("Merging entries in directory '%s'\n", directory->name);
4549
4550 entries = directory->entries;
4551 directory->entries = NULL;
4552
4553 entries = g_slist_sort_with_data (entries,
4554 (GCompareDataFunc) matemenu_tree_item_compare,
4555 GINT_TO_POINTER (tree->flags));
4556
4557 tmp = entries;
4558 while (tmp != NULL)
4559 {
4560 MateMenuTreeEntry *entry = tmp->data;
4561
4562 if (MATEMENU_TREE_ITEM (entry)->type == MATEMENU_TREE_ITEM_ALIAS)
4563 {
4564 merge_alias (tree, directory, MATEMENU_TREE_ALIAS (entry));
4565 matemenu_tree_item_unref (entry);
4566 }
4567 else if (!find_name_in_list (entry->desktop_file_id, except))
4568 {
4569 merge_entry (tree, directory, entry);
4570 matemenu_tree_item_unref (entry);
4571 }
4572 else
4573 {
4574 menu_verbose ("Not merging entry '%s' yet\n", entry->desktop_file_id);
4575 directory->entries = g_slist_append (directory->entries, entry);
4576 }
4577
4578 tmp = tmp->next;
4579 }
4580
4581 g_slist_free (entries);
4582 g_slist_free (except);
4583 }
4584
4585 static void
merge_subdirs_and_entries(MateMenuTree * tree,MateMenuTreeDirectory * directory,GSList * except_subdirs,GSList * except_entries)4586 merge_subdirs_and_entries (MateMenuTree *tree,
4587 MateMenuTreeDirectory *directory,
4588 GSList *except_subdirs,
4589 GSList *except_entries)
4590 {
4591 GSList *items;
4592 GSList *tmp;
4593
4594 menu_verbose ("Merging subdirs and entries together in directory %s\n",
4595 directory->name);
4596
4597 items = g_slist_concat (directory->subdirs, directory->entries);
4598
4599 directory->subdirs = NULL;
4600 directory->entries = NULL;
4601
4602 items = g_slist_sort_with_data (items,
4603 (GCompareDataFunc) matemenu_tree_item_compare,
4604 GINT_TO_POINTER (tree->flags));
4605
4606 tmp = items;
4607 while (tmp != NULL)
4608 {
4609 MateMenuTreeItem *item = tmp->data;
4610 MateMenuTreeItemType type;
4611
4612 type = item->type;
4613
4614 if (type == MATEMENU_TREE_ITEM_ALIAS)
4615 {
4616 merge_alias (tree, directory, MATEMENU_TREE_ALIAS (item));
4617 matemenu_tree_item_unref (item);
4618 }
4619 else if (type == MATEMENU_TREE_ITEM_DIRECTORY)
4620 {
4621 if (!find_name_in_list (MATEMENU_TREE_DIRECTORY (item)->name, except_subdirs))
4622 {
4623 merge_subdir (tree,
4624 directory,
4625 MATEMENU_TREE_DIRECTORY (item));
4626 matemenu_tree_item_unref (item);
4627 }
4628 else
4629 {
4630 menu_verbose ("Not merging directory '%s' yet\n",
4631 MATEMENU_TREE_DIRECTORY (item)->name);
4632 directory->subdirs = g_slist_append (directory->subdirs, item);
4633 }
4634 }
4635 else if (type == MATEMENU_TREE_ITEM_ENTRY)
4636 {
4637 if (!find_name_in_list (MATEMENU_TREE_ENTRY (item)->desktop_file_id, except_entries))
4638 {
4639 merge_entry (tree, directory, MATEMENU_TREE_ENTRY (item));
4640 matemenu_tree_item_unref (item);
4641 }
4642 else
4643 {
4644 menu_verbose ("Not merging entry '%s' yet\n",
4645 MATEMENU_TREE_ENTRY (item)->desktop_file_id);
4646 directory->entries = g_slist_append (directory->entries, item);
4647 }
4648 }
4649 else
4650 {
4651 g_assert_not_reached ();
4652 }
4653
4654 tmp = tmp->next;
4655 }
4656
4657 g_slist_free (items);
4658 g_slist_free (except_subdirs);
4659 g_slist_free (except_entries);
4660 }
4661
4662 static GSList *
get_subdirs_from_layout_info(GSList * layout_info)4663 get_subdirs_from_layout_info (GSList *layout_info)
4664 {
4665 GSList *subdirs;
4666 GSList *tmp;
4667
4668 subdirs = NULL;
4669
4670 tmp = layout_info;
4671 while (tmp != NULL)
4672 {
4673 MenuLayoutNode *node = tmp->data;
4674
4675 if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_MENUNAME)
4676 {
4677 subdirs = g_slist_append (subdirs,
4678 (char *) menu_layout_node_get_content (node));
4679 }
4680
4681 tmp = tmp->next;
4682 }
4683
4684 return subdirs;
4685 }
4686
4687 static GSList *
get_entries_from_layout_info(GSList * layout_info)4688 get_entries_from_layout_info (GSList *layout_info)
4689 {
4690 GSList *entries;
4691 GSList *tmp;
4692
4693 entries = NULL;
4694
4695 tmp = layout_info;
4696 while (tmp != NULL)
4697 {
4698 MenuLayoutNode *node = tmp->data;
4699
4700 if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_FILENAME)
4701 {
4702 entries = g_slist_append (entries,
4703 (char *) menu_layout_node_get_content (node));
4704 }
4705
4706 tmp = tmp->next;
4707 }
4708
4709 return entries;
4710 }
4711
4712 static void
process_layout_info(MateMenuTree * tree,MateMenuTreeDirectory * directory)4713 process_layout_info (MateMenuTree *tree,
4714 MateMenuTreeDirectory *directory)
4715 {
4716 GSList *layout_info;
4717
4718 menu_verbose ("Processing menu layout hints for %s\n", directory->name);
4719
4720 g_slist_foreach (directory->contents,
4721 (GFunc) matemenu_tree_item_unref_and_unset_parent,
4722 NULL);
4723 g_slist_free (directory->contents);
4724 directory->contents = NULL;
4725 directory->layout_pending_separator = FALSE;
4726
4727 layout_info = get_layout_info (directory, NULL);
4728
4729 if (layout_info == NULL)
4730 {
4731 merge_subdirs (tree, directory, NULL);
4732 merge_entries (tree, directory, NULL);
4733 }
4734 else
4735 {
4736 GSList *tmp;
4737
4738 tmp = layout_info;
4739 while (tmp != NULL)
4740 {
4741 MenuLayoutNode *node = tmp->data;
4742
4743 switch (menu_layout_node_get_type (node))
4744 {
4745 case MENU_LAYOUT_NODE_MENUNAME:
4746 merge_subdir_by_name (tree,
4747 directory,
4748 menu_layout_node_get_content (node));
4749 break;
4750
4751 case MENU_LAYOUT_NODE_FILENAME:
4752 merge_entry_by_id (tree,
4753 directory,
4754 menu_layout_node_get_content (node));
4755 break;
4756
4757 case MENU_LAYOUT_NODE_SEPARATOR:
4758 /* Unless explicitly told to show all separators, do not show a
4759 * separator at the beginning of a menu. Note that we don't add
4760 * the separators now, and instead make it pending. This way, we
4761 * won't show two consecutive separators nor will we show a
4762 * separator at the end of a menu. */
4763 if (tree->flags & MATEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS)
4764 {
4765 directory->layout_pending_separator = TRUE;
4766 check_pending_separator (directory);
4767 }
4768 else if (directory->contents)
4769 {
4770 menu_verbose ("Adding a potential separator in '%s'\n",
4771 directory->name);
4772
4773 directory->layout_pending_separator = TRUE;
4774 }
4775 else
4776 {
4777 menu_verbose ("Skipping separator at the beginning of '%s'\n",
4778 directory->name);
4779 }
4780 break;
4781
4782 case MENU_LAYOUT_NODE_MERGE:
4783 switch (menu_layout_node_merge_get_type (node))
4784 {
4785 case MENU_LAYOUT_MERGE_NONE:
4786 break;
4787
4788 case MENU_LAYOUT_MERGE_MENUS:
4789 merge_subdirs (tree,
4790 directory,
4791 get_subdirs_from_layout_info (tmp->next));
4792 break;
4793
4794 case MENU_LAYOUT_MERGE_FILES:
4795 merge_entries (tree,
4796 directory,
4797 get_entries_from_layout_info (tmp->next));
4798 break;
4799
4800 case MENU_LAYOUT_MERGE_ALL:
4801 merge_subdirs_and_entries (tree,
4802 directory,
4803 get_subdirs_from_layout_info (tmp->next),
4804 get_entries_from_layout_info (tmp->next));
4805 break;
4806
4807 default:
4808 g_assert_not_reached ();
4809 break;
4810 }
4811 break;
4812
4813 default:
4814 g_assert_not_reached ();
4815 break;
4816 }
4817
4818 tmp = tmp->next;
4819 }
4820 }
4821
4822 g_slist_foreach (directory->subdirs,
4823 (GFunc) matemenu_tree_item_unref,
4824 NULL);
4825 g_slist_free (directory->subdirs);
4826 directory->subdirs = NULL;
4827
4828 g_slist_foreach (directory->entries,
4829 (GFunc) matemenu_tree_item_unref,
4830 NULL);
4831 g_slist_free (directory->entries);
4832 directory->entries = NULL;
4833
4834 g_slist_foreach (directory->default_layout_info,
4835 (GFunc) menu_layout_node_unref,
4836 NULL);
4837 g_slist_free (directory->default_layout_info);
4838 directory->default_layout_info = NULL;
4839
4840 g_slist_foreach (directory->layout_info,
4841 (GFunc) menu_layout_node_unref,
4842 NULL);
4843 g_slist_free (directory->layout_info);
4844 directory->layout_info = NULL;
4845 }
4846
4847 static void
handle_entries_changed(MenuLayoutNode * layout,MateMenuTree * tree)4848 handle_entries_changed (MenuLayoutNode *layout,
4849 MateMenuTree *tree)
4850 {
4851 if (tree->layout == layout)
4852 {
4853 matemenu_tree_force_rebuild (tree);
4854 matemenu_tree_invoke_monitors (tree);
4855 }
4856 }
4857
4858 static void
update_entry_index(MateMenuTree * tree,MateMenuTreeDirectory * dir)4859 update_entry_index (MateMenuTree *tree,
4860 MateMenuTreeDirectory *dir)
4861 {
4862 MateMenuTreeIter *iter = matemenu_tree_directory_iter (dir);
4863 MateMenuTreeItemType next_type;
4864
4865 while ((next_type = matemenu_tree_iter_next (iter)) != MATEMENU_TREE_ITEM_INVALID)
4866 {
4867 gpointer item = NULL;
4868
4869 switch (next_type)
4870 {
4871 case MATEMENU_TREE_ITEM_ENTRY:
4872 {
4873 const char *id;
4874
4875 item = matemenu_tree_iter_get_entry (iter);
4876 id = matemenu_tree_entry_get_desktop_file_id (item);
4877 if (id != NULL)
4878 g_hash_table_insert (tree->entries_by_id, (char*)id, item);
4879 }
4880 break;
4881 case MATEMENU_TREE_ITEM_DIRECTORY:
4882 {
4883 item = matemenu_tree_iter_get_directory (iter);
4884 update_entry_index (tree, (MateMenuTreeDirectory*)item);
4885 }
4886 break;
4887 default:
4888 break;
4889 }
4890 if (item != NULL)
4891 matemenu_tree_item_unref (item);
4892 }
4893
4894 matemenu_tree_iter_unref (iter);
4895 }
4896
4897 static gboolean
matemenu_tree_build_from_layout(MateMenuTree * tree,GError ** error)4898 matemenu_tree_build_from_layout (MateMenuTree *tree,
4899 GError **error)
4900 {
4901 DesktopEntrySet *allocated;
4902
4903 if (tree->root)
4904 return TRUE;
4905
4906 if (!matemenu_tree_load_layout (tree, error))
4907 return FALSE;
4908
4909 menu_verbose ("Building menu tree from layout\n");
4910
4911 allocated = desktop_entry_set_new ();
4912
4913 /* create the menu structure */
4914 tree->root = process_layout (tree,
4915 NULL,
4916 find_menu_child (tree->layout),
4917 allocated);
4918 if (tree->root)
4919 {
4920 DesktopEntrySet *unallocated_used;
4921
4922 unallocated_used = desktop_entry_set_new ();
4923
4924 process_only_unallocated (tree, tree->root, allocated, unallocated_used);
4925 if (tree->flags & MATEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED)
4926 {
4927 DesktopEntrySet *entry_pool;
4928 DesktopEntrySet *still_unallocated;
4929 GetStillUnallocatedForeachData data;
4930
4931 entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (find_menu_child (tree->layout)));
4932 still_unallocated = desktop_entry_set_new ();
4933
4934 data.tree = tree;
4935 data.allocated = allocated;
4936 data.unallocated_used = unallocated_used;
4937 data.still_unallocated = still_unallocated;
4938
4939 desktop_entry_set_foreach (entry_pool,
4940 (DesktopEntrySetForeachFunc) get_still_unallocated_foreach,
4941 &data);
4942
4943 desktop_entry_set_unref (entry_pool);
4944
4945 desktop_entry_set_foreach (still_unallocated,
4946 (DesktopEntrySetForeachFunc) unallocated_entries_listify_foreach,
4947 tree->root);
4948
4949 desktop_entry_set_unref (still_unallocated);
4950 }
4951
4952 desktop_entry_set_unref (unallocated_used);
4953
4954 /* process the layout info part that can move/remove items:
4955 * inline, show_empty, etc. */
4956 preprocess_layout_info (tree, tree->root);
4957 /* populate the menu structure that we got with the items, and order it
4958 * according to the layout info */
4959 process_layout_info (tree, tree->root);
4960
4961 update_entry_index (tree, tree->root);
4962
4963 menu_layout_node_root_add_entries_monitor (tree->layout,
4964 (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed,
4965 tree);
4966 }
4967
4968 desktop_entry_set_unref (allocated);
4969
4970 return TRUE;
4971 }
4972
4973 static void
matemenu_tree_force_rebuild(MateMenuTree * tree)4974 matemenu_tree_force_rebuild (MateMenuTree *tree)
4975 {
4976 if (tree->root)
4977 {
4978 g_hash_table_remove_all (tree->entries_by_id);
4979 matemenu_tree_item_unref (tree->root);
4980 tree->root = NULL;
4981 tree->loaded = FALSE;
4982
4983 g_assert (tree->layout != NULL);
4984
4985 menu_layout_node_root_remove_entries_monitor (tree->layout,
4986 (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed,
4987 tree);
4988 }
4989 }
4990
4991 GType
matemenu_tree_iter_get_type(void)4992 matemenu_tree_iter_get_type (void)
4993 {
4994 static GType gtype = G_TYPE_INVALID;
4995 if (gtype == G_TYPE_INVALID)
4996 {
4997 gtype = g_boxed_type_register_static ("MateMenuTreeIter",
4998 (GBoxedCopyFunc)matemenu_tree_iter_ref,
4999 (GBoxedFreeFunc)matemenu_tree_iter_unref);
5000 }
5001 return gtype;
5002 }
5003
5004 GType
matemenu_tree_directory_get_type(void)5005 matemenu_tree_directory_get_type (void)
5006 {
5007 static GType gtype = G_TYPE_INVALID;
5008 if (gtype == G_TYPE_INVALID)
5009 {
5010 gtype = g_boxed_type_register_static ("MateMenuTreeDirectory",
5011 (GBoxedCopyFunc)matemenu_tree_item_ref,
5012 (GBoxedFreeFunc)matemenu_tree_item_unref);
5013 }
5014 return gtype;
5015 }
5016
5017 GType
matemenu_tree_entry_get_type(void)5018 matemenu_tree_entry_get_type (void)
5019 {
5020 static GType gtype = G_TYPE_INVALID;
5021 if (gtype == G_TYPE_INVALID)
5022 {
5023 gtype = g_boxed_type_register_static ("MateMenuTreeEntry",
5024 (GBoxedCopyFunc)matemenu_tree_item_ref,
5025 (GBoxedFreeFunc)matemenu_tree_item_unref);
5026 }
5027 return gtype;
5028 }
5029
5030 GType
matemenu_tree_separator_get_type(void)5031 matemenu_tree_separator_get_type (void)
5032 {
5033 static GType gtype = G_TYPE_INVALID;
5034 if (gtype == G_TYPE_INVALID)
5035 {
5036 gtype = g_boxed_type_register_static ("MateMenuTreeSeparator",
5037 (GBoxedCopyFunc)matemenu_tree_item_ref,
5038 (GBoxedFreeFunc)matemenu_tree_item_unref);
5039 }
5040 return gtype;
5041 }
5042
5043 GType
matemenu_tree_header_get_type(void)5044 matemenu_tree_header_get_type (void)
5045 {
5046 static GType gtype = G_TYPE_INVALID;
5047 if (gtype == G_TYPE_INVALID)
5048 {
5049 gtype = g_boxed_type_register_static ("MateMenuTreeHeader",
5050 (GBoxedCopyFunc)matemenu_tree_item_ref,
5051 (GBoxedFreeFunc)matemenu_tree_item_unref);
5052 }
5053 return gtype;
5054 }
5055
5056 GType
matemenu_tree_alias_get_type(void)5057 matemenu_tree_alias_get_type (void)
5058 {
5059 static GType gtype = G_TYPE_INVALID;
5060 if (gtype == G_TYPE_INVALID)
5061 {
5062 gtype = g_boxed_type_register_static ("MateMenuTreeAlias",
5063 (GBoxedCopyFunc)matemenu_tree_item_ref,
5064 (GBoxedFreeFunc)matemenu_tree_item_unref);
5065 }
5066 return gtype;
5067 }
5068
5069 GType
matemenu_tree_flags_get_type(void)5070 matemenu_tree_flags_get_type (void)
5071 {
5072 static GType enum_type_id = 0;
5073 if (G_UNLIKELY (!enum_type_id))
5074 {
5075 static const GFlagsValue values[] = {
5076 { MATEMENU_TREE_FLAGS_NONE, "MATEMENU_TREE_FLAGS_NONE", "none" },
5077 { MATEMENU_TREE_FLAGS_INCLUDE_EXCLUDED, "MATEMENU_TREE_FLAGS_INCLUDE_EXCLUDED", "include-excluded" },
5078 { MATEMENU_TREE_FLAGS_SHOW_EMPTY, "MATEMENU_TREE_FLAGS_SHOW_EMPTY", "show-empty" },
5079 { MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY, "MATEMENU_TREE_FLAGS_INCLUDE_NODISPLAY", "include-nodisplay" },
5080 { MATEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS, "MATEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS", "show-all-separators" },
5081 { MATEMENU_TREE_FLAGS_SORT_DISPLAY_NAME, "MATEMENU_TREE_FLAGS_SORT_DISPLAY_NAME", "sort-display-name" },
5082 { MATEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED, "MATEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED,", "include-unallocated" },
5083 { 0, NULL, NULL }
5084 };
5085 enum_type_id = g_flags_register_static ("MateMenuTreeFlags", values);
5086 }
5087 return enum_type_id;
5088 }
5089