Bug Summary

File:cafemenu-tree.c
Warning:line 832, column 12
This statement is never executed

Annotated Source Code

Press '?' to see keyboard shortcuts

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