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-21/lib/clang/21 -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/libmount -I /usr/include/blkid -I /usr/include/sysprof-6 -D CAFEMENU_I_KNOW_THIS_IS_UNSTABLE -D PIC -internal-isystem /usr/lib/llvm-21/lib/clang/21/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/15/../../../../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 security.ArrayBound -analyzer-checker unix.cstring.NotNullTerminated -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -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/2026-07-19-115732-11079-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)
> 0 ? sizeof (MenuFileMonitor) : 1)))
;
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_free_full (iter->contents, (GDestroyNotify) cafemenu_tree_item_unref);
973
974 g_slice_free (CafeMenuTreeIter, iter)do { if (1) g_slice_free1 (sizeof (CafeMenuTreeIter), (iter))
; else (void) ((CafeMenuTreeIter*) 0 == (iter)); } while (0)
;
975}
976
977/**
978 * cafemenu_tree_directory_iter:
979 * @directory: directory
980 *
981 * Returns: (transfer full): A new iterator over the directory contents
982 */
983CafeMenuTreeIter *
984cafemenu_tree_directory_iter (CafeMenuTreeDirectory *directory)
985{
986 CafeMenuTreeIter *iter;
987
988 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)
;
989
990 iter = g_slice_new0 (CafeMenuTreeIter)((CafeMenuTreeIter*) g_slice_alloc0 ((sizeof (CafeMenuTreeIter
) > 0 ? sizeof (CafeMenuTreeIter) : 1)))
;
991 iter->refcount = 1;
992
993 iter->contents = g_slist_copy_deep (directory->contents, (GCopyFunc) cafemenu_tree_item_ref, NULL((void*)0));
994 iter->contents_iter = iter->contents;
995
996 return iter;
997}
998
999/**
1000 * cafemenu_tree_iter_next:
1001 * @iter: iter
1002 *
1003 * Change the iterator to the next item, and return its type. If
1004 * there are no more items, %CAFEMENU_TREE_ITEM_INVALID is returned.
1005 *
1006 * Returns: The type of the next item that can be retrived from the iterator
1007 */
1008CafeMenuTreeItemType
1009cafemenu_tree_iter_next (CafeMenuTreeIter *iter)
1010{
1011 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)
;
1012
1013 if (iter->contents_iter)
1014 {
1015 iter->item = iter->contents_iter->data;
1016 iter->contents_iter = iter->contents_iter->next;
1017 return iter->item->type;
1018 }
1019 else
1020 return CAFEMENU_TREE_ITEM_INVALID;
1021}
1022
1023/**
1024 * cafemenu_tree_iter_get_directory:
1025 * @iter: iter
1026 *
1027 * This method may only be called if cafemenu_tree_iter_next()
1028 * returned CAFEMENU_TREE_ITEM_DIRECTORY.
1029 *
1030 * Returns: (transfer full): A directory
1031 */
1032CafeMenuTreeDirectory *
1033cafemenu_tree_iter_get_directory (CafeMenuTreeIter *iter)
1034{
1035 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)
;
1036 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)
;
1037 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)
;
1038
1039 return (CafeMenuTreeDirectory*)cafemenu_tree_item_ref (iter->item);
1040}
1041
1042/**
1043 * cafemenu_tree_iter_get_entry:
1044 * @iter: iter
1045 *
1046 * This method may only be called if cafemenu_tree_iter_next()
1047 * returned CAFEMENU_TREE_ITEM_ENTRY.
1048 *
1049 * Returns: (transfer full): An entry
1050 */
1051CafeMenuTreeEntry *
1052cafemenu_tree_iter_get_entry (CafeMenuTreeIter *iter)
1053{
1054 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)
;
1055 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)
;
1056 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)
;
1057
1058 return (CafeMenuTreeEntry*)cafemenu_tree_item_ref (iter->item);
1059}
1060
1061/**
1062 * cafemenu_tree_iter_get_header:
1063 * @iter: iter
1064 *
1065 * This method may only be called if cafemenu_tree_iter_next()
1066 * returned CAFEMENU_TREE_ITEM_HEADER.
1067 *
1068 * Returns: (transfer full): A header
1069 */
1070CafeMenuTreeHeader *
1071cafemenu_tree_iter_get_header (CafeMenuTreeIter *iter)
1072{
1073 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)
;
1074 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)
;
1075 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)
;
1076
1077 return (CafeMenuTreeHeader*)cafemenu_tree_item_ref (iter->item);
1078}
1079
1080/**
1081 * cafemenu_tree_iter_get_alias:
1082 * @iter: iter
1083 *
1084 * This method may only be called if cafemenu_tree_iter_next()
1085 * returned CAFEMENU_TREE_ITEM_ALIAS.
1086 *
1087 * Returns: (transfer full): An alias
1088 */
1089CafeMenuTreeAlias *
1090cafemenu_tree_iter_get_alias (CafeMenuTreeIter *iter)
1091{
1092 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)
;
1093 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)
;
1094 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)
;
1095
1096 return (CafeMenuTreeAlias*)cafemenu_tree_item_ref (iter->item);
1097}
1098
1099/**
1100 * cafemenu_tree_iter_get_separator:
1101 * @iter: iter
1102 *
1103 * This method may only be called if cafemenu_tree_iter_next()
1104 * returned #CAFEMENU_TREE_ITEM_SEPARATOR.
1105 *
1106 * Returns: (transfer full): A separator
1107 */
1108CafeMenuTreeSeparator *
1109cafemenu_tree_iter_get_separator (CafeMenuTreeIter *iter)
1110{
1111 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)
;
1112 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)
;
1113 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)
;
1114
1115 return (CafeMenuTreeSeparator*)cafemenu_tree_item_ref (iter->item);
1116}
1117
1118const char *
1119cafemenu_tree_directory_get_name (CafeMenuTreeDirectory *directory)
1120{
1121 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)
;
1122
1123 if (!directory->directory_entry)
1124 return directory->name;
1125
1126 return desktop_entry_get_name (directory->directory_entry);
1127}
1128
1129const char *
1130cafemenu_tree_directory_get_generic_name (CafeMenuTreeDirectory *directory)
1131{
1132 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)
;
1133
1134 if (!directory->directory_entry)
1135 return NULL((void*)0);
1136
1137 return desktop_entry_get_generic_name (directory->directory_entry);
1138}
1139
1140const char *
1141cafemenu_tree_directory_get_comment (CafeMenuTreeDirectory *directory)
1142{
1143 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)
;
1144
1145 if (!directory->directory_entry)
1146 return NULL((void*)0);
1147
1148 return desktop_entry_get_comment (directory->directory_entry);
1149}
1150
1151/**
1152 * cafemenu_tree_directory_get_icon:
1153 * @directory: a #CafeMenuTreeDirectory
1154 *
1155 * Gets the icon for the directory.
1156 *
1157 * Returns: (transfer none): The #GIcon for this directory
1158 */
1159GIcon *
1160cafemenu_tree_directory_get_icon (CafeMenuTreeDirectory *directory)
1161{
1162 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)
;
1163
1164 if (!directory->directory_entry)
1165 return NULL((void*)0);
1166
1167 return desktop_entry_get_icon(directory->directory_entry);
1168}
1169
1170const char *
1171cafemenu_tree_directory_get_desktop_file_path (CafeMenuTreeDirectory *directory)
1172{
1173 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)
;
1174
1175 if (!directory->directory_entry)
1176 return NULL((void*)0);
1177
1178 return desktop_entry_get_path (directory->directory_entry);
1179}
1180
1181const char *
1182cafemenu_tree_directory_get_menu_id (CafeMenuTreeDirectory *directory)
1183{
1184 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)
;
1185
1186 return directory->name;
1187}
1188
1189gboolean
1190cafemenu_tree_directory_get_is_nodisplay (CafeMenuTreeDirectory *directory)
1191{
1192 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)
;
1193
1194 return directory->is_nodisplay;
1195}
1196
1197/**
1198 * cafemenu_tree_directory_get_tree:
1199 * @directory: A #CafeMenuTreeDirectory
1200 *
1201 * Grab the tree associated with a #CafeMenuTreeItem.
1202 *
1203 * Returns: (transfer full): The #CafeMenuTree
1204 */
1205CafeMenuTree *
1206cafemenu_tree_directory_get_tree (CafeMenuTreeDirectory *directory)
1207{
1208 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)
;
1209
1210 return g_object_ref (directory->item.tree)((__typeof__ (directory->item.tree)) (g_object_ref) (directory
->item.tree))
;
1211}
1212
1213static void
1214append_directory_path (CafeMenuTreeDirectory *directory,
1215 GString *path)
1216{
1217
1218 if (!directory->item.parent)
1219 {
1220 g_string_append_c (path, G_DIR_SEPARATOR)g_string_append_c_inline (path, '/');
1221 return;
1222 }
1223
1224 append_directory_path (directory->item.parent, path);
1225
1226 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))
;
1227 g_string_append_c (path, G_DIR_SEPARATOR)g_string_append_c_inline (path, '/');
1228}
1229
1230char *
1231cafemenu_tree_directory_make_path (CafeMenuTreeDirectory *directory,
1232 CafeMenuTreeEntry *entry)
1233{
1234 GString *path;
1235
1236 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)
;
1237
1238 path = g_string_new (NULL((void*)0));
1239
1240 append_directory_path (directory, path);
1241
1242 if (entry != NULL((void*)0))
1243 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))
1244 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))
;
1245
1246 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))))
;
1247}
1248
1249/**
1250 * cafemenu_tree_entry_get_app_info:
1251 * @entry: a #CafeMenuTreeEntry
1252 *
1253 * Returns: (transfer none): The #GDesktopAppInfo for this entry
1254 */
1255GDesktopAppInfo *
1256cafemenu_tree_entry_get_app_info (CafeMenuTreeEntry *entry)
1257{
1258 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)
;
1259
1260 return desktop_entry_get_app_info (entry->desktop_entry);
1261}
1262
1263const char *
1264cafemenu_tree_entry_get_desktop_file_path (CafeMenuTreeEntry *entry)
1265{
1266 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)
;
1267
1268 return desktop_entry_get_path (entry->desktop_entry);
1269}
1270
1271const char *
1272cafemenu_tree_entry_get_desktop_file_id (CafeMenuTreeEntry *entry)
1273{
1274 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)
;
1275
1276 return entry->desktop_file_id;
1277}
1278
1279gboolean
1280cafemenu_tree_entry_get_is_nodisplay_recurse (CafeMenuTreeEntry *entry)
1281{
1282 CafeMenuTreeDirectory *directory;
1283 GDesktopAppInfo *app_info;
1284
1285 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)
;
1286
1287 app_info = cafemenu_tree_entry_get_app_info (entry);
1288
1289 if (g_desktop_app_info_get_nodisplay (app_info))
1290 return TRUE(!(0));
1291
1292 directory = entry->item.parent;
1293 while (directory != NULL((void*)0))
1294 {
1295 if (directory->is_nodisplay)
1296 return TRUE(!(0));
1297
1298 directory = directory->item.parent;
1299 }
1300
1301 return FALSE(0);
1302}
1303
1304gboolean
1305cafemenu_tree_entry_get_is_excluded (CafeMenuTreeEntry *entry)
1306{
1307 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)
;
1308
1309 return entry->is_excluded;
1310}
1311
1312gboolean
1313cafemenu_tree_entry_get_is_unallocated (CafeMenuTreeEntry *entry)
1314{
1315 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)
;
1316
1317 return entry->is_unallocated;
1318}
1319
1320/**
1321 * cafemenu_tree_entry_get_tree:
1322 * @entry: A #CafeMenuTreeEntry
1323 *
1324 * Grab the tree associated with a #CafeMenuTreeEntry.
1325 *
1326 * Returns: (transfer full): The #CafeMenuTree
1327 */
1328CafeMenuTree *
1329cafemenu_tree_entry_get_tree (CafeMenuTreeEntry *entry)
1330{
1331 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)
;
1332
1333 return g_object_ref (entry->item.tree)((__typeof__ (entry->item.tree)) (g_object_ref) (entry->
item.tree))
;
1334}
1335
1336CafeMenuTreeDirectory *
1337cafemenu_tree_header_get_directory (CafeMenuTreeHeader *header)
1338{
1339 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)
;
1340
1341 return cafemenu_tree_item_ref (header->directory);
1342}
1343
1344/**
1345 * cafemenu_tree_header_get_tree:
1346 * @header: A #CafeMenuTreeHeader
1347 *
1348 * Grab the tree associated with a #CafeMenuTreeHeader.
1349 *
1350 * Returns: (transfer full): The #CafeMenuTree
1351 */
1352CafeMenuTree *
1353cafemenu_tree_header_get_tree (CafeMenuTreeHeader *header)
1354{
1355 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)
;
1356
1357 return g_object_ref (header->item.tree)((__typeof__ (header->item.tree)) (g_object_ref) (header->
item.tree))
;
1358}
1359
1360CafeMenuTreeItemType
1361cafemenu_tree_alias_get_aliased_item_type (CafeMenuTreeAlias *alias)
1362{
1363 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)
;
1364
1365 g_assert (alias->aliased_item != NULL)do { if (alias->aliased_item != ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 1365, ((const char*) (__func__
)), "alias->aliased_item != NULL"); } while (0)
;
1366 return alias->aliased_item->type;
1367}
1368
1369CafeMenuTreeDirectory* cafemenu_tree_alias_get_directory(CafeMenuTreeAlias* alias)
1370{
1371 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)
;
1372
1373 return cafemenu_tree_item_ref(alias->directory);
1374}
1375
1376/**
1377 * cafemenu_tree_alias_get_tree:
1378 * @alias: A #CafeMenuTreeAlias
1379 *
1380 * Grab the tree associated with a #CafeMenuTreeAlias.
1381 *
1382 * Returns: (transfer full): The #CafeMenuTree
1383 */
1384CafeMenuTree *
1385cafemenu_tree_alias_get_tree (CafeMenuTreeAlias *alias)
1386{
1387 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)
;
1388
1389 return g_object_ref (alias->item.tree)((__typeof__ (alias->item.tree)) (g_object_ref) (alias->
item.tree))
;
1390}
1391
1392/**
1393 * cafemenu_tree_separator_get_tree:
1394 * @separator: A #CafeMenuTreeSeparator
1395 *
1396 * Grab the tree associated with a #CafeMenuTreeSeparator.
1397 *
1398 * Returns: (transfer full): The #CafeMenuTree
1399 */
1400CafeMenuTree *
1401cafemenu_tree_separator_get_tree (CafeMenuTreeSeparator *separator)
1402{
1403 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)
;
1404
1405 return g_object_ref (separator->item.tree)((__typeof__ (separator->item.tree)) (g_object_ref) (separator
->item.tree))
;
1406}
1407
1408/**
1409 * cafemenu_tree_alias_get_aliased_directory:
1410 * @alias: alias
1411 *
1412 * Returns: (transfer full): The aliased directory entry
1413 */
1414CafeMenuTreeDirectory *
1415cafemenu_tree_alias_get_aliased_directory (CafeMenuTreeAlias *alias)
1416{
1417 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)
;
1418 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)
;
1419
1420 return (CafeMenuTreeDirectory *) cafemenu_tree_item_ref (alias->aliased_item);
1421}
1422
1423/**
1424 * cafemenu_tree_alias_get_aliased_entry:
1425 * @alias: alias
1426 *
1427 * Returns: (transfer full): The aliased entry
1428 */
1429CafeMenuTreeEntry *
1430cafemenu_tree_alias_get_aliased_entry (CafeMenuTreeAlias *alias)
1431{
1432 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)
;
1433 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)
;
1434
1435 return (CafeMenuTreeEntry *) cafemenu_tree_item_ref (alias->aliased_item);
1436}
1437
1438static CafeMenuTreeDirectory *
1439cafemenu_tree_directory_new (CafeMenuTree *tree,
1440 CafeMenuTreeDirectory *parent,
1441 const char *name)
1442{
1443 CafeMenuTreeDirectory *retval;
1444
1445 retval = g_slice_new0 (CafeMenuTreeDirectory)((CafeMenuTreeDirectory*) g_slice_alloc0 ((sizeof (CafeMenuTreeDirectory
) > 0 ? sizeof (CafeMenuTreeDirectory) : 1)))
;
1446
1447 retval->item.type = CAFEMENU_TREE_ITEM_DIRECTORY;
1448 retval->item.parent = parent;
1449 retval->item.refcount = 1;
1450 retval->item.tree = tree;
1451
1452 retval->name = g_strdup (name)g_strdup_inline (name);
1453 retval->directory_entry = NULL((void*)0);
1454 retval->entries = NULL((void*)0);
1455 retval->subdirs = NULL((void*)0);
1456 retval->default_layout_info = NULL((void*)0);
1457 retval->layout_info = NULL((void*)0);
1458 retval->contents = NULL((void*)0);
1459 retval->only_unallocated = FALSE(0);
1460 retval->is_nodisplay = FALSE(0);
1461 retval->layout_pending_separator = FALSE(0);
1462 retval->preprocessed = FALSE(0);
1463 retval->will_inline_header = G_MAXUINT16((guint16) 0xffff);
1464
1465 retval->default_layout_values.mask = MENU_LAYOUT_VALUES_NONE;
1466 retval->default_layout_values.show_empty = FALSE(0);
1467 retval->default_layout_values.inline_menus = FALSE(0);
1468 retval->default_layout_values.inline_limit = 4;
1469 retval->default_layout_values.inline_header = FALSE(0);
1470 retval->default_layout_values.inline_alias = FALSE(0);
1471
1472 return retval;
1473}
1474
1475static void
1476cafemenu_tree_directory_finalize (CafeMenuTreeDirectory *directory)
1477{
1478 g_assert (directory->item.refcount == 0)do { if (directory->item.refcount == 0) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 1478, ((const char*) (__func__
)), "directory->item.refcount == 0"); } while (0)
;
1479
1480 g_slist_free_full (directory->contents, (GDestroyNotify) cafemenu_tree_item_unref_and_unset_parent);
1481 directory->contents = NULL((void*)0);
1482
1483 g_slist_free_full (directory->default_layout_info, (GDestroyNotify) menu_layout_node_unref);
1484 directory->default_layout_info = NULL((void*)0);
1485
1486 g_slist_free_full (directory->layout_info, (GDestroyNotify) menu_layout_node_unref);
1487 directory->layout_info = NULL((void*)0);
1488
1489 g_slist_free_full (directory->subdirs, (GDestroyNotify) cafemenu_tree_item_unref_and_unset_parent);
1490 directory->subdirs = NULL((void*)0);
1491
1492 g_slist_free_full (directory->entries, (GDestroyNotify) cafemenu_tree_item_unref_and_unset_parent);
1493 directory->entries = NULL((void*)0);
1494
1495 if (directory->directory_entry)
1496 desktop_entry_unref (directory->directory_entry);
1497 directory->directory_entry = NULL((void*)0);
1498
1499 g_free (directory->name);
1500 directory->name = NULL((void*)0);
1501
1502 g_slice_free (CafeMenuTreeDirectory, directory)do { if (1) g_slice_free1 (sizeof (CafeMenuTreeDirectory), (directory
)); else (void) ((CafeMenuTreeDirectory*) 0 == (directory)); }
while (0)
;
1503}
1504
1505static CafeMenuTreeSeparator *
1506cafemenu_tree_separator_new (CafeMenuTreeDirectory *parent)
1507{
1508 CafeMenuTreeSeparator *retval;
1509
1510 retval = g_slice_new0 (CafeMenuTreeSeparator)((CafeMenuTreeSeparator*) g_slice_alloc0 ((sizeof (CafeMenuTreeSeparator
) > 0 ? sizeof (CafeMenuTreeSeparator) : 1)))
;
1511
1512 retval->item.type = CAFEMENU_TREE_ITEM_SEPARATOR;
1513 retval->item.parent = parent;
1514 retval->item.refcount = 1;
1515 retval->item.tree = parent->item.tree;
1516
1517 return retval;
1518}
1519
1520static void
1521cafemenu_tree_separator_finalize (CafeMenuTreeSeparator *separator)
1522{
1523 g_assert (separator->item.refcount == 0)do { if (separator->item.refcount == 0) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 1523, ((const char*) (__func__
)), "separator->item.refcount == 0"); } while (0)
;
1524
1525 g_slice_free (CafeMenuTreeSeparator, separator)do { if (1) g_slice_free1 (sizeof (CafeMenuTreeSeparator), (separator
)); else (void) ((CafeMenuTreeSeparator*) 0 == (separator)); }
while (0)
;
1526}
1527
1528static CafeMenuTreeHeader *
1529cafemenu_tree_header_new (CafeMenuTreeDirectory *parent,
1530 CafeMenuTreeDirectory *directory)
1531{
1532 CafeMenuTreeHeader *retval;
1533
1534 retval = g_slice_new0 (CafeMenuTreeHeader)((CafeMenuTreeHeader*) g_slice_alloc0 ((sizeof (CafeMenuTreeHeader
) > 0 ? sizeof (CafeMenuTreeHeader) : 1)))
;
1535
1536 retval->item.type = CAFEMENU_TREE_ITEM_HEADER;
1537 retval->item.parent = parent;
1538 retval->item.refcount = 1;
1539 retval->item.tree = parent->item.tree;
1540
1541 retval->directory = cafemenu_tree_item_ref (directory);
1542
1543 cafemenu_tree_item_set_parent (CAFEMENU_TREE_ITEM (retval->directory)((CafeMenuTreeItem *)(retval->directory)), NULL((void*)0));
1544
1545 return retval;
1546}
1547
1548static void
1549cafemenu_tree_header_finalize (CafeMenuTreeHeader *header)
1550{
1551 g_assert (header->item.refcount == 0)do { if (header->item.refcount == 0) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 1551, ((const char*) (__func__
)), "header->item.refcount == 0"); } while (0)
;
1552
1553 if (header->directory != NULL((void*)0))
1554 cafemenu_tree_item_unref (header->directory);
1555 header->directory = NULL((void*)0);
1556
1557 g_slice_free (CafeMenuTreeHeader, header)do { if (1) g_slice_free1 (sizeof (CafeMenuTreeHeader), (header
)); else (void) ((CafeMenuTreeHeader*) 0 == (header)); } while
(0)
;
1558}
1559
1560static CafeMenuTreeAlias *
1561cafemenu_tree_alias_new (CafeMenuTreeDirectory *parent,
1562 CafeMenuTreeDirectory *directory,
1563 CafeMenuTreeItem *item)
1564{
1565 CafeMenuTreeAlias *retval;
1566
1567 retval = g_slice_new0 (CafeMenuTreeAlias)((CafeMenuTreeAlias*) g_slice_alloc0 ((sizeof (CafeMenuTreeAlias
) > 0 ? sizeof (CafeMenuTreeAlias) : 1)))
;
1568
1569 retval->item.type = CAFEMENU_TREE_ITEM_ALIAS;
1570 retval->item.parent = parent;
1571 retval->item.refcount = 1;
1572 retval->item.tree = parent->item.tree;
1573
1574 retval->directory = cafemenu_tree_item_ref (directory);
1575 if (item->type != CAFEMENU_TREE_ITEM_ALIAS)
1576 retval->aliased_item = cafemenu_tree_item_ref (item);
1577 else
1578 {
1579 CafeMenuTreeAlias *alias = CAFEMENU_TREE_ALIAS (item)((CafeMenuTreeAlias *)(item));
1580 retval->aliased_item = cafemenu_tree_item_ref (alias->aliased_item);
1581 }
1582
1583 cafemenu_tree_item_set_parent (CAFEMENU_TREE_ITEM (retval->directory)((CafeMenuTreeItem *)(retval->directory)), NULL((void*)0));
1584 cafemenu_tree_item_set_parent (retval->aliased_item, NULL((void*)0));
1585
1586 return retval;
1587}
1588
1589static void
1590cafemenu_tree_alias_finalize (CafeMenuTreeAlias *alias)
1591{
1592 g_assert (alias->item.refcount == 0)do { if (alias->item.refcount == 0) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 1592, ((const char*) (__func__
)), "alias->item.refcount == 0"); } while (0)
;
1593
1594 if (alias->directory != NULL((void*)0))
1595 cafemenu_tree_item_unref (alias->directory);
1596 alias->directory = NULL((void*)0);
1597
1598 if (alias->aliased_item != NULL((void*)0))
1599 cafemenu_tree_item_unref (alias->aliased_item);
1600 alias->aliased_item = NULL((void*)0);
1601
1602 g_slice_free (CafeMenuTreeAlias, alias)do { if (1) g_slice_free1 (sizeof (CafeMenuTreeAlias), (alias
)); else (void) ((CafeMenuTreeAlias*) 0 == (alias)); } while (
0)
;
1603}
1604
1605static CafeMenuTreeEntry *
1606cafemenu_tree_entry_new (CafeMenuTreeDirectory *parent,
1607 DesktopEntry *desktop_entry,
1608 const char *desktop_file_id,
1609 gboolean is_excluded,
1610 gboolean is_unallocated)
1611{
1612 CafeMenuTreeEntry *retval;
1613
1614 retval = g_slice_new0 (CafeMenuTreeEntry)((CafeMenuTreeEntry*) g_slice_alloc0 ((sizeof (CafeMenuTreeEntry
) > 0 ? sizeof (CafeMenuTreeEntry) : 1)))
;
1615
1616 retval->item.type = CAFEMENU_TREE_ITEM_ENTRY;
1617 retval->item.parent = parent;
1618 retval->item.refcount = 1;
1619 retval->item.tree = parent->item.tree;
1620
1621 retval->desktop_entry = desktop_entry_ref (desktop_entry);
1622 retval->desktop_file_id = g_strdup (desktop_file_id)g_strdup_inline (desktop_file_id);
1623 retval->is_excluded = is_excluded != FALSE(0);
1624 retval->is_unallocated = is_unallocated != FALSE(0);
1625
1626 return retval;
1627}
1628
1629static void
1630cafemenu_tree_entry_finalize (CafeMenuTreeEntry *entry)
1631{
1632 g_assert (entry->item.refcount == 0)do { if (entry->item.refcount == 0) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 1632, ((const char*) (__func__
)), "entry->item.refcount == 0"); } while (0)
;
1633
1634 g_free (entry->desktop_file_id);
1635 entry->desktop_file_id = NULL((void*)0);
1636
1637 if (entry->desktop_entry)
1638 desktop_entry_unref (entry->desktop_entry);
1639 entry->desktop_entry = NULL((void*)0);
1640
1641 g_slice_free (CafeMenuTreeEntry, entry)do { if (1) g_slice_free1 (sizeof (CafeMenuTreeEntry), (entry
)); else (void) ((CafeMenuTreeEntry*) 0 == (entry)); } while (
0)
;
1642}
1643
1644static int
1645cafemenu_tree_entry_compare_by_id (CafeMenuTreeItem *a,
1646 CafeMenuTreeItem *b)
1647{
1648 if (a->type == CAFEMENU_TREE_ITEM_ALIAS)
1649 a = CAFEMENU_TREE_ALIAS (a)((CafeMenuTreeAlias *)(a))->aliased_item;
1650
1651 if (b->type == CAFEMENU_TREE_ITEM_ALIAS)
1652 b = CAFEMENU_TREE_ALIAS (b)((CafeMenuTreeAlias *)(b))->aliased_item;
1653
1654 return strcmp (CAFEMENU_TREE_ENTRY (a)((CafeMenuTreeEntry *)(a))->desktop_file_id,
1655 CAFEMENU_TREE_ENTRY (b)((CafeMenuTreeEntry *)(b))->desktop_file_id);
1656}
1657
1658/**
1659 * cafemenu_tree_item_ref:
1660 * @item: a #CafeMenuTreeItem
1661 *
1662 * Returns: (transfer full): The same @item, or %NULL if @item is not a valid #CafeMenuTreeItem
1663 */
1664gpointer
1665cafemenu_tree_item_ref (gpointer itemp)
1666{
1667 CafeMenuTreeItem* item = (CafeMenuTreeItem*) itemp;
1668
1669 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)
;
1670 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)
;
1671
1672 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)
; }))
;
1673
1674 return item;
1675}
1676
1677void
1678cafemenu_tree_item_unref (gpointer itemp)
1679{
1680 CafeMenuTreeItem *item;
1681
1682 item = (CafeMenuTreeItem *) itemp;
1683
1684 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)
;
1685 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)
;
1686
1687 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; }))
)
1688 {
1689 switch (item->type)
1690 {
1691 case CAFEMENU_TREE_ITEM_DIRECTORY:
1692 cafemenu_tree_directory_finalize (CAFEMENU_TREE_DIRECTORY (item)((CafeMenuTreeDirectory *)(item)));
1693 break;
1694
1695 case CAFEMENU_TREE_ITEM_ENTRY:
1696 cafemenu_tree_entry_finalize (CAFEMENU_TREE_ENTRY (item)((CafeMenuTreeEntry *)(item)));
1697 break;
1698
1699 case CAFEMENU_TREE_ITEM_SEPARATOR:
1700 cafemenu_tree_separator_finalize (CAFEMENU_TREE_SEPARATOR (item)((CafeMenuTreeSeparator *)(item)));
1701 break;
1702
1703 case CAFEMENU_TREE_ITEM_HEADER:
1704 cafemenu_tree_header_finalize (CAFEMENU_TREE_HEADER (item)((CafeMenuTreeHeader *)(item)));
1705 break;
1706
1707 case CAFEMENU_TREE_ITEM_ALIAS:
1708 cafemenu_tree_alias_finalize (CAFEMENU_TREE_ALIAS (item)((CafeMenuTreeAlias *)(item)));
1709 break;
1710
1711 default:
1712 g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "cafemenu-tree.c"
, 1712, ((const char*) (__func__)), ((void*)0)); } while (0)
;
1713 break;
1714 }
1715 }
1716}
1717
1718static void
1719cafemenu_tree_item_unref_and_unset_parent (gpointer itemp)
1720{
1721 CafeMenuTreeItem *item;
1722
1723 item = (CafeMenuTreeItem *) itemp;
1724
1725 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)
;
1726
1727 cafemenu_tree_item_set_parent (item, NULL((void*)0));
1728 cafemenu_tree_item_unref (item);
1729}
1730
1731static inline const char *
1732cafemenu_tree_item_compare_get_name_helper (CafeMenuTreeItem *item,
1733 CafeMenuTreeFlags flags)
1734{
1735 const char *name;
1736
1737 name = NULL((void*)0);
1738
1739 switch (item->type)
1740 {
1741 case CAFEMENU_TREE_ITEM_DIRECTORY:
1742 if (CAFEMENU_TREE_DIRECTORY (item)((CafeMenuTreeDirectory *)(item))->directory_entry)
1743 name = desktop_entry_get_name (CAFEMENU_TREE_DIRECTORY (item)((CafeMenuTreeDirectory *)(item))->directory_entry);
1744 else
1745 name = CAFEMENU_TREE_DIRECTORY (item)((CafeMenuTreeDirectory *)(item))->name;
1746 break;
1747
1748 case CAFEMENU_TREE_ITEM_ENTRY:
1749 if (flags & CAFEMENU_TREE_FLAGS_SORT_DISPLAY_NAME)
1750 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 ()))))))
);
1751 else
1752 name = desktop_entry_get_name (CAFEMENU_TREE_ENTRY (item)((CafeMenuTreeEntry *)(item))->desktop_entry);
1753 break;
1754
1755 case CAFEMENU_TREE_ITEM_ALIAS:
1756 {
1757 CafeMenuTreeItem *dir;
1758 dir = CAFEMENU_TREE_ITEM (CAFEMENU_TREE_ALIAS (item)->directory)((CafeMenuTreeItem *)(((CafeMenuTreeAlias *)(item))->directory
))
;
1759 name = cafemenu_tree_item_compare_get_name_helper (dir, flags);
1760 }
1761 break;
1762
1763 case CAFEMENU_TREE_ITEM_SEPARATOR:
1764 case CAFEMENU_TREE_ITEM_HEADER:
1765 default:
1766 g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "cafemenu-tree.c"
, 1766, ((const char*) (__func__)), ((void*)0)); } while (0)
;
1767 break;
1768 }
1769
1770 return name;
1771}
1772
1773static int
1774cafemenu_tree_item_compare (CafeMenuTreeItem *a,
1775 CafeMenuTreeItem *b,
1776 gpointer flags_p)
1777{
1778 const char *name_a;
1779 const char *name_b;
1780 CafeMenuTreeFlags flags;
1781
1782 flags = GPOINTER_TO_INT (flags_p)((gint) (glong) (flags_p));
1783
1784 name_a = cafemenu_tree_item_compare_get_name_helper (a, flags);
1785 name_b = cafemenu_tree_item_compare_get_name_helper (b, flags);
1786
1787 return g_utf8_collate (name_a, name_b);
1788}
1789
1790static MenuLayoutNode *
1791find_menu_child (MenuLayoutNode *layout)
1792{
1793 MenuLayoutNode *child;
1794
1795 child = menu_layout_node_get_children (layout);
1796 while (child && menu_layout_node_get_type (child) != MENU_LAYOUT_NODE_MENU)
1797 child = menu_layout_node_get_next (child);
1798
1799 return child;
1800}
1801
1802static void
1803merge_resolved_children (CafeMenuTree *tree,
1804 GHashTable *loaded_menu_files,
1805 MenuLayoutNode *where,
1806 MenuLayoutNode *from)
1807{
1808 MenuLayoutNode *insert_after;
1809 MenuLayoutNode *menu_child;
1810 MenuLayoutNode *from_child;
1811
1812 cafemenu_tree_resolve_files (tree, loaded_menu_files, from);
1813
1814 insert_after = where;
1815 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"
, 1815, ((const char*) (__func__)), "menu_layout_node_get_type (insert_after) != MENU_LAYOUT_NODE_ROOT"
); } while (0)
;
1816 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"
, 1816, ((const char*) (__func__)), "menu_layout_node_get_parent (insert_after) != NULL"
); } while (0)
;
1817
1818 /* skip root node */
1819 menu_child = find_menu_child (from);
1820 g_assert (menu_child != NULL)do { if (menu_child != ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 1820, ((const char*) (__func__
)), "menu_child != NULL"); } while (0)
;
1821 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"
, 1821, ((const char*) (__func__)), "menu_layout_node_get_type (menu_child) == MENU_LAYOUT_NODE_MENU"
); } while (0)
;
1822
1823 /* merge children of toplevel <Menu> */
1824 from_child = menu_layout_node_get_children (menu_child);
1825 while (from_child != NULL((void*)0))
1826 {
1827 MenuLayoutNode *next;
1828
1829 next = menu_layout_node_get_next (from_child);
1830
1831 menu_verbose ("Merging ");
1832 menu_debug_print_layout (from_child, FALSE);
1833 menu_verbose (" after ");
1834 menu_debug_print_layout (insert_after, FALSE);
1835
1836 switch (menu_layout_node_get_type (from_child))
1837 {
1838 case MENU_LAYOUT_NODE_NAME:
1839 menu_layout_node_unlink (from_child); /* delete this */
1840 break;
1841
1842 default:
1843 menu_layout_node_steal (from_child);
1844 menu_layout_node_insert_after (insert_after, from_child);
1845 menu_layout_node_unref (from_child);
1846
1847 insert_after = from_child;
1848 break;
1849 }
1850
1851 from_child = next;
1852 }
1853}
1854
1855static gboolean
1856load_merge_file (CafeMenuTree *tree,
1857 GHashTable *loaded_menu_files,
1858 const char *filename,
1859 gboolean is_canonical,
1860 gboolean add_monitor,
1861 MenuLayoutNode *where)
1862{
1863 MenuLayoutNode *to_merge;
1864 const char *canonical;
1865 char *freeme;
1866 gboolean retval;
1867
1868 freeme = NULL((void*)0);
1869 retval = FALSE(0);
1870
1871 if (!is_canonical)
1872 {
1873 canonical = freeme = realpath (filename, NULL((void*)0));
1874 if (canonical == NULL((void*)0))
1875 {
1876 if (add_monitor)
1877 cafemenu_tree_add_menu_file_monitor (tree,
1878 filename,
1879 MENU_FILE_MONITOR_NONEXISTENT_FILE);
1880
1881 menu_verbose ("Failed to canonicalize merge file path \"%s\": %s\n",
1882 filename, g_strerror (errno));
1883 goto out;
1884 }
1885 }
1886 else
1887 {
1888 canonical = filename;
1889 }
1890
1891 if (g_hash_table_lookup (loaded_menu_files, canonical) != NULL((void*)0))
1892 {
1893 g_warning ("Not loading \"%s\": recursive loop detected in .menu files",
1894 canonical);
1895 retval = TRUE(!(0));
1896 goto out;
1897 }
1898
1899 menu_verbose ("Merging file \"%s\"\n", canonical);
1900
1901 to_merge = menu_layout_load (canonical, tree->non_prefixed_basename, NULL((void*)0));
1902 if (to_merge == NULL((void*)0))
1903 {
1904 menu_verbose ("No menu for file \"%s\" found when merging\n",
1905 canonical);
1906 goto out;
1907 }
1908
1909 retval = TRUE(!(0));
1910
1911 g_hash_table_insert (loaded_menu_files, (char *) canonical, GUINT_TO_POINTER (TRUE)((gpointer) (gulong) ((!(0)))));
1912
1913 if (add_monitor)
1914 cafemenu_tree_add_menu_file_monitor (tree,
1915 canonical,
1916 MENU_FILE_MONITOR_FILE);
1917
1918 merge_resolved_children (tree, loaded_menu_files, where, to_merge);
1919
1920 g_hash_table_remove (loaded_menu_files, canonical);
1921
1922 menu_layout_node_unref (to_merge);
1923
1924 out:
1925 if (freeme)
1926 g_free (freeme);
1927
1928 return retval;
1929}
1930
1931static gboolean
1932load_merge_file_with_config_dir (CafeMenuTree *tree,
1933 GHashTable *loaded_menu_files,
1934 const char *menu_file,
1935 const char *config_dir,
1936 MenuLayoutNode *where)
1937{
1938 char *merge_file;
1939 gboolean loaded;
1940
1941 loaded = FALSE(0);
1942
1943 merge_file = g_build_filename (config_dir, "menus", menu_file, NULL((void*)0));
1944
1945 if (load_merge_file (tree, loaded_menu_files, merge_file, FALSE(0), TRUE(!(0)), where))
1946 loaded = TRUE(!(0));
1947
1948 g_free (merge_file);
1949
1950 return loaded;
1951}
1952
1953static gboolean
1954compare_basedir_to_config_dir (const char *canonical_basedir,
1955 const char *config_dir)
1956{
1957 char *dirname;
1958 char *canonical_menus_dir;
1959 gboolean retval;
1960
1961 menu_verbose ("Checking to see if basedir '%s' is in '%s'\n",
1962 canonical_basedir, config_dir);
1963
1964 dirname = g_build_filename (config_dir, "menus", NULL((void*)0));
1965
1966 retval = FALSE(0);
1967
1968 canonical_menus_dir = realpath (dirname, NULL((void*)0));
1969 if (canonical_menus_dir != NULL((void*)0) &&
1970 strcmp (canonical_basedir, canonical_menus_dir) == 0)
1971 {
1972 retval = TRUE(!(0));
1973 }
1974
1975 g_free (canonical_menus_dir);
1976 g_free (dirname);
1977
1978 return retval;
1979}
1980
1981static gboolean
1982load_parent_merge_file_from_basename (CafeMenuTree *tree,
1983 GHashTable *loaded_menu_files,
1984 MenuLayoutNode *layout,
1985 const char *menu_file,
1986 const char *canonical_basedir)
1987{
1988 gboolean found_basedir;
1989 const char * const *system_config_dirs;
1990 int i;
1991
1992 /* We're not interested in menu files that are in directories which are not a
1993 * parent of the base directory of this menu file */
1994 found_basedir = compare_basedir_to_config_dir (canonical_basedir,
1995 g_get_user_config_dir ());
1996
1997 system_config_dirs = g_get_system_config_dirs ();
1998
1999 i = 0;
2000 while (system_config_dirs[i] != NULL((void*)0))
2001 {
2002 if (!found_basedir)
2003 {
2004 found_basedir = compare_basedir_to_config_dir (canonical_basedir,
2005 system_config_dirs[i]);
2006 }
2007 else
2008 {
2009 menu_verbose ("Looking for parent menu file '%s' in '%s'\n",
2010 menu_file, system_config_dirs[i]);
2011
2012 if (load_merge_file_with_config_dir (tree,
2013 loaded_menu_files,
2014 menu_file,
2015 system_config_dirs[i],
2016 layout))
2017 {
2018 break;
2019 }
2020 }
2021
2022 ++i;
2023 }
2024
2025 return system_config_dirs[i] != NULL((void*)0);
2026}
2027
2028static gboolean load_parent_merge_file(CafeMenuTree* tree, GHashTable* loaded_menu_files, MenuLayoutNode* layout)
2029{
2030 MenuLayoutNode* root;
2031 const char* basedir;
2032 const char* menu_name;
2033 char* canonical_basedir;
2034 char* menu_file;
2035 gboolean found;
2036
2037 root = menu_layout_node_get_root(layout);
2038
2039 basedir = menu_layout_node_root_get_basedir(root);
2040 menu_name = menu_layout_node_root_get_name(root);
2041
2042 canonical_basedir = realpath (basedir, NULL((void*)0));
2043
2044 if (canonical_basedir == NULL((void*)0))
2045 {
2046 menu_verbose("Menu basedir '%s' no longer exists, not merging parent\n", basedir);
2047 return FALSE(0);
2048 }
2049
2050 found = FALSE(0);
2051 menu_file = g_strconcat(menu_name, ".menu", NULL((void*)0));
2052
2053 if (strcmp(menu_file, "cafe-applications.menu") == 0 && g_getenv("XDG_MENU_PREFIX"))
2054 {
2055 char* prefixed_basename;
2056 prefixed_basename = g_strdup_printf("%s%s", g_getenv("XDG_MENU_PREFIX"), menu_file);
2057 found = load_parent_merge_file_from_basename(tree, loaded_menu_files, layout, prefixed_basename, canonical_basedir);
2058 g_free(prefixed_basename);
2059 }
2060
2061 if (!found)
2062 {
2063 found = load_parent_merge_file_from_basename(tree, loaded_menu_files, layout, menu_file, canonical_basedir);
2064 }
2065
2066 g_free(menu_file);
2067 g_free(canonical_basedir);
2068
2069 return found;
2070}
2071
2072static void
2073load_merge_dir (CafeMenuTree *tree,
2074 GHashTable *loaded_menu_files,
2075 const char *dirname,
2076 MenuLayoutNode *where)
2077{
2078 GDir *dir;
2079 const char *menu_file;
2080
2081 menu_verbose ("Loading merge dir \"%s\"\n", dirname);
2082
2083 cafemenu_tree_add_menu_file_monitor (tree,
2084 dirname,
2085 MENU_FILE_MONITOR_DIRECTORY);
2086
2087 if ((dir = g_dir_open (dirname, 0, NULL((void*)0))) == NULL((void*)0))
2088 return;
2089
2090 while ((menu_file = g_dir_read_name (dir)))
2091 {
2092 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"
) )
)
2093 {
2094 char *full_path;
2095
2096 full_path = g_build_filename (dirname, menu_file, NULL((void*)0));
2097
2098 load_merge_file (tree, loaded_menu_files, full_path, TRUE(!(0)), FALSE(0), where);
2099
2100 g_free (full_path);
2101 }
2102 }
2103
2104 g_dir_close (dir);
2105}
2106
2107static void
2108load_merge_dir_with_config_dir (CafeMenuTree *tree,
2109 GHashTable *loaded_menu_files,
2110 const char *config_dir,
2111 const char *dirname,
2112 MenuLayoutNode *where)
2113{
2114 char *path;
2115
2116 path = g_build_filename (config_dir, "menus", dirname, NULL((void*)0));
2117
2118 load_merge_dir (tree, loaded_menu_files, path, where);
2119
2120 g_free (path);
2121}
2122
2123static void
2124resolve_merge_file (CafeMenuTree *tree,
2125 GHashTable *loaded_menu_files,
2126 MenuLayoutNode *layout)
2127{
2128 char *filename;
2129
2130 if (menu_layout_node_merge_file_get_type (layout) == MENU_MERGE_FILE_TYPE_PARENT)
2131 {
2132 if (load_parent_merge_file (tree, loaded_menu_files, layout))
2133 return;
2134 }
2135
2136 filename = menu_layout_node_get_content_as_path (layout);
2137 if (filename == NULL((void*)0))
2138 {
2139 menu_verbose ("didn't get node content as a path, not merging file\n");
2140 }
2141 else
2142 {
2143 load_merge_file (tree, loaded_menu_files, filename, FALSE(0), TRUE(!(0)), layout);
2144
2145 g_free (filename);
2146 }
2147
2148 /* remove the now-replaced node */
2149 menu_layout_node_unlink (layout);
2150}
2151
2152static void
2153resolve_merge_dir (CafeMenuTree *tree,
2154 GHashTable *loaded_menu_files,
2155 MenuLayoutNode *layout)
2156{
2157 char *path;
2158
2159 path = menu_layout_node_get_content_as_path (layout);
2160 if (path == NULL((void*)0))
2161 {
2162 menu_verbose ("didn't get layout node content as a path, not merging dir\n");
2163 }
2164 else
2165 {
2166 load_merge_dir (tree, loaded_menu_files, path, layout);
2167
2168 g_free (path);
2169 }
2170
2171 /* remove the now-replaced node */
2172 menu_layout_node_unlink (layout);
2173}
2174
2175static MenuLayoutNode *
2176add_app_dir (CafeMenuTree *tree G_GNUC_UNUSED__attribute__ ((__unused__)),
2177 MenuLayoutNode *before,
2178 const char *data_dir)
2179{
2180 MenuLayoutNode *tmp;
2181 char *dirname;
2182
2183 tmp = menu_layout_node_new (MENU_LAYOUT_NODE_APP_DIR);
2184 dirname = g_build_filename (data_dir, "applications", NULL((void*)0));
2185 menu_layout_node_set_content (tmp, dirname);
2186 menu_layout_node_insert_before (before, tmp);
2187 menu_layout_node_unref (before);
2188
2189 menu_verbose ("Adding <AppDir>%s</AppDir> in <DefaultAppDirs/>\n",
2190 dirname);
2191
2192 g_free (dirname);
2193
2194 return tmp;
2195}
2196
2197static void
2198resolve_default_app_dirs (CafeMenuTree *tree,
2199 MenuLayoutNode *layout)
2200{
2201 MenuLayoutNode *before;
2202 const char * const *system_data_dirs;
2203 int i;
2204
2205 system_data_dirs = g_get_system_data_dirs ();
2206
2207 before = add_app_dir (tree,
2208 menu_layout_node_ref (layout),
2209 g_get_user_data_dir ());
2210
2211 i = 0;
2212 while (system_data_dirs[i] != NULL((void*)0))
2213 {
2214 before = add_app_dir (tree, before, system_data_dirs[i]);
2215
2216 ++i;
2217 }
2218
2219 menu_layout_node_unref (before);
2220
2221 /* remove the now-replaced node */
2222 menu_layout_node_unlink (layout);
2223}
2224
2225static MenuLayoutNode* add_directory_dir (CafeMenuTree *tree G_GNUC_UNUSED__attribute__ ((__unused__)),
2226 MenuLayoutNode *before,
2227 const char *data_dir)
2228{
2229 MenuLayoutNode* tmp;
2230 char* dirname;
2231
2232 tmp = menu_layout_node_new(MENU_LAYOUT_NODE_DIRECTORY_DIR);
2233 dirname = g_build_filename(data_dir, "desktop-directories", NULL((void*)0));
2234 menu_layout_node_set_content(tmp, dirname);
2235 menu_layout_node_insert_before(before, tmp);
2236 menu_layout_node_unref(before);
2237
2238 menu_verbose("Adding <DirectoryDir>%s</DirectoryDir> in <DefaultDirectoryDirs/>\n", dirname);
2239
2240 g_free(dirname);
2241
2242 return tmp;
2243}
2244
2245/* According to desktop spec, since our menu file is called 'cafe-applications', our
2246 * merged menu folders need to be called 'cafe-applications-merged'. We'll setup the folder
2247 * 'applications-merged' if it doesn't exist yet, and a symlink pointing to it in the
2248 * ~/.config/menus directory
2249 */
2250static void
2251setup_merge_dir_symlink(void)
2252{
2253 gchar *user_config = (gchar *) g_get_user_config_dir();
2254 gchar *merge_path = g_build_filename (user_config, "menus", "applications-merged", NULL((void*)0));
2255 GFile *merge_file = g_file_new_for_path (merge_path);
2256 gchar *sym_path;
2257 GFile *sym_file;
2258
2259 g_file_make_directory_with_parents (merge_file, NULL((void*)0), NULL((void*)0));
2260
2261 sym_path = g_build_filename (user_config, "menus", "cafe-applications-merged", NULL((void*)0));
2262 sym_file = g_file_new_for_path (sym_path);
2263 if (!g_file_query_exists (sym_file, NULL((void*)0))) {
2264 g_file_make_symbolic_link (sym_file, merge_path, NULL((void*)0), NULL((void*)0));
2265 }
2266
2267 g_free (merge_path);
2268 g_free (sym_path);
2269 g_object_unref (merge_file);
2270 g_object_unref (sym_file);
2271}
2272
2273static void
2274resolve_default_directory_dirs (CafeMenuTree *tree,
2275 MenuLayoutNode *layout)
2276{
2277 MenuLayoutNode *before;
2278 const char * const *system_data_dirs;
2279 int i;
2280
2281 system_data_dirs = g_get_system_data_dirs ();
2282
2283 before = add_directory_dir (tree,
2284 menu_layout_node_ref (layout),
2285 g_get_user_data_dir ());
2286
2287 i = 0;
2288 while (system_data_dirs[i] != NULL((void*)0))
2289 {
2290 /* Parche para tomar las carpetas /cafe/ */
2291 char* path = g_build_filename(system_data_dirs[i], "cafe", NULL((void*)0));
2292 before = add_directory_dir(tree, before, path);
2293 g_free(path);
2294 /* /fin parche */
2295 before = add_directory_dir (tree, before, system_data_dirs[i]);
2296
2297 ++i;
2298 }
2299
2300 menu_layout_node_unref (before);
2301
2302 /* remove the now-replaced node */
2303 menu_layout_node_unlink (layout);
2304}
2305
2306static void
2307resolve_default_merge_dirs (CafeMenuTree *tree,
2308 GHashTable *loaded_menu_files,
2309 MenuLayoutNode *layout)
2310{
2311 MenuLayoutNode *root;
2312 const char *menu_name;
2313 char *merge_name;
2314 const char * const *system_config_dirs;
2315 int i;
2316
2317 setup_merge_dir_symlink();
2318
2319 root = menu_layout_node_get_root (layout);
2320 menu_name = menu_layout_node_root_get_name (root);
2321
2322 merge_name = g_strconcat (menu_name, "-merged", NULL((void*)0));
2323
2324 system_config_dirs = g_get_system_config_dirs ();
2325
2326 /* Merge in reverse order */
2327 i = 0;
2328 while (system_config_dirs[i] != NULL((void*)0)) i++;
2329 while (i > 0)
2330 {
2331 i--;
2332 load_merge_dir_with_config_dir (tree,
2333 loaded_menu_files,
2334 system_config_dirs[i],
2335 merge_name,
2336 layout);
2337 }
2338
2339 load_merge_dir_with_config_dir (tree,
2340 loaded_menu_files,
2341 g_get_user_config_dir (),
2342 merge_name,
2343 layout);
2344
2345 g_free (merge_name);
2346
2347 /* remove the now-replaced node */
2348 menu_layout_node_unlink (layout);
2349}
2350
2351static void
2352add_filename_include (const char *desktop_file_id,
2353 DesktopEntry *entry,
2354 MenuLayoutNode *include)
2355{
2356 if (!desktop_entry_has_categories (entry))
2357 {
2358 MenuLayoutNode *node;
2359
2360 node = menu_layout_node_new (MENU_LAYOUT_NODE_FILENAME);
2361 menu_layout_node_set_content (node, desktop_file_id);
2362
2363 menu_layout_node_append_child (include, node);
2364 menu_layout_node_unref (node);
2365 }
2366}
2367
2368static void
2369is_dot_directory (const char *basename,
2370 DesktopEntry *entry G_GNUC_UNUSED__attribute__ ((__unused__)),
2371 gboolean *has_dot_directory)
2372{
2373 if (!strcmp (basename, ".directory"))
2374 *has_dot_directory = TRUE(!(0));
2375}
2376
2377static gboolean
2378add_menu_for_legacy_dir (MenuLayoutNode *parent,
2379 const char *legacy_dir,
2380 const char *relative_path,
2381 const char *legacy_prefix,
2382 const char *menu_name)
2383{
2384 EntryDirectory *ed;
2385 DesktopEntrySet *desktop_entries;
2386 DesktopEntrySet *directory_entries;
2387 GSList *subdirs;
2388 gboolean menu_added;
2389 gboolean has_dot_directory;
2390
2391 ed = entry_directory_new_legacy (DESKTOP_ENTRY_INVALID, legacy_dir, legacy_prefix);
2392 if (!ed)
2393 return FALSE(0);
2394
2395 subdirs = NULL((void*)0);
2396 desktop_entries = desktop_entry_set_new ();
2397 directory_entries = desktop_entry_set_new ();
2398
2399 entry_directory_get_flat_contents (ed,
2400 desktop_entries,
2401 directory_entries,
2402 &subdirs);
2403 entry_directory_unref (ed);
2404
2405 has_dot_directory = FALSE(0);
2406 desktop_entry_set_foreach (directory_entries,
2407 (DesktopEntrySetForeachFunc) is_dot_directory,
2408 &has_dot_directory);
2409 desktop_entry_set_unref (directory_entries);
2410
2411 menu_added = FALSE(0);
2412 if (desktop_entry_set_get_count (desktop_entries) > 0 || subdirs)
2413 {
2414 MenuLayoutNode *menu;
2415 MenuLayoutNode *node;
2416 GString *subdir_path;
2417 GString *subdir_relative;
2418 GSList *tmp;
2419 int legacy_dir_len;
2420 int relative_path_len;
2421
2422 menu = menu_layout_node_new (MENU_LAYOUT_NODE_MENU);
2423 menu_layout_node_append_child (parent, menu);
2424
2425 menu_added = TRUE(!(0));
2426
2427 g_assert (menu_name != NULL)do { if (menu_name != ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 2427, ((const char*) (__func__
)), "menu_name != NULL"); } while (0)
;
2428
2429 node = menu_layout_node_new (MENU_LAYOUT_NODE_NAME);
2430 menu_layout_node_set_content (node, menu_name);
2431 menu_layout_node_append_child (menu, node);
2432 menu_layout_node_unref (node);
2433
2434 if (has_dot_directory)
2435 {
2436 node = menu_layout_node_new (MENU_LAYOUT_NODE_DIRECTORY);
2437 if (relative_path != NULL((void*)0))
2438 {
2439 char *directory_entry_path;
2440
2441 directory_entry_path = g_strdup_printf ("%s/.directory", relative_path);
2442 menu_layout_node_set_content (node, directory_entry_path);
2443 g_free (directory_entry_path);
2444 }
2445 else
2446 {
2447 menu_layout_node_set_content (node, ".directory");
2448 }
2449 menu_layout_node_append_child (menu, node);
2450 menu_layout_node_unref (node);
2451 }
2452
2453 if (desktop_entry_set_get_count (desktop_entries) > 0)
2454 {
2455 MenuLayoutNode *include;
2456
2457 include = menu_layout_node_new (MENU_LAYOUT_NODE_INCLUDE);
2458 menu_layout_node_append_child (menu, include);
2459
2460 desktop_entry_set_foreach (desktop_entries,
2461 (DesktopEntrySetForeachFunc) add_filename_include,
2462 include);
2463
2464 menu_layout_node_unref (include);
2465 }
2466
2467 subdir_path = g_string_new (legacy_dir);
2468 legacy_dir_len = strlen (legacy_dir);
2469
2470 subdir_relative = g_string_new (relative_path);
2471 relative_path_len = relative_path ? strlen (relative_path) : 0;
2472
2473 tmp = subdirs;
2474 while (tmp != NULL((void*)0))
2475 {
2476 const char *subdir = tmp->data;
2477
2478 g_string_append_c (subdir_path, G_DIR_SEPARATOR)g_string_append_c_inline (subdir_path, '/');
2479 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))
;
2480
2481 if (relative_path_len)
2482 {
2483 g_string_append_c (subdir_relative, G_DIR_SEPARATOR)g_string_append_c_inline (subdir_relative, '/');
2484 }
2485 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))
;
2486
2487 add_menu_for_legacy_dir (menu,
2488 subdir_path->str,
2489 subdir_relative->str,
2490 legacy_prefix,
2491 subdir);
2492
2493 g_string_truncate (subdir_relative, relative_path_len)g_string_truncate_inline (subdir_relative, relative_path_len);
2494 g_string_truncate (subdir_path, legacy_dir_len)g_string_truncate_inline (subdir_path, legacy_dir_len);
2495
2496 tmp = tmp->next;
2497 }
2498
2499 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)))))
;
2500 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)))))
;
2501
2502 menu_layout_node_unref (menu);
2503 }
2504
2505 desktop_entry_set_unref (desktop_entries);
2506
2507 g_slist_free_full (subdirs, g_free);
2508
2509 return menu_added;
2510}
2511
2512static void
2513resolve_legacy_dir (CafeMenuTree *tree,
2514 GHashTable *loaded_menu_files,
2515 MenuLayoutNode *legacy)
2516{
2517 MenuLayoutNode *to_merge;
2518 MenuLayoutNode *menu;
2519
2520 to_merge = menu_layout_node_new (MENU_LAYOUT_NODE_ROOT);
2521
2522 menu = menu_layout_node_get_parent (legacy);
2523 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"
, 2523, ((const char*) (__func__)), "menu_layout_node_get_type (menu) == MENU_LAYOUT_NODE_MENU"
); } while (0)
;
2524
2525 if (add_menu_for_legacy_dir (to_merge,
2526 menu_layout_node_get_content (legacy),
2527 NULL((void*)0),
2528 menu_layout_node_legacy_dir_get_prefix (legacy),
2529 menu_layout_node_menu_get_name (menu)))
2530 {
2531 merge_resolved_children (tree, loaded_menu_files, legacy, to_merge);
2532 }
2533
2534 menu_layout_node_unref (to_merge);
2535}
2536
2537static MenuLayoutNode *
2538add_legacy_dir (CafeMenuTree *tree,
2539 GHashTable *loaded_menu_files,
2540 MenuLayoutNode *before,
2541 const char *data_dir)
2542{
2543 MenuLayoutNode *legacy;
2544 char *dirname;
2545
2546 dirname = g_build_filename (data_dir, "applnk", NULL((void*)0));
2547
2548 legacy = menu_layout_node_new (MENU_LAYOUT_NODE_LEGACY_DIR);
2549 menu_layout_node_set_content (legacy, dirname);
2550 menu_layout_node_legacy_dir_set_prefix (legacy, "kde");
2551 menu_layout_node_insert_before (before, legacy);
2552 menu_layout_node_unref (before);
2553
2554 menu_verbose ("Adding <LegacyDir>%s</LegacyDir> in <KDELegacyDirs/>\n",
2555 dirname);
2556
2557 resolve_legacy_dir (tree, loaded_menu_files, legacy);
2558
2559 g_free (dirname);
2560
2561 return legacy;
2562}
2563
2564static void
2565resolve_kde_legacy_dirs (CafeMenuTree *tree,
2566 GHashTable *loaded_menu_files,
2567 MenuLayoutNode *layout)
2568{
2569 MenuLayoutNode *before;
2570 const char * const *system_data_dirs;
2571 int i;
2572
2573 system_data_dirs = g_get_system_data_dirs ();
2574
2575 before = add_legacy_dir (tree,
2576 loaded_menu_files,
2577 menu_layout_node_ref (layout),
2578 g_get_user_data_dir ());
2579
2580 i = 0;
2581 while (system_data_dirs[i] != NULL((void*)0))
2582 {
2583 before = add_legacy_dir (tree, loaded_menu_files, before, system_data_dirs[i]);
2584
2585 ++i;
2586 }
2587
2588 menu_layout_node_unref (before);
2589
2590 /* remove the now-replaced node */
2591 menu_layout_node_unlink (layout);
2592}
2593
2594static void
2595cafemenu_tree_resolve_files (CafeMenuTree *tree,
2596 GHashTable *loaded_menu_files,
2597 MenuLayoutNode *layout)
2598{
2599 MenuLayoutNode *child;
2600
2601 menu_verbose ("Resolving files in: ");
2602 menu_debug_print_layout (layout, TRUE);
2603
2604 switch (menu_layout_node_get_type (layout))
2605 {
2606 case MENU_LAYOUT_NODE_MERGE_FILE:
2607 resolve_merge_file (tree, loaded_menu_files, layout);
2608 break;
2609
2610 case MENU_LAYOUT_NODE_MERGE_DIR:
2611 resolve_merge_dir (tree, loaded_menu_files, layout);
2612 break;
2613
2614 case MENU_LAYOUT_NODE_DEFAULT_APP_DIRS:
2615 resolve_default_app_dirs (tree, layout);
2616 break;
2617
2618 case MENU_LAYOUT_NODE_DEFAULT_DIRECTORY_DIRS:
2619 resolve_default_directory_dirs (tree, layout);
2620 break;
2621
2622 case MENU_LAYOUT_NODE_DEFAULT_MERGE_DIRS:
2623 resolve_default_merge_dirs (tree, loaded_menu_files, layout);
2624 break;
2625
2626 case MENU_LAYOUT_NODE_LEGACY_DIR:
2627 resolve_legacy_dir (tree, loaded_menu_files, layout);
2628 break;
2629
2630 case MENU_LAYOUT_NODE_KDE_LEGACY_DIRS:
2631 resolve_kde_legacy_dirs (tree, loaded_menu_files, layout);
2632 break;
2633
2634 case MENU_LAYOUT_NODE_PASSTHROUGH:
2635 /* Just get rid of these, we don't need the memory usage */
2636 menu_layout_node_unlink (layout);
2637 break;
2638
2639 default:
2640 /* Recurse */
2641 child = menu_layout_node_get_children (layout);
2642 while (child != NULL((void*)0))
2643 {
2644 MenuLayoutNode *next = menu_layout_node_get_next (child);
2645
2646 cafemenu_tree_resolve_files (tree, loaded_menu_files, child);
2647
2648 child = next;
2649 }
2650 break;
2651 }
2652}
2653
2654static void
2655move_children (MenuLayoutNode *from,
2656 MenuLayoutNode *to)
2657{
2658 MenuLayoutNode *from_child;
2659 MenuLayoutNode *insert_before;
2660
2661 insert_before = menu_layout_node_get_children (to);
2662 from_child = menu_layout_node_get_children (from);
2663
2664 while (from_child != NULL((void*)0))
2665 {
2666 MenuLayoutNode *next;
2667
2668 next = menu_layout_node_get_next (from_child);
2669
2670 menu_layout_node_steal (from_child);
2671
2672 if (menu_layout_node_get_type (from_child) == MENU_LAYOUT_NODE_NAME)
2673 {
2674 ; /* just drop the Name in the old <Menu> */
2675 }
2676 else if (insert_before)
2677 {
2678 menu_layout_node_insert_before (insert_before, from_child);
2679 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"
, 2679, ((const char*) (__func__)), "menu_layout_node_get_next (from_child) == insert_before"
); } while (0)
;
2680 }
2681 else
2682 {
2683 menu_layout_node_append_child (to, from_child);
2684 }
2685
2686 menu_layout_node_unref (from_child);
2687
2688 from_child = next;
2689 }
2690}
2691
2692static int
2693null_safe_strcmp (const char *a,
2694 const char *b)
2695{
2696 if (a == NULL((void*)0) && b == NULL((void*)0))
2697 return 0;
2698 else if (a == NULL((void*)0))
2699 return -1;
2700 else if (b == NULL((void*)0))
2701 return 1;
2702 else
2703 return strcmp (a, b);
2704}
2705
2706static int
2707node_compare_func (const void *a,
2708 const void *b)
2709{
2710 MenuLayoutNode *node_a = (MenuLayoutNode*) a;
2711 MenuLayoutNode *node_b = (MenuLayoutNode*) b;
2712 MenuLayoutNodeType t_a = menu_layout_node_get_type (node_a);
2713 MenuLayoutNodeType t_b = menu_layout_node_get_type (node_b);
2714
2715 if (t_a < t_b)
2716 return -1;
2717 else if (t_a > t_b)
2718 return 1;
2719 else
2720 {
2721 const char *c_a = menu_layout_node_get_content (node_a);
2722 const char *c_b = menu_layout_node_get_content (node_b);
2723
2724 return null_safe_strcmp (c_a, c_b);
2725 }
2726}
2727
2728static int
2729node_menu_compare_func (const void *a,
2730 const void *b)
2731{
2732 MenuLayoutNode *node_a = (MenuLayoutNode*) a;
2733 MenuLayoutNode *node_b = (MenuLayoutNode*) b;
2734 MenuLayoutNode *parent_a = menu_layout_node_get_parent (node_a);
2735 MenuLayoutNode *parent_b = menu_layout_node_get_parent (node_b);
2736
2737 if (parent_a < parent_b)
2738 return -1;
2739 else if (parent_a > parent_b)
2740 return 1;
2741 else
2742 return null_safe_strcmp (menu_layout_node_menu_get_name (node_a),
2743 menu_layout_node_menu_get_name (node_b));
2744}
2745
2746static void
2747cafemenu_tree_strip_duplicate_children (CafeMenuTree *tree,
2748 MenuLayoutNode *layout)
2749{
2750 MenuLayoutNode *child;
2751 GSList *simple_nodes;
2752 GSList *menu_layout_nodes;
2753 GSList *prev;
2754 GSList *tmp;
2755
2756 /* to strip dups, we find all the child nodes where
2757 * we want to kill dups, sort them,
2758 * then nuke the adjacent nodes that are equal
2759 */
2760
2761 simple_nodes = NULL((void*)0);
2762 menu_layout_nodes = NULL((void*)0);
2763
2764 child = menu_layout_node_get_children (layout);
2765 while (child != NULL((void*)0))
2766 {
2767 switch (menu_layout_node_get_type (child))
2768 {
2769 /* These are dups if their content is the same */
2770 case MENU_LAYOUT_NODE_APP_DIR:
2771 case MENU_LAYOUT_NODE_DIRECTORY_DIR:
2772 case MENU_LAYOUT_NODE_DIRECTORY:
2773 simple_nodes = g_slist_prepend (simple_nodes, child);
2774 break;
2775
2776 /* These have to be merged in a more complicated way,
2777 * and then recursed
2778 */
2779 case MENU_LAYOUT_NODE_MENU:
2780 menu_layout_nodes = g_slist_prepend (menu_layout_nodes, child);
2781 break;
2782
2783 default:
2784 break;
2785 }
2786
2787 child = menu_layout_node_get_next (child);
2788 }
2789
2790 /* Note that the lists are all backward. So we want to keep
2791 * the items that are earlier in the list, because they were
2792 * later in the file
2793 */
2794
2795 /* stable sort the simple nodes */
2796 simple_nodes = g_slist_sort (simple_nodes,
2797 node_compare_func);
2798
2799 prev = NULL((void*)0);
2800 tmp = simple_nodes;
2801 while (tmp != NULL((void*)0))
2802 {
2803 GSList *next = tmp->next;
2804
2805 if (prev)
2806 {
2807 MenuLayoutNode *p = prev->data;
2808 MenuLayoutNode *n = tmp->data;
2809
2810 if (node_compare_func (p, n) == 0)
2811 {
2812 /* nuke it! */
2813 menu_layout_node_unlink (n);
2814 simple_nodes = g_slist_delete_link (simple_nodes, tmp);
2815 tmp = prev;
2816 }
2817 }
2818
2819 prev = tmp;
2820 tmp = next;
2821 }
2822
2823 g_slist_free (simple_nodes);
2824 simple_nodes = NULL((void*)0);
2825
2826 /* stable sort the menu nodes (the sort includes the
2827 * parents of the nodes in the comparison). Remember
2828 * the list is backward.
2829 */
2830 menu_layout_nodes = g_slist_sort (menu_layout_nodes,
2831 node_menu_compare_func);
2832
2833 prev = NULL((void*)0);
2834 tmp = menu_layout_nodes;
2835 while (tmp != NULL((void*)0))
2836 {
2837 GSList *next = tmp->next;
2838
2839 if (prev)
2840 {
2841 MenuLayoutNode *p = prev->data;
2842 MenuLayoutNode *n = tmp->data;
2843
2844 if (node_menu_compare_func (p, n) == 0)
2845 {
2846 /* Move children of first menu to the start of second
2847 * menu and nuke the first menu
2848 */
2849 move_children (n, p);
2850 menu_layout_node_unlink (n);
2851 menu_layout_nodes = g_slist_delete_link (menu_layout_nodes, tmp);
2852 tmp = prev;
2853 }
2854 }
2855
2856 prev = tmp;
2857 tmp = next;
2858 }
2859
2860 g_slist_free (menu_layout_nodes);
2861 menu_layout_nodes = NULL((void*)0);
2862
2863 /* Recursively clean up all children */
2864 child = menu_layout_node_get_children (layout);
2865 while (child != NULL((void*)0))
2866 {
2867 if (menu_layout_node_get_type (child) == MENU_LAYOUT_NODE_MENU)
2868 cafemenu_tree_strip_duplicate_children (tree, child);
2869
2870 child = menu_layout_node_get_next (child);
2871 }
2872}
2873
2874static MenuLayoutNode *
2875find_submenu (MenuLayoutNode *layout,
2876 const char *path,
2877 gboolean create_if_not_found)
2878{
2879 MenuLayoutNode *child;
2880 const char *slash;
2881 const char *next_path;
2882 char *name;
2883
2884 menu_verbose (" (splitting \"%s\")\n", path);
2885
2886 if (path[0] == '\0' || path[0] == G_DIR_SEPARATOR'/')
2887 return NULL((void*)0);
2888
2889 slash = strchr (path, G_DIR_SEPARATOR'/');
2890 if (slash != NULL((void*)0))
2891 {
2892 name = g_strndup (path, slash - path);
2893 next_path = slash + 1;
2894 if (*next_path == '\0')
2895 next_path = NULL((void*)0);
2896 }
2897 else
2898 {
2899 name = g_strdup (path)g_strdup_inline (path);
2900 next_path = NULL((void*)0);
2901 }
2902
2903 child = menu_layout_node_get_children (layout);
2904 while (child != NULL((void*)0))
2905 {
2906 switch (menu_layout_node_get_type (child))
2907 {
2908 case MENU_LAYOUT_NODE_MENU:
2909 {
2910 if (strcmp (name, menu_layout_node_menu_get_name (child)) == 0)
2911 {
2912 menu_verbose ("MenuNode %p found for path component \"%s\"\n",
2913 child, name);
2914
2915 g_free (name);
2916
2917 if (!next_path)
2918 {
2919 menu_verbose (" Found menu node %p parent is %p\n",
2920 child, layout);
2921 return child;
2922 }
2923
2924 return find_submenu (child, next_path, create_if_not_found);
2925 }
2926 }
2927 break;
2928
2929 default:
2930 break;
2931 }
2932
2933 child = menu_layout_node_get_next (child);
2934 }
2935
2936 if (create_if_not_found)
2937 {
2938 MenuLayoutNode *name_node;
2939
2940 child = menu_layout_node_new (MENU_LAYOUT_NODE_MENU);
2941 menu_layout_node_append_child (layout, child);
2942
2943 name_node = menu_layout_node_new (MENU_LAYOUT_NODE_NAME);
2944 menu_layout_node_set_content (name_node, name);
2945 menu_layout_node_append_child (child, name_node);
2946 menu_layout_node_unref (name_node);
2947
2948 menu_verbose (" Created menu node %p parent is %p\n",
2949 child, layout);
2950
2951 menu_layout_node_unref (child);
2952 g_free (name);
2953
2954 if (!next_path)
2955 return child;
2956
2957 return find_submenu (child, next_path, create_if_not_found);
2958 }
2959 else
2960 {
2961 g_free (name);
2962 return NULL((void*)0);
2963 }
2964}
2965
2966/* To call this you first have to strip duplicate children once,
2967 * otherwise when you move a menu Foo to Bar then you may only
2968 * move one of Foo, not all the merged Foo.
2969 */
2970static void
2971cafemenu_tree_execute_moves (CafeMenuTree *tree,
2972 MenuLayoutNode *layout,
2973 gboolean *need_remove_dups_p)
2974{
2975 MenuLayoutNode *child;
2976 gboolean need_remove_dups;
2977 GSList *move_nodes;
2978 GSList *tmp;
2979
2980 need_remove_dups = FALSE(0);
2981
2982 move_nodes = NULL((void*)0);
2983
2984 child = menu_layout_node_get_children (layout);
2985 while (child != NULL((void*)0))
2986 {
2987 switch (menu_layout_node_get_type (child))
2988 {
2989 case MENU_LAYOUT_NODE_MENU:
2990 /* Recurse - we recurse first and process the current node
2991 * second, as the spec dictates.
2992 */
2993 cafemenu_tree_execute_moves (tree, child, &need_remove_dups);
2994 break;
2995
2996 case MENU_LAYOUT_NODE_MOVE:
2997 move_nodes = g_slist_prepend (move_nodes, child);
2998 break;
2999
3000 default:
3001 break;
3002 }
3003
3004 child = menu_layout_node_get_next (child);
3005 }
3006
3007 /* We need to execute the move operations in the order that they appear */
3008 move_nodes = g_slist_reverse (move_nodes);
3009
3010 tmp = move_nodes;
3011 while (tmp != NULL((void*)0))
3012 {
3013 MenuLayoutNode *move_node = tmp->data;
3014 MenuLayoutNode *old_node;
3015 GSList *next = tmp->next;
3016 const char *old;
3017 const char *new;
3018
3019 old = menu_layout_node_move_get_old (move_node);
3020 new = menu_layout_node_move_get_new (move_node);
3021 g_assert (old != NULL && new != NULL)do { if (old != ((void*)0) && new != ((void*)0)) ; else
g_assertion_message_expr (((gchar*) 0), "cafemenu-tree.c", 3021
, ((const char*) (__func__)), "old != NULL && new != NULL"
); } while (0)
;
3022
3023 menu_verbose ("executing <Move> old = \"%s\" new = \"%s\"\n",
3024 old, new);
3025
3026 old_node = find_submenu (layout, old, FALSE(0));
3027 if (old_node != NULL((void*)0))
3028 {
3029 MenuLayoutNode *new_node;
3030
3031 /* here we can create duplicates anywhere below the
3032 * node
3033 */
3034 need_remove_dups = TRUE(!(0));
3035
3036 /* look up new node creating it and its parents if
3037 * required
3038 */
3039 new_node = find_submenu (layout, new, TRUE(!(0)));
3040 g_assert (new_node != NULL)do { if (new_node != ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 3040, ((const char*) (__func__
)), "new_node != NULL"); } while (0)
;
3041
3042 move_children (old_node, new_node);
3043
3044 menu_layout_node_unlink (old_node);
3045 }
3046
3047 menu_layout_node_unlink (move_node);
3048
3049 tmp = next;
3050 }
3051
3052 g_slist_free (move_nodes);
3053
3054 /* This oddness is to ensure we only remove dups once,
3055 * at the root, instead of recursing the tree over
3056 * and over.
3057 */
3058 if (need_remove_dups_p)
3059 *need_remove_dups_p = need_remove_dups;
3060 else if (need_remove_dups)
3061 cafemenu_tree_strip_duplicate_children (tree, layout);
3062}
3063
3064static gboolean
3065cafemenu_tree_load_layout (CafeMenuTree *tree,
3066 GError **error)
3067{
3068 GHashTable *loaded_menu_files;
3069
3070 if (tree->layout)
3071 return TRUE(!(0));
3072
3073 if (!cafemenu_tree_canonicalize_path (tree, error))
3074 return FALSE(0);
3075
3076 menu_verbose ("Loading menu layout from \"%s\"\n",
3077 tree->canonical_path);
3078
3079 tree->layout = menu_layout_load (tree->canonical_path,
3080 tree->non_prefixed_basename,
3081 error);
3082 if (!tree->layout)
3083 return FALSE(0);
3084
3085 loaded_menu_files = g_hash_table_new (g_str_hash, g_str_equal);
3086 g_hash_table_insert (loaded_menu_files, tree->canonical_path, GUINT_TO_POINTER (TRUE)((gpointer) (gulong) ((!(0)))));
3087 cafemenu_tree_resolve_files (tree, loaded_menu_files, tree->layout);
3088 g_hash_table_destroy (loaded_menu_files);
3089
3090 cafemenu_tree_strip_duplicate_children (tree, tree->layout);
3091 cafemenu_tree_execute_moves (tree, tree->layout, NULL((void*)0));
3092
3093 return TRUE(!(0));
3094}
3095
3096static void
3097cafemenu_tree_force_reload (CafeMenuTree *tree)
3098{
3099 cafemenu_tree_force_rebuild (tree);
3100
3101 if (tree->layout)
3102 menu_layout_node_unref (tree->layout);
3103 tree->layout = NULL((void*)0);
3104}
3105
3106typedef struct
3107{
3108 DesktopEntrySet *set;
3109 const char *category;
3110} GetByCategoryForeachData;
3111
3112static void
3113get_by_category_foreach (const char *file_id,
3114 DesktopEntry *entry,
3115 GetByCategoryForeachData *data)
3116{
3117 if (desktop_entry_has_category (entry, data->category))
3118 desktop_entry_set_add_entry (data->set, entry, file_id);
3119}
3120
3121static void
3122get_by_category (DesktopEntrySet *entry_pool,
3123 DesktopEntrySet *set,
3124 const char *category)
3125{
3126 GetByCategoryForeachData data;
3127
3128 data.set = set;
3129 data.category = category;
3130
3131 desktop_entry_set_foreach (entry_pool,
3132 (DesktopEntrySetForeachFunc) get_by_category_foreach,
3133 &data);
3134}
3135
3136static DesktopEntrySet *
3137process_include_rules (MenuLayoutNode *layout,
3138 DesktopEntrySet *entry_pool)
3139{
3140 DesktopEntrySet *set = NULL((void*)0);
3141
3142 switch (menu_layout_node_get_type (layout))
3143 {
3144 case MENU_LAYOUT_NODE_AND:
3145 {
3146 MenuLayoutNode *child;
3147
3148 menu_verbose ("Processing <And>\n");
3149
3150 child = menu_layout_node_get_children (layout);
3151 while (child != NULL((void*)0))
3152 {
3153 DesktopEntrySet *child_set;
3154
3155 child_set = process_include_rules (child, entry_pool);
3156
3157 if (set == NULL((void*)0))
3158 {
3159 set = child_set;
3160 }
3161 else
3162 {
3163 desktop_entry_set_intersection (set, child_set);
3164 desktop_entry_set_unref (child_set);
3165 }
3166
3167 /* as soon as we get empty results, we can bail,
3168 * because it's an AND
3169 */
3170 if (desktop_entry_set_get_count (set) == 0)
3171 break;
3172
3173 child = menu_layout_node_get_next (child);
3174 }
3175 menu_verbose ("Processed <And>\n");
3176 }
3177 break;
3178
3179 case MENU_LAYOUT_NODE_OR:
3180 {
3181 MenuLayoutNode *child;
3182
3183 menu_verbose ("Processing <Or>\n");
3184
3185 child = menu_layout_node_get_children (layout);
3186 while (child != NULL((void*)0))
3187 {
3188 DesktopEntrySet *child_set;
3189
3190 child_set = process_include_rules (child, entry_pool);
3191
3192 if (set == NULL((void*)0))
3193 {
3194 set = child_set;
3195 }
3196 else
3197 {
3198 desktop_entry_set_union (set, child_set);
3199 desktop_entry_set_unref (child_set);
3200 }
3201
3202 child = menu_layout_node_get_next (child);
3203 }
3204 menu_verbose ("Processed <Or>\n");
3205 }
3206 break;
3207
3208 case MENU_LAYOUT_NODE_NOT:
3209 {
3210 /* First get the OR of all the rules */
3211 MenuLayoutNode *child;
3212
3213 menu_verbose ("Processing <Not>\n");
3214
3215 child = menu_layout_node_get_children (layout);
3216 while (child != NULL((void*)0))
3217 {
3218 DesktopEntrySet *child_set;
3219
3220 child_set = process_include_rules (child, entry_pool);
3221
3222 if (set == NULL((void*)0))
3223 {
3224 set = child_set;
3225 }
3226 else
3227 {
3228 desktop_entry_set_union (set, child_set);
3229 desktop_entry_set_unref (child_set);
3230 }
3231
3232 child = menu_layout_node_get_next (child);
3233 }
3234
3235 if (set != NULL((void*)0))
3236 {
3237 DesktopEntrySet *inverted;
3238
3239 /* Now invert the result */
3240 inverted = desktop_entry_set_new ();
3241 desktop_entry_set_union (inverted, entry_pool);
3242 desktop_entry_set_subtract (inverted, set);
3243 desktop_entry_set_unref (set);
3244 set = inverted;
3245 }
3246 menu_verbose ("Processed <Not>\n");
3247 }
3248 break;
3249
3250 case MENU_LAYOUT_NODE_ALL:
3251 menu_verbose ("Processing <All>\n");
3252 set = desktop_entry_set_new ();
3253 desktop_entry_set_union (set, entry_pool);
3254 menu_verbose ("Processed <All>\n");
3255 break;
3256
3257 case MENU_LAYOUT_NODE_FILENAME:
3258 {
3259 DesktopEntry *entry;
3260
3261 menu_verbose ("Processing <Filename>%s</Filename>\n",
3262 menu_layout_node_get_content (layout));
3263
3264 entry = desktop_entry_set_lookup (entry_pool,
3265 menu_layout_node_get_content (layout));
3266 if (entry != NULL((void*)0))
3267 {
3268 set = desktop_entry_set_new ();
3269 desktop_entry_set_add_entry (set,
3270 entry,
3271 menu_layout_node_get_content (layout));
3272 }
3273 menu_verbose ("Processed <Filename>%s</Filename>\n",
3274 menu_layout_node_get_content (layout));
3275 }
3276 break;
3277
3278 case MENU_LAYOUT_NODE_CATEGORY:
3279 menu_verbose ("Processing <Category>%s</Category>\n",
3280 menu_layout_node_get_content (layout));
3281 set = desktop_entry_set_new ();
3282 get_by_category (entry_pool, set, menu_layout_node_get_content (layout));
3283 menu_verbose ("Processed <Category>%s</Category>\n",
3284 menu_layout_node_get_content (layout));
3285 break;
3286
3287 default:
3288 break;
3289 }
3290
3291 if (set == NULL((void*)0))
3292 set = desktop_entry_set_new (); /* create an empty set */
3293
3294 menu_verbose ("Matched %d entries\n", desktop_entry_set_get_count (set));
3295
3296 return set;
3297}
3298
3299static void
3300collect_layout_info (MenuLayoutNode *layout,
3301 GSList **layout_info)
3302{
3303 MenuLayoutNode *iter;
3304
3305 g_slist_free_full (*layout_info, (GDestroyNotify) menu_layout_node_unref);
3306 *layout_info = NULL((void*)0);
3307
3308 iter = menu_layout_node_get_children (layout);
3309 while (iter != NULL((void*)0))
3310 {
3311 switch (menu_layout_node_get_type (iter))
3312 {
3313 case MENU_LAYOUT_NODE_MENUNAME:
3314 case MENU_LAYOUT_NODE_FILENAME:
3315 case MENU_LAYOUT_NODE_SEPARATOR:
3316 case MENU_LAYOUT_NODE_MERGE:
3317 *layout_info = g_slist_prepend (*layout_info,
3318 menu_layout_node_ref (iter));
3319 break;
3320
3321 default:
3322 break;
3323 }
3324
3325 iter = menu_layout_node_get_next (iter);
3326 }
3327
3328 *layout_info = g_slist_reverse (*layout_info);
3329}
3330
3331static void
3332entries_listify_foreach (const char *desktop_file_id,
3333 DesktopEntry *desktop_entry,
3334 CafeMenuTreeDirectory *directory)
3335{
3336 directory->entries =
3337 g_slist_prepend (directory->entries,
3338 cafemenu_tree_entry_new (directory,
3339 desktop_entry,
3340 desktop_file_id,
3341 FALSE(0),
3342 FALSE(0)));
3343}
3344
3345static void
3346excluded_entries_listify_foreach (const char *desktop_file_id,
3347 DesktopEntry *desktop_entry,
3348 CafeMenuTreeDirectory *directory)
3349{
3350 directory->entries =
3351 g_slist_prepend (directory->entries,
3352 cafemenu_tree_entry_new (directory,
3353 desktop_entry,
3354 desktop_file_id,
3355 TRUE(!(0)),
3356 FALSE(0)));
3357}
3358
3359static void
3360unallocated_entries_listify_foreach (const char *desktop_file_id,
3361 DesktopEntry *desktop_entry,
3362 CafeMenuTreeDirectory *directory)
3363{
3364 directory->entries =
3365 g_slist_prepend (directory->entries,
3366 cafemenu_tree_entry_new (directory,
3367 desktop_entry,
3368 desktop_file_id,
3369 FALSE(0),
3370 TRUE(!(0))));
3371}
3372
3373static void
3374set_default_layout_values (CafeMenuTreeDirectory *parent,
3375 CafeMenuTreeDirectory *child)
3376{
3377 GSList *tmp;
3378
3379 /* if the child has a defined default layout, we don't want to override its
3380 * values. The parent might have a non-defined layout info (ie, no child of
3381 * the DefaultLayout node) but it doesn't meant the default layout values
3382 * (ie, DefaultLayout attributes) aren't different from the global defaults.
3383 */
3384 if (child->default_layout_info != NULL((void*)0) ||
3385 child->default_layout_values.mask != MENU_LAYOUT_VALUES_NONE)
3386 return;
3387
3388 child->default_layout_values = parent->default_layout_values;
3389
3390 tmp = child->subdirs;
3391 while (tmp != NULL((void*)0))
3392 {
3393 CafeMenuTreeDirectory *subdir = tmp->data;
3394
3395 set_default_layout_values (child, subdir);
3396
3397 tmp = tmp->next;
3398 }
3399}
3400
3401static CafeMenuTreeDirectory *
3402process_layout (CafeMenuTree *tree,
3403 CafeMenuTreeDirectory *parent,
3404 MenuLayoutNode *layout,
3405 DesktopEntrySet *allocated)
3406{
3407 MenuLayoutNode *layout_iter;
3408 CafeMenuTreeDirectory *directory;
3409 DesktopEntrySet *entry_pool;
3410 DesktopEntrySet *entries;
3411 DesktopEntrySet *allocated_set;
3412 DesktopEntrySet *excluded_set;
3413 gboolean deleted;
3414 gboolean only_unallocated;
3415 GSList *tmp;
3416
3417 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"
, 3417, ((const char*) (__func__)), "menu_layout_node_get_type (layout) == MENU_LAYOUT_NODE_MENU"
); } while (0)
;
3418 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"
, 3418, ((const char*) (__func__)), "menu_layout_node_menu_get_name (layout) != NULL"
); } while (0)
;
3419
3420 directory = cafemenu_tree_directory_new (tree, parent,
3421 menu_layout_node_menu_get_name (layout));
3422
3423 menu_verbose ("=== Menu name = %s ===\n", directory->name);
3424
3425
3426 deleted = FALSE(0);
3427 only_unallocated = FALSE(0);
3428
3429 entries = desktop_entry_set_new ();
3430 allocated_set = desktop_entry_set_new ();
3431
3432 if (tree->flags & CAFEMENU_TREE_FLAGS_INCLUDE_EXCLUDED)
3433 excluded_set = desktop_entry_set_new ();
3434 else
3435 excluded_set = NULL((void*)0);
3436
3437 entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (layout));
3438
3439 layout_iter = menu_layout_node_get_children (layout);
3440 while (layout_iter != NULL((void*)0))
3441 {
3442 switch (menu_layout_node_get_type (layout_iter))
3443 {
3444 case MENU_LAYOUT_NODE_MENU:
3445 /* recurse */
3446 {
3447 CafeMenuTreeDirectory *child_dir;
3448
3449 menu_verbose ("Processing <Menu>\n");
3450
3451 child_dir = process_layout (tree,
3452 directory,
3453 layout_iter,
3454 allocated);
3455 if (child_dir)
3456 directory->subdirs = g_slist_prepend (directory->subdirs,
3457 child_dir);
3458
3459 menu_verbose ("Processed <Menu>\n");
3460 }
3461 break;
3462
3463 case MENU_LAYOUT_NODE_INCLUDE:
3464 {
3465 /* The match rule children of the <Include> are
3466 * independent (logical OR) so we can process each one by
3467 * itself
3468 */
3469 MenuLayoutNode *rule;
3470
3471 menu_verbose ("Processing <Include> (%d entries)\n",
3472 desktop_entry_set_get_count (entries));
3473
3474 rule = menu_layout_node_get_children (layout_iter);
3475 while (rule != NULL((void*)0))
3476 {
3477 DesktopEntrySet *rule_set;
3478
3479 rule_set = process_include_rules (rule, entry_pool);
3480 if (rule_set != NULL((void*)0))
3481 {
3482 desktop_entry_set_union (entries, rule_set);
3483 desktop_entry_set_union (allocated_set, rule_set);
3484 if (excluded_set != NULL((void*)0))
3485 desktop_entry_set_subtract (excluded_set, rule_set);
3486 desktop_entry_set_unref (rule_set);
3487 }
3488
3489 rule = menu_layout_node_get_next (rule);
3490 }
3491
3492 menu_verbose ("Processed <Include> (%d entries)\n",
3493 desktop_entry_set_get_count (entries));
3494 }
3495 break;
3496
3497 case MENU_LAYOUT_NODE_EXCLUDE:
3498 {
3499 /* The match rule children of the <Exclude> are
3500 * independent (logical OR) so we can process each one by
3501 * itself
3502 */
3503 MenuLayoutNode *rule;
3504
3505 menu_verbose ("Processing <Exclude> (%d entries)\n",
3506 desktop_entry_set_get_count (entries));
3507
3508 rule = menu_layout_node_get_children (layout_iter);
3509 while (rule != NULL((void*)0))
3510 {
3511 DesktopEntrySet *rule_set;
3512
3513 rule_set = process_include_rules (rule, entry_pool);
3514 if (rule_set != NULL((void*)0))
3515 {
3516 if (excluded_set != NULL((void*)0))
3517 desktop_entry_set_union (excluded_set, rule_set);
3518 desktop_entry_set_subtract (entries, rule_set);
3519 desktop_entry_set_unref (rule_set);
3520 }
3521
3522 rule = menu_layout_node_get_next (rule);
3523 }
3524
3525 menu_verbose ("Processed <Exclude> (%d entries)\n",
3526 desktop_entry_set_get_count (entries));
3527 }
3528 break;
3529
3530 case MENU_LAYOUT_NODE_DIRECTORY:
3531 {
3532 DesktopEntry *entry;
3533
3534 menu_verbose ("Processing <Directory>%s</Directory>\n",
3535 menu_layout_node_get_content (layout_iter));
3536
3537 /*
3538 * The last <Directory> to exist wins, so we always try overwriting
3539 */
3540 entry = entry_directory_list_get_directory (menu_layout_node_menu_get_directory_dirs (layout),
3541 menu_layout_node_get_content (layout_iter));
3542
3543 if (entry != NULL((void*)0))
3544 {
3545 if (!desktop_entry_get_hidden (entry))
3546 {
3547 if (directory->directory_entry)
3548 desktop_entry_unref (directory->directory_entry);
3549 directory->directory_entry = entry; /* pass ref ownership */
3550 }
3551 else
3552 {
3553 desktop_entry_unref (entry);
3554 }
3555 }
3556
3557 menu_verbose ("Processed <Directory> new directory entry = %p (%s)\n",
3558 directory->directory_entry,
3559 directory->directory_entry? desktop_entry_get_path (directory->directory_entry) : "null");
3560 }
3561 break;
3562
3563 case MENU_LAYOUT_NODE_DELETED:
3564 menu_verbose ("Processed <Deleted/>\n");
3565 deleted = TRUE(!(0));
3566 break;
3567
3568 case MENU_LAYOUT_NODE_NOT_DELETED:
3569 menu_verbose ("Processed <NotDeleted/>\n");
3570 deleted = FALSE(0);
3571 break;
3572
3573 case MENU_LAYOUT_NODE_ONLY_UNALLOCATED:
3574 menu_verbose ("Processed <OnlyUnallocated/>\n");
3575 only_unallocated = TRUE(!(0));
3576 break;
3577
3578 case MENU_LAYOUT_NODE_NOT_ONLY_UNALLOCATED:
3579 menu_verbose ("Processed <NotOnlyUnallocated/>\n");
3580 only_unallocated = FALSE(0);
3581 break;
3582
3583 case MENU_LAYOUT_NODE_DEFAULT_LAYOUT:
3584 menu_layout_node_default_layout_get_values (layout_iter,
3585 &directory->default_layout_values);
3586 collect_layout_info (layout_iter, &directory->default_layout_info);
3587 menu_verbose ("Processed <DefaultLayout/>\n");
3588 break;
3589
3590 case MENU_LAYOUT_NODE_LAYOUT:
3591 collect_layout_info (layout_iter, &directory->layout_info);
3592 menu_verbose ("Processed <Layout/>\n");
3593 break;
3594
3595 default:
3596 break;
3597 }
3598
3599 layout_iter = menu_layout_node_get_next (layout_iter);
3600 }
3601
3602 desktop_entry_set_unref (entry_pool);
3603
3604 directory->only_unallocated = only_unallocated;
3605
3606 if (!directory->only_unallocated)
3607 desktop_entry_set_union (allocated, allocated_set);
3608
3609 desktop_entry_set_unref (allocated_set);
3610
3611 if (directory->directory_entry)
3612 {
3613 if (desktop_entry_get_no_display (directory->directory_entry))
3614 {
3615 directory->is_nodisplay = TRUE(!(0));
3616
3617 if (!(tree->flags & CAFEMENU_TREE_FLAGS_INCLUDE_NODISPLAY))
3618 {
3619 menu_verbose ("Not showing menu %s because NoDisplay=true\n",
3620 desktop_entry_get_name (directory->directory_entry));
3621 deleted = TRUE(!(0));
3622 }
3623 }
3624
3625 if (!desktop_entry_get_show_in (directory->directory_entry))
3626 {
3627 menu_verbose ("Not showing menu %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n",
3628 desktop_entry_get_name (directory->directory_entry));
3629 deleted = TRUE(!(0));
3630 }
3631 }
3632
3633 if (deleted)
3634 {
3635 if (excluded_set != NULL((void*)0))
3636 desktop_entry_set_unref (excluded_set);
3637 desktop_entry_set_unref (entries);
3638 cafemenu_tree_item_unref (directory);
3639 return NULL((void*)0);
3640 }
3641
3642 desktop_entry_set_foreach (entries,
3643 (DesktopEntrySetForeachFunc) entries_listify_foreach,
3644 directory);
3645 desktop_entry_set_unref (entries);
3646
3647 if (excluded_set != NULL((void*)0))
3648 {
3649 desktop_entry_set_foreach (excluded_set,
3650 (DesktopEntrySetForeachFunc) excluded_entries_listify_foreach,
3651 directory);
3652 desktop_entry_set_unref (excluded_set);
3653 }
3654
3655 tmp = directory->subdirs;
3656 while (tmp != NULL((void*)0))
3657 {
3658 CafeMenuTreeDirectory *subdir = tmp->data;
3659
3660 set_default_layout_values (directory, subdir);
3661
3662 tmp = tmp->next;
3663 }
3664
3665 tmp = directory->entries;
3666 while (tmp != NULL((void*)0))
3667 {
3668 CafeMenuTreeEntry *entry = tmp->data;
3669 GSList *next = tmp->next;
3670 gboolean delete = FALSE(0);
3671
3672 /* If adding a new condition to delete here, it has to be added to
3673 * get_still_unallocated_foreach() too */
3674
3675 if (desktop_entry_get_hidden (entry->desktop_entry))
3676 {
3677 menu_verbose ("Deleting %s because Hidden=true\n",
3678 desktop_entry_get_name (entry->desktop_entry));
3679 delete = TRUE(!(0));
3680 }
3681
3682 if (!(tree->flags & CAFEMENU_TREE_FLAGS_INCLUDE_NODISPLAY) &&
3683 desktop_entry_get_no_display (entry->desktop_entry))
3684 {
3685 menu_verbose ("Deleting %s because NoDisplay=true\n",
3686 desktop_entry_get_name (entry->desktop_entry));
3687 delete = TRUE(!(0));
3688 }
3689
3690 if (!desktop_entry_get_show_in (entry->desktop_entry))
3691 {
3692 menu_verbose ("Deleting %s because OnlyShowIn!=$DESKTOP or NotShowIn=$DESKTOP (with $DESKTOP=${XDG_CURRENT_DESKTOP:-GNOME})\n",
3693 desktop_entry_get_name (entry->desktop_entry));
3694 delete = TRUE(!(0));
3695 }
3696
3697 /* No need to filter out based on TryExec since GDesktopAppInfo cannot
3698 * deal with .desktop files with a failed TryExec. */
3699
3700 if (delete)
3701 {
3702 directory->entries = g_slist_delete_link (directory->entries,
3703 tmp);
3704 cafemenu_tree_item_unref_and_unset_parent (entry);
3705 }
3706
3707 tmp = next;
3708 }
3709
3710 g_assert (directory->name != NULL)do { if (directory->name != ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 3710, ((const char*) (__func__
)), "directory->name != NULL"); } while (0)
;
3711
3712 return directory;
3713}
3714
3715static void
3716process_only_unallocated (CafeMenuTree *tree,
3717 CafeMenuTreeDirectory *directory,
3718 DesktopEntrySet *allocated,
3719 DesktopEntrySet *unallocated_used)
3720{
3721 GSList *tmp;
3722
3723 /* For any directory marked only_unallocated, we have to remove any
3724 * entries that were in fact allocated.
3725 */
3726
3727 if (directory->only_unallocated)
3728 {
3729 tmp = directory->entries;
3730 while (tmp != NULL((void*)0))
3731 {
3732 CafeMenuTreeEntry *entry = tmp->data;
3733 GSList *next = tmp->next;
3734
3735 if (desktop_entry_set_lookup (allocated, entry->desktop_file_id))
3736 {
3737 directory->entries = g_slist_delete_link (directory->entries,
3738 tmp);
3739 cafemenu_tree_item_unref_and_unset_parent (entry);
3740 }
3741 else
3742 {
3743 desktop_entry_set_add_entry (unallocated_used, entry->desktop_entry, entry->desktop_file_id);
3744 }
3745
3746 tmp = next;
3747 }
3748 }
3749
3750 tmp = directory->subdirs;
3751 while (tmp != NULL((void*)0))
3752 {
3753 CafeMenuTreeDirectory *subdir = tmp->data;
3754
3755 process_only_unallocated (tree, subdir, allocated, unallocated_used);
3756
3757 tmp = tmp->next;
3758 }
3759}
3760
3761typedef struct
3762{
3763 CafeMenuTree *tree;
3764 DesktopEntrySet *allocated;
3765 DesktopEntrySet *unallocated_used;
3766 DesktopEntrySet *still_unallocated;
3767} GetStillUnallocatedForeachData;
3768
3769static void
3770get_still_unallocated_foreach (const char *file_id,
3771 DesktopEntry *entry,
3772 GetStillUnallocatedForeachData *data)
3773{
3774 if (desktop_entry_set_lookup (data->allocated, file_id))
3775 return;
3776
3777 if (desktop_entry_set_lookup (data->unallocated_used, file_id))
3778 return;
3779
3780 /* Same rules than at the end of process_layout() */
3781 if (desktop_entry_get_hidden (entry))
3782 return;
3783
3784 if (!(data->tree->flags & CAFEMENU_TREE_FLAGS_INCLUDE_NODISPLAY) &&
3785 desktop_entry_get_no_display (entry))
3786 return;
3787
3788 if (!desktop_entry_get_show_in (entry))
3789 return;
3790
3791 desktop_entry_set_add_entry (data->still_unallocated, entry, file_id);
3792}
3793
3794static void preprocess_layout_info (CafeMenuTree *tree,
3795 CafeMenuTreeDirectory *directory);
3796
3797static GSList *
3798get_layout_info (CafeMenuTreeDirectory *directory,
3799 gboolean *is_default_layout)
3800{
3801 CafeMenuTreeDirectory *iter;
3802
3803 if (directory->layout_info != NULL((void*)0))
3804 {
3805 if (is_default_layout)
3806 {
3807 *is_default_layout = FALSE(0);
3808 }
3809 return directory->layout_info;
3810 }
3811
3812 /* Even if there's no layout information at all, the result will be an
3813 * implicit default layout */
3814 if (is_default_layout)
3815 {
3816 *is_default_layout = TRUE(!(0));
3817 }
3818
3819 iter = directory;
3820 while (iter != NULL((void*)0))
3821 {
3822 /* FIXME: this is broken: we might skip real parent in the
3823 * XML structure, that are hidden because of inlining. */
3824 if (iter->default_layout_info != NULL((void*)0))
3825 {
3826 return iter->default_layout_info;
3827 }
3828
3829 iter = CAFEMENU_TREE_ITEM (iter)((CafeMenuTreeItem *)(iter))->parent;
3830 }
3831
3832 return NULL((void*)0);
3833}
3834
3835static void
3836get_values_with_defaults (MenuLayoutNode *node,
3837 MenuLayoutValues *layout_values,
3838 MenuLayoutValues *default_layout_values)
3839{
3840 menu_layout_node_menuname_get_values (node, layout_values);
3841
3842 if (!(layout_values->mask & MENU_LAYOUT_VALUES_SHOW_EMPTY))
3843 layout_values->show_empty = default_layout_values->show_empty;
3844
3845 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_MENUS))
3846 layout_values->inline_menus = default_layout_values->inline_menus;
3847
3848 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_LIMIT))
3849 layout_values->inline_limit = default_layout_values->inline_limit;
3850
3851 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_HEADER))
3852 layout_values->inline_header = default_layout_values->inline_header;
3853
3854 if (!(layout_values->mask & MENU_LAYOUT_VALUES_INLINE_ALIAS))
3855 layout_values->inline_alias = default_layout_values->inline_alias;
3856}
3857
3858static guint
3859get_real_subdirs_len (CafeMenuTreeDirectory *directory)
3860{
3861 guint len;
3862 GSList *tmp;
3863
3864 len = 0;
3865
3866 tmp = directory->subdirs;
3867 while (tmp != NULL((void*)0))
3868 {
3869 CafeMenuTreeDirectory *subdir = tmp->data;
3870
3871 tmp = tmp->next;
3872
3873 if (subdir->will_inline_header != G_MAXUINT16((guint16) 0xffff))
3874 {
3875 len += get_real_subdirs_len (subdir) + g_slist_length (subdir->entries) + 1;
3876 }
3877 else
3878 len += 1;
3879 }
3880
3881 return len;
3882}
3883
3884static void
3885preprocess_layout_info_subdir_helper (CafeMenuTree *tree,
3886 CafeMenuTreeDirectory *directory,
3887 CafeMenuTreeDirectory *subdir,
3888 MenuLayoutValues *layout_values,
3889 gboolean *contents_added,
3890 gboolean *should_remove)
3891{
3892 preprocess_layout_info (tree, subdir);
3893
3894 *should_remove = FALSE(0);
3895 *contents_added = FALSE(0);
3896
3897 if (subdir->subdirs == NULL((void*)0) && subdir->entries == NULL((void*)0))
3898 {
3899 if (!(tree->flags & CAFEMENU_TREE_FLAGS_SHOW_EMPTY) &&
3900 !layout_values->show_empty)
3901 {
3902 menu_verbose ("Not showing empty menu '%s'\n", subdir->name);
3903 *should_remove = TRUE(!(0));
3904 }
3905 }
3906
3907 else if (layout_values->inline_menus)
3908 {
3909 guint real_subdirs_len;
3910
3911 real_subdirs_len = get_real_subdirs_len (subdir);
3912
3913 if (layout_values->inline_alias &&
3914 real_subdirs_len + g_slist_length (subdir->entries) == 1)
3915 {
3916 CafeMenuTreeAlias *alias;
3917 CafeMenuTreeItem *item;
3918 GSList *list;
3919
3920 if (subdir->subdirs != NULL((void*)0))
3921 list = subdir->subdirs;
3922 else
3923 list = subdir->entries;
3924
3925 item = CAFEMENU_TREE_ITEM (list->data)((CafeMenuTreeItem *)(list->data));
3926
3927 menu_verbose ("Inline aliasing '%s' to '%s'\n",
3928 item->type == CAFEMENU_TREE_ITEM_ENTRY ?
3929 g_app_info_get_name (G_APP_INFO (cafemenu_tree_entry_get_app_info (CAFEMENU_TREE_ENTRY (item)))) :
3930 (item->type == CAFEMENU_TREE_ITEM_DIRECTORY ?
3931 cafemenu_tree_directory_get_name (CAFEMENU_TREE_DIRECTORY (item)) :
3932 cafemenu_tree_directory_get_name (CAFEMENU_TREE_ALIAS (item)->directory)),
3933 subdir->name);
3934
3935 alias = cafemenu_tree_alias_new (directory, subdir, item);
3936
3937 g_slist_free_full (list, (GDestroyNotify) cafemenu_tree_item_unref_and_unset_parent);
3938 subdir->subdirs = NULL((void*)0);
3939 subdir->entries = NULL((void*)0);
3940
3941 if (item->type == CAFEMENU_TREE_ITEM_DIRECTORY)
3942 directory->subdirs = g_slist_append (directory->subdirs, alias);
3943 else
3944 directory->entries = g_slist_append (directory->entries, alias);
3945
3946 *contents_added = TRUE(!(0));
3947 *should_remove = TRUE(!(0));
3948 }
3949
3950 else if (layout_values->inline_limit == 0 ||
3951 layout_values->inline_limit >= real_subdirs_len + g_slist_length (subdir->entries))
3952 {
3953 if (layout_values->inline_header)
3954 {
3955 menu_verbose ("Creating inline header with name '%s'\n", subdir->name);
3956 /* we're limited to 16-bits to spare some memory; if the limit is
3957 * higher than that (would be crazy), we just consider it's
3958 * unlimited */
3959 if (layout_values->inline_limit < G_MAXUINT16((guint16) 0xffff))
3960 subdir->will_inline_header = layout_values->inline_limit;
3961 else
3962 subdir->will_inline_header = 0;
3963 }
3964 else
3965 {
3966 g_slist_foreach (subdir->subdirs,
3967 (GFunc) cafemenu_tree_item_set_parent,
3968 directory);
3969 directory->subdirs = g_slist_concat (directory->subdirs,
3970 subdir->subdirs);
3971 subdir->subdirs = NULL((void*)0);
3972
3973 g_slist_foreach (subdir->entries,
3974 (GFunc) cafemenu_tree_item_set_parent,
3975 directory);
3976 directory->entries = g_slist_concat (directory->entries,
3977 subdir->entries);
3978 subdir->entries = NULL((void*)0);
3979
3980 *contents_added = TRUE(!(0));
3981 *should_remove = TRUE(!(0));
3982 }
3983
3984 menu_verbose ("Inlining directory contents of '%s' to '%s'\n",
3985 subdir->name, directory->name);
3986 }
3987 }
3988}
3989
3990static void
3991preprocess_layout_info (CafeMenuTree *tree,
3992 CafeMenuTreeDirectory *directory)
3993{
3994 GSList *tmp;
3995 GSList *layout_info;
3996 gboolean using_default_layout;
3997 GSList *last_subdir;
3998 gboolean strip_duplicates;
3999 gboolean contents_added;
4000 gboolean should_remove;
4001 GSList *subdirs_sentinel;
4002
4003 /* Note: we need to preprocess all menus, even if the layout mask for a menu
4004 * is MENU_LAYOUT_VALUES_NONE: in this case, we need to remove empty menus;
4005 * and the layout mask can be different for a submenu anyway */
4006
4007 menu_verbose ("Processing menu layout inline hints for %s\n", directory->name);
4008 g_assert (!directory->preprocessed)do { if (!directory->preprocessed) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 4008, ((const char*) (__func__
)), "!directory->preprocessed"); } while (0)
;
4009
4010 strip_duplicates = FALSE(0);
4011 /* we use last_subdir to track the last non-inlined subdirectory */
4012 last_subdir = g_slist_last (directory->subdirs);
4013
4014 /*
4015 * First process subdirectories with explicit layout
4016 */
4017 layout_info = get_layout_info (directory, &using_default_layout);
4018 tmp = layout_info;
4019 /* see comment below about Menuname to understand why we leave the loop if
4020 * last_subdir is NULL */
4021 while (tmp != NULL((void*)0) && last_subdir != NULL((void*)0))
4022 {
4023 MenuLayoutNode *node = tmp->data;
4024 MenuLayoutValues layout_values;
4025 const char *name;
4026 CafeMenuTreeDirectory *subdir;
4027 GSList *subdir_l;
4028
4029 tmp = tmp->next;
4030
4031 /* only Menuname nodes are relevant here */
4032 if (menu_layout_node_get_type (node) != MENU_LAYOUT_NODE_MENUNAME)
4033 continue;
4034
4035 get_values_with_defaults (node,
4036 &layout_values,
4037 &directory->default_layout_values);
4038
4039 /* find the subdirectory that is affected by those attributes */
4040 name = menu_layout_node_get_content (node);
4041 subdir = NULL((void*)0);
4042 subdir_l = directory->subdirs;
4043 while (subdir_l != NULL((void*)0))
4044 {
4045 subdir = subdir_l->data;
4046
4047 if (!strcmp (subdir->name, name))
4048 break;
4049
4050 subdir = NULL((void*)0);
4051 subdir_l = subdir_l->next;
4052
4053 /* We do not want to use Menuname on a menu that appeared via
4054 * inlining: without inlining, the Menuname wouldn't have matched
4055 * anything, and we want to keep the same behavior.
4056 * Unless the layout is a default layout, in which case the Menuname
4057 * does match the subdirectory. */
4058 if (!using_default_layout && subdir_l == last_subdir)
4059 {
4060 subdir_l = NULL((void*)0);
4061 break;
4062 }
4063 }
4064
4065 if (subdir == NULL((void*)0))
4066 continue;
4067
4068 preprocess_layout_info_subdir_helper (tree, directory,
4069 subdir, &layout_values,
4070 &contents_added, &should_remove);
4071 strip_duplicates = strip_duplicates || contents_added;
4072 if (should_remove)
4073 {
4074 if (last_subdir == subdir_l)
4075 {
4076 /* we need to recompute last_subdir since we'll remove it from
4077 * the list */
4078 GSList *buf;
4079
4080 if (subdir_l == directory->subdirs)
4081 last_subdir = NULL((void*)0);
4082 else
4083 {
4084 buf = directory->subdirs;
4085 while (buf != NULL((void*)0) && buf->next != subdir_l)
4086 buf = buf->next;
4087 last_subdir = buf;
4088 }
4089 }
4090
4091 directory->subdirs = g_slist_remove (directory->subdirs, subdir);
4092 cafemenu_tree_item_unref_and_unset_parent (CAFEMENU_TREE_ITEM (subdir)((CafeMenuTreeItem *)(subdir)));
4093 }
4094 }
4095
4096 /*
4097 * Now process the subdirectories with no explicit layout
4098 */
4099 /* this is bogus data, but we just need the pointer anyway */
4100 subdirs_sentinel = g_slist_prepend (directory->subdirs, PACKAGE"cafe-menus");
4101 directory->subdirs = subdirs_sentinel;
4102
4103 tmp = directory->subdirs;
4104 while (tmp->next != NULL((void*)0))
4105 {
4106 CafeMenuTreeDirectory *subdir = tmp->next->data;
4107
4108 if (subdir->preprocessed)
4109 {
4110 tmp = tmp->next;
4111 continue;
4112 }
4113
4114 preprocess_layout_info_subdir_helper (tree, directory,
4115 subdir, &directory->default_layout_values,
4116 &contents_added, &should_remove);
4117 strip_duplicates = strip_duplicates || contents_added;
4118 if (should_remove)
4119 {
4120 tmp = g_slist_delete_link (tmp, tmp->next);
4121 cafemenu_tree_item_unref_and_unset_parent (CAFEMENU_TREE_ITEM (subdir)((CafeMenuTreeItem *)(subdir)));
4122 }
4123 else
4124 tmp = tmp->next;
4125 }
4126
4127 /* remove the sentinel */
4128 directory->subdirs = g_slist_delete_link (directory->subdirs,
4129 directory->subdirs);
4130
4131 /*
4132 * Finally, remove duplicates if needed
4133 */
4134 if (strip_duplicates)
4135 {
4136 /* strip duplicate entries; there should be no duplicate directories */
4137 directory->entries = g_slist_sort (directory->entries,
4138 (GCompareFunc) cafemenu_tree_entry_compare_by_id);
4139 tmp = directory->entries;
4140 while (tmp != NULL((void*)0) && tmp->next != NULL((void*)0))
4141 {
4142 CafeMenuTreeItem *a = tmp->data;
4143 CafeMenuTreeItem *b = tmp->next->data;
4144
4145 if (a->type == CAFEMENU_TREE_ITEM_ALIAS)
4146 a = CAFEMENU_TREE_ALIAS (a)((CafeMenuTreeAlias *)(a))->aliased_item;
4147
4148 if (b->type == CAFEMENU_TREE_ITEM_ALIAS)
4149 b = CAFEMENU_TREE_ALIAS (b)((CafeMenuTreeAlias *)(b))->aliased_item;
4150
4151 if (strcmp (CAFEMENU_TREE_ENTRY (a)((CafeMenuTreeEntry *)(a))->desktop_file_id,
4152 CAFEMENU_TREE_ENTRY (b)((CafeMenuTreeEntry *)(b))->desktop_file_id) == 0)
4153 {
4154 tmp = g_slist_delete_link (tmp, tmp->next);
4155 cafemenu_tree_item_unref (b);
4156 }
4157 else
4158 tmp = tmp->next;
4159 }
4160 }
4161
4162 directory->preprocessed = TRUE(!(0));
4163}
4164
4165static void process_layout_info (CafeMenuTree *tree,
4166 CafeMenuTreeDirectory *directory);
4167
4168static void
4169check_pending_separator (CafeMenuTreeDirectory *directory)
4170{
4171 if (directory->layout_pending_separator)
4172 {
4173 menu_verbose ("Adding pending separator in '%s'\n", directory->name);
4174
4175 directory->contents = g_slist_append (directory->contents,
4176 cafemenu_tree_separator_new (directory));
4177 directory->layout_pending_separator = FALSE(0);
4178 }
4179}
4180
4181static void
4182merge_alias (CafeMenuTree *tree,
4183 CafeMenuTreeDirectory *directory,
4184 CafeMenuTreeAlias *alias)
4185{
4186 menu_verbose ("Merging alias '%s' in directory '%s'\n",
4187 alias->directory->name, directory->name);
4188
4189 if (alias->aliased_item->type == CAFEMENU_TREE_ITEM_DIRECTORY)
4190 {
4191 process_layout_info (tree, CAFEMENU_TREE_DIRECTORY (alias->aliased_item)((CafeMenuTreeDirectory *)(alias->aliased_item)));
4192 }
4193
4194 check_pending_separator (directory);
4195
4196 directory->contents = g_slist_append (directory->contents,
4197 cafemenu_tree_item_ref (alias));
4198}
4199
4200static void
4201merge_subdir (CafeMenuTree *tree,
4202 CafeMenuTreeDirectory *directory,
4203 CafeMenuTreeDirectory *subdir)
4204{
4205 menu_verbose ("Merging subdir '%s' in directory '%s'\n",
4206 subdir->name, directory->name);
4207
4208 process_layout_info (tree, subdir);
4209
4210 check_pending_separator (directory);
4211
4212 if (subdir->will_inline_header == 0 ||
4213 (subdir->will_inline_header != G_MAXUINT16((guint16) 0xffff) &&
4214 g_slist_length (subdir->contents) <= subdir->will_inline_header))
4215 {
4216 CafeMenuTreeHeader *header;
4217
4218 header = cafemenu_tree_header_new (directory, subdir);
4219 directory->contents = g_slist_append (directory->contents, header);
4220
4221 g_slist_foreach (subdir->contents,
4222 (GFunc) cafemenu_tree_item_set_parent,
4223 directory);
4224 directory->contents = g_slist_concat (directory->contents,
4225 subdir->contents);
4226 subdir->contents = NULL((void*)0);
4227 subdir->will_inline_header = G_MAXUINT16((guint16) 0xffff);
4228
4229 cafemenu_tree_item_set_parent (CAFEMENU_TREE_ITEM (subdir)((CafeMenuTreeItem *)(subdir)), NULL((void*)0));
4230 }
4231 else
4232 {
4233 directory->contents = g_slist_append (directory->contents,
4234 cafemenu_tree_item_ref (subdir));
4235 }
4236}
4237
4238static void
4239merge_subdir_by_name (CafeMenuTree *tree,
4240 CafeMenuTreeDirectory *directory,
4241 const char *subdir_name)
4242{
4243 GSList *tmp;
4244
4245 menu_verbose ("Attempting to merge subdir '%s' in directory '%s'\n",
4246 subdir_name, directory->name);
4247
4248 tmp = directory->subdirs;
4249 while (tmp != NULL((void*)0))
4250 {
4251 CafeMenuTreeDirectory *subdir = tmp->data;
4252 GSList *next = tmp->next;
4253
4254 /* if it's an alias, then it cannot be affected by
4255 * the Merge nodes in the layout */
4256 if (CAFEMENU_TREE_ITEM (subdir)((CafeMenuTreeItem *)(subdir))->type == CAFEMENU_TREE_ITEM_ALIAS)
4257 continue;
4258
4259 if (!strcmp (subdir->name, subdir_name))
4260 {
4261 directory->subdirs = g_slist_delete_link (directory->subdirs, tmp);
4262 merge_subdir (tree, directory, subdir);
4263 cafemenu_tree_item_unref (subdir);
4264 }
4265
4266 tmp = next;
4267 }
4268}
4269
4270static void
4271merge_entry (CafeMenuTree *tree G_GNUC_UNUSED__attribute__ ((__unused__)),
4272 CafeMenuTreeDirectory *directory,
4273 CafeMenuTreeEntry *entry)
4274{
4275 menu_verbose ("Merging entry '%s' in directory '%s'\n",
4276 entry->desktop_file_id, directory->name);
4277
4278 check_pending_separator (directory);
4279 directory->contents = g_slist_append (directory->contents,
4280 cafemenu_tree_item_ref (entry));
4281}
4282
4283static void
4284merge_entry_by_id (CafeMenuTree *tree,
4285 CafeMenuTreeDirectory *directory,
4286 const char *file_id)
4287{
4288 GSList *tmp;
4289
4290 menu_verbose ("Attempting to merge entry '%s' in directory '%s'\n",
4291 file_id, directory->name);
4292
4293 tmp = directory->entries;
4294 while (tmp != NULL((void*)0))
4295 {
4296 CafeMenuTreeEntry *entry = tmp->data;
4297 GSList *next = tmp->next;
4298
4299 /* if it's an alias, then it cannot be affected by
4300 * the Merge nodes in the layout */
4301 if (CAFEMENU_TREE_ITEM (entry)((CafeMenuTreeItem *)(entry))->type == CAFEMENU_TREE_ITEM_ALIAS)
4302 continue;
4303
4304 if (!strcmp (entry->desktop_file_id, file_id))
4305 {
4306 directory->entries = g_slist_delete_link (directory->entries, tmp);
4307 merge_entry (tree, directory, entry);
4308 cafemenu_tree_item_unref (entry);
4309 }
4310
4311 tmp = next;
4312 }
4313}
4314
4315static inline gboolean
4316find_name_in_list (const char *name,
4317 GSList *list)
4318{
4319 while (list != NULL((void*)0))
4320 {
4321 if (!strcmp (name, list->data))
4322 return TRUE(!(0));
4323
4324 list = list->next;
4325 }
4326
4327 return FALSE(0);
4328}
4329
4330static void
4331merge_subdirs (CafeMenuTree *tree,
4332 CafeMenuTreeDirectory *directory,
4333 GSList *except)
4334{
4335 GSList *subdirs;
4336 GSList *tmp;
4337
4338 menu_verbose ("Merging subdirs in directory '%s'\n", directory->name);
4339
4340 subdirs = directory->subdirs;
4341 directory->subdirs = NULL((void*)0);
4342
4343 subdirs = g_slist_sort_with_data (subdirs,
4344 (GCompareDataFunc) cafemenu_tree_item_compare,
4345 GINT_TO_POINTER (CAFEMENU_TREE_FLAGS_NONE)((gpointer) (glong) (CAFEMENU_TREE_FLAGS_NONE)));
4346
4347 tmp = subdirs;
4348 while (tmp != NULL((void*)0))
4349 {
4350 CafeMenuTreeDirectory *subdir = tmp->data;
4351
4352 if (CAFEMENU_TREE_ITEM (subdir)((CafeMenuTreeItem *)(subdir))->type == CAFEMENU_TREE_ITEM_ALIAS)
4353 {
4354 merge_alias (tree, directory, CAFEMENU_TREE_ALIAS (subdir)((CafeMenuTreeAlias *)(subdir)));
4355 cafemenu_tree_item_unref (subdir);
4356 }
4357 else if (!find_name_in_list (subdir->name, except))
4358 {
4359 merge_subdir (tree, directory, subdir);
4360 cafemenu_tree_item_unref (subdir);
4361 }
4362 else
4363 {
4364 menu_verbose ("Not merging directory '%s' yet\n", subdir->name);
4365 directory->subdirs = g_slist_append (directory->subdirs, subdir);
4366 }
4367
4368 tmp = tmp->next;
4369 }
4370
4371 g_slist_free (subdirs);
4372 g_slist_free (except);
4373}
4374
4375static void
4376merge_entries (CafeMenuTree *tree,
4377 CafeMenuTreeDirectory *directory,
4378 GSList *except)
4379{
4380 GSList *entries;
4381 GSList *tmp;
4382
4383 menu_verbose ("Merging entries in directory '%s'\n", directory->name);
4384
4385 entries = directory->entries;
4386 directory->entries = NULL((void*)0);
4387
4388 entries = g_slist_sort_with_data (entries,
4389 (GCompareDataFunc) cafemenu_tree_item_compare,
4390 GINT_TO_POINTER (tree->flags)((gpointer) (glong) (tree->flags)));
4391
4392 tmp = entries;
4393 while (tmp != NULL((void*)0))
4394 {
4395 CafeMenuTreeEntry *entry = tmp->data;
4396
4397 if (CAFEMENU_TREE_ITEM (entry)((CafeMenuTreeItem *)(entry))->type == CAFEMENU_TREE_ITEM_ALIAS)
4398 {
4399 merge_alias (tree, directory, CAFEMENU_TREE_ALIAS (entry)((CafeMenuTreeAlias *)(entry)));
4400 cafemenu_tree_item_unref (entry);
4401 }
4402 else if (!find_name_in_list (entry->desktop_file_id, except))
4403 {
4404 merge_entry (tree, directory, entry);
4405 cafemenu_tree_item_unref (entry);
4406 }
4407 else
4408 {
4409 menu_verbose ("Not merging entry '%s' yet\n", entry->desktop_file_id);
4410 directory->entries = g_slist_append (directory->entries, entry);
4411 }
4412
4413 tmp = tmp->next;
4414 }
4415
4416 g_slist_free (entries);
4417 g_slist_free (except);
4418}
4419
4420static void
4421merge_subdirs_and_entries (CafeMenuTree *tree,
4422 CafeMenuTreeDirectory *directory,
4423 GSList *except_subdirs,
4424 GSList *except_entries)
4425{
4426 GSList *items;
4427 GSList *tmp;
4428
4429 menu_verbose ("Merging subdirs and entries together in directory %s\n",
4430 directory->name);
4431
4432 items = g_slist_concat (directory->subdirs, directory->entries);
4433
4434 directory->subdirs = NULL((void*)0);
4435 directory->entries = NULL((void*)0);
4436
4437 items = g_slist_sort_with_data (items,
4438 (GCompareDataFunc) cafemenu_tree_item_compare,
4439 GINT_TO_POINTER (tree->flags)((gpointer) (glong) (tree->flags)));
4440
4441 tmp = items;
4442 while (tmp != NULL((void*)0))
4443 {
4444 CafeMenuTreeItem *item = tmp->data;
4445 CafeMenuTreeItemType type;
4446
4447 type = item->type;
4448
4449 if (type == CAFEMENU_TREE_ITEM_ALIAS)
4450 {
4451 merge_alias (tree, directory, CAFEMENU_TREE_ALIAS (item)((CafeMenuTreeAlias *)(item)));
4452 cafemenu_tree_item_unref (item);
4453 }
4454 else if (type == CAFEMENU_TREE_ITEM_DIRECTORY)
4455 {
4456 if (!find_name_in_list (CAFEMENU_TREE_DIRECTORY (item)((CafeMenuTreeDirectory *)(item))->name, except_subdirs))
4457 {
4458 merge_subdir (tree,
4459 directory,
4460 CAFEMENU_TREE_DIRECTORY (item)((CafeMenuTreeDirectory *)(item)));
4461 cafemenu_tree_item_unref (item);
4462 }
4463 else
4464 {
4465 menu_verbose ("Not merging directory '%s' yet\n",
4466 CAFEMENU_TREE_DIRECTORY (item)->name);
4467 directory->subdirs = g_slist_append (directory->subdirs, item);
4468 }
4469 }
4470 else if (type == CAFEMENU_TREE_ITEM_ENTRY)
4471 {
4472 if (!find_name_in_list (CAFEMENU_TREE_ENTRY (item)((CafeMenuTreeEntry *)(item))->desktop_file_id, except_entries))
4473 {
4474 merge_entry (tree, directory, CAFEMENU_TREE_ENTRY (item)((CafeMenuTreeEntry *)(item)));
4475 cafemenu_tree_item_unref (item);
4476 }
4477 else
4478 {
4479 menu_verbose ("Not merging entry '%s' yet\n",
4480 CAFEMENU_TREE_ENTRY (item)->desktop_file_id);
4481 directory->entries = g_slist_append (directory->entries, item);
4482 }
4483 }
4484 else
4485 {
4486 g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "cafemenu-tree.c"
, 4486, ((const char*) (__func__)), ((void*)0)); } while (0)
;
4487 }
4488
4489 tmp = tmp->next;
4490 }
4491
4492 g_slist_free (items);
4493 g_slist_free (except_subdirs);
4494 g_slist_free (except_entries);
4495}
4496
4497static GSList *
4498get_subdirs_from_layout_info (GSList *layout_info)
4499{
4500 GSList *subdirs;
4501 GSList *tmp;
4502
4503 subdirs = NULL((void*)0);
4504
4505 tmp = layout_info;
4506 while (tmp != NULL((void*)0))
4507 {
4508 MenuLayoutNode *node = tmp->data;
4509
4510 if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_MENUNAME)
4511 {
4512 subdirs = g_slist_append (subdirs,
4513 (char *) menu_layout_node_get_content (node));
4514 }
4515
4516 tmp = tmp->next;
4517 }
4518
4519 return subdirs;
4520}
4521
4522static GSList *
4523get_entries_from_layout_info (GSList *layout_info)
4524{
4525 GSList *entries;
4526 GSList *tmp;
4527
4528 entries = NULL((void*)0);
4529
4530 tmp = layout_info;
4531 while (tmp != NULL((void*)0))
4532 {
4533 MenuLayoutNode *node = tmp->data;
4534
4535 if (menu_layout_node_get_type (node) == MENU_LAYOUT_NODE_FILENAME)
4536 {
4537 entries = g_slist_append (entries,
4538 (char *) menu_layout_node_get_content (node));
4539 }
4540
4541 tmp = tmp->next;
4542 }
4543
4544 return entries;
4545}
4546
4547static void
4548process_layout_info (CafeMenuTree *tree,
4549 CafeMenuTreeDirectory *directory)
4550{
4551 GSList *layout_info;
4552
4553 menu_verbose ("Processing menu layout hints for %s\n", directory->name);
4554
4555 g_slist_free_full (directory->contents, (GDestroyNotify) cafemenu_tree_item_unref_and_unset_parent);
4556 directory->contents = NULL((void*)0);
4557 directory->layout_pending_separator = FALSE(0);
4558
4559 layout_info = get_layout_info (directory, NULL((void*)0));
4560
4561 if (layout_info == NULL((void*)0))
4562 {
4563 merge_subdirs (tree, directory, NULL((void*)0));
4564 merge_entries (tree, directory, NULL((void*)0));
4565 }
4566 else
4567 {
4568 GSList *tmp;
4569
4570 tmp = layout_info;
4571 while (tmp != NULL((void*)0))
4572 {
4573 MenuLayoutNode *node = tmp->data;
4574
4575 switch (menu_layout_node_get_type (node))
4576 {
4577 case MENU_LAYOUT_NODE_MENUNAME:
4578 merge_subdir_by_name (tree,
4579 directory,
4580 menu_layout_node_get_content (node));
4581 break;
4582
4583 case MENU_LAYOUT_NODE_FILENAME:
4584 merge_entry_by_id (tree,
4585 directory,
4586 menu_layout_node_get_content (node));
4587 break;
4588
4589 case MENU_LAYOUT_NODE_SEPARATOR:
4590 /* Unless explicitly told to show all separators, do not show a
4591 * separator at the beginning of a menu. Note that we don't add
4592 * the separators now, and instead make it pending. This way, we
4593 * won't show two consecutive separators nor will we show a
4594 * separator at the end of a menu. */
4595 if (tree->flags & CAFEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS)
4596 {
4597 directory->layout_pending_separator = TRUE(!(0));
4598 check_pending_separator (directory);
4599 }
4600 else if (directory->contents)
4601 {
4602 menu_verbose ("Adding a potential separator in '%s'\n",
4603 directory->name);
4604
4605 directory->layout_pending_separator = TRUE(!(0));
4606 }
4607 else
4608 {
4609 menu_verbose ("Skipping separator at the beginning of '%s'\n",
4610 directory->name);
4611 }
4612 break;
4613
4614 case MENU_LAYOUT_NODE_MERGE:
4615 switch (menu_layout_node_merge_get_type (node))
4616 {
4617 case MENU_LAYOUT_MERGE_NONE:
4618 break;
4619
4620 case MENU_LAYOUT_MERGE_MENUS:
4621 merge_subdirs (tree,
4622 directory,
4623 get_subdirs_from_layout_info (tmp->next));
4624 break;
4625
4626 case MENU_LAYOUT_MERGE_FILES:
4627 merge_entries (tree,
4628 directory,
4629 get_entries_from_layout_info (tmp->next));
4630 break;
4631
4632 case MENU_LAYOUT_MERGE_ALL:
4633 merge_subdirs_and_entries (tree,
4634 directory,
4635 get_subdirs_from_layout_info (tmp->next),
4636 get_entries_from_layout_info (tmp->next));
4637 break;
4638
4639 default:
4640 g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "cafemenu-tree.c"
, 4640, ((const char*) (__func__)), ((void*)0)); } while (0)
;
4641 break;
4642 }
4643 break;
4644
4645 default:
4646 g_assert_not_reached ()do { g_assertion_message_expr (((gchar*) 0), "cafemenu-tree.c"
, 4646, ((const char*) (__func__)), ((void*)0)); } while (0)
;
4647 break;
4648 }
4649
4650 tmp = tmp->next;
4651 }
4652 }
4653
4654 g_slist_free_full (directory->subdirs, (GDestroyNotify) cafemenu_tree_item_unref);
4655 directory->subdirs = NULL((void*)0);
4656
4657 g_slist_free_full (directory->entries, (GDestroyNotify) cafemenu_tree_item_unref);
4658 directory->entries = NULL((void*)0);
4659
4660 g_slist_free_full (directory->default_layout_info, (GDestroyNotify) menu_layout_node_unref);
4661 directory->default_layout_info = NULL((void*)0);
4662
4663 g_slist_free_full (directory->layout_info, (GDestroyNotify) menu_layout_node_unref);
4664 directory->layout_info = NULL((void*)0);
4665}
4666
4667static void
4668handle_entries_changed (MenuLayoutNode *layout,
4669 CafeMenuTree *tree)
4670{
4671 if (tree->layout == layout)
4672 {
4673 cafemenu_tree_force_rebuild (tree);
4674 cafemenu_tree_invoke_monitors (tree);
4675 }
4676}
4677
4678static void
4679update_entry_index (CafeMenuTree *tree,
4680 CafeMenuTreeDirectory *dir)
4681{
4682 CafeMenuTreeIter *iter = cafemenu_tree_directory_iter (dir);
4683 CafeMenuTreeItemType next_type;
4684
4685 while ((next_type = cafemenu_tree_iter_next (iter)) != CAFEMENU_TREE_ITEM_INVALID)
4686 {
4687 gpointer item = NULL((void*)0);
4688
4689 switch (next_type)
4690 {
4691 case CAFEMENU_TREE_ITEM_ENTRY:
4692 {
4693 const char *id;
4694
4695 item = cafemenu_tree_iter_get_entry (iter);
4696 id = cafemenu_tree_entry_get_desktop_file_id (item);
4697 if (id != NULL((void*)0))
4698 g_hash_table_insert (tree->entries_by_id, (char*)id, item);
4699 }
4700 break;
4701 case CAFEMENU_TREE_ITEM_DIRECTORY:
4702 {
4703 item = cafemenu_tree_iter_get_directory (iter);
4704 update_entry_index (tree, (CafeMenuTreeDirectory*)item);
4705 }
4706 break;
4707 default:
4708 break;
4709 }
4710 if (item != NULL((void*)0))
4711 cafemenu_tree_item_unref (item);
4712 }
4713
4714 cafemenu_tree_iter_unref (iter);
4715}
4716
4717static gboolean
4718cafemenu_tree_build_from_layout (CafeMenuTree *tree,
4719 GError **error)
4720{
4721 DesktopEntrySet *allocated;
4722
4723 if (tree->root)
4724 return TRUE(!(0));
4725
4726 if (!cafemenu_tree_load_layout (tree, error))
4727 return FALSE(0);
4728
4729 menu_verbose ("Building menu tree from layout\n");
4730
4731 allocated = desktop_entry_set_new ();
4732
4733 /* create the menu structure */
4734 tree->root = process_layout (tree,
4735 NULL((void*)0),
4736 find_menu_child (tree->layout),
4737 allocated);
4738 if (tree->root)
4739 {
4740 DesktopEntrySet *unallocated_used;
4741
4742 unallocated_used = desktop_entry_set_new ();
4743
4744 process_only_unallocated (tree, tree->root, allocated, unallocated_used);
4745 if (tree->flags & CAFEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED)
4746 {
4747 DesktopEntrySet *entry_pool;
4748 DesktopEntrySet *still_unallocated;
4749 GetStillUnallocatedForeachData data;
4750
4751 entry_pool = _entry_directory_list_get_all_desktops (menu_layout_node_menu_get_app_dirs (find_menu_child (tree->layout)));
4752 still_unallocated = desktop_entry_set_new ();
4753
4754 data.tree = tree;
4755 data.allocated = allocated;
4756 data.unallocated_used = unallocated_used;
4757 data.still_unallocated = still_unallocated;
4758
4759 desktop_entry_set_foreach (entry_pool,
4760 (DesktopEntrySetForeachFunc) get_still_unallocated_foreach,
4761 &data);
4762
4763 desktop_entry_set_unref (entry_pool);
4764
4765 desktop_entry_set_foreach (still_unallocated,
4766 (DesktopEntrySetForeachFunc) unallocated_entries_listify_foreach,
4767 tree->root);
4768
4769 desktop_entry_set_unref (still_unallocated);
4770 }
4771
4772 desktop_entry_set_unref (unallocated_used);
4773
4774 /* process the layout info part that can move/remove items:
4775 * inline, show_empty, etc. */
4776 preprocess_layout_info (tree, tree->root);
4777 /* populate the menu structure that we got with the items, and order it
4778 * according to the layout info */
4779 process_layout_info (tree, tree->root);
4780
4781 update_entry_index (tree, tree->root);
4782
4783 menu_layout_node_root_add_entries_monitor (tree->layout,
4784 (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed,
4785 tree);
4786 }
4787
4788 desktop_entry_set_unref (allocated);
4789
4790 return TRUE(!(0));
4791}
4792
4793static void
4794cafemenu_tree_force_rebuild (CafeMenuTree *tree)
4795{
4796 if (tree->root)
4797 {
4798 g_hash_table_remove_all (tree->entries_by_id);
4799 cafemenu_tree_item_unref (tree->root);
4800 tree->root = NULL((void*)0);
4801 tree->loaded = FALSE(0);
4802
4803 g_assert (tree->layout != NULL)do { if (tree->layout != ((void*)0)) ; else g_assertion_message_expr
(((gchar*) 0), "cafemenu-tree.c", 4803, ((const char*) (__func__
)), "tree->layout != NULL"); } while (0)
;
4804
4805 menu_layout_node_root_remove_entries_monitor (tree->layout,
4806 (MenuLayoutNodeEntriesChangedFunc) handle_entries_changed,
4807 tree);
4808 }
4809}
4810
4811GType
4812cafemenu_tree_iter_get_type (void)
4813{
4814 static GType gtype = G_TYPE_INVALID((GType) ((0) << (2)));
4815 if (gtype == G_TYPE_INVALID((GType) ((0) << (2))))
4816 {
4817 gtype = g_boxed_type_register_static ("CafeMenuTreeIter",
4818 (GBoxedCopyFunc)cafemenu_tree_iter_ref,
4819 (GBoxedFreeFunc)cafemenu_tree_iter_unref);
4820 }
4821 return gtype;
4822}
4823
4824GType
4825cafemenu_tree_directory_get_type (void)
4826{
4827 static GType gtype = G_TYPE_INVALID((GType) ((0) << (2)));
4828 if (gtype == G_TYPE_INVALID((GType) ((0) << (2))))
4829 {
4830 gtype = g_boxed_type_register_static ("CafeMenuTreeDirectory",
4831 (GBoxedCopyFunc)cafemenu_tree_item_ref,
4832 (GBoxedFreeFunc)cafemenu_tree_item_unref);
4833 }
4834 return gtype;
4835}
4836
4837GType
4838cafemenu_tree_entry_get_type (void)
4839{
4840 static GType gtype = G_TYPE_INVALID((GType) ((0) << (2)));
4841 if (gtype == G_TYPE_INVALID((GType) ((0) << (2))))
4842 {
4843 gtype = g_boxed_type_register_static ("CafeMenuTreeEntry",
4844 (GBoxedCopyFunc)cafemenu_tree_item_ref,
4845 (GBoxedFreeFunc)cafemenu_tree_item_unref);
4846 }
4847 return gtype;
4848}
4849
4850GType
4851cafemenu_tree_separator_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 ("CafeMenuTreeSeparator",
4857 (GBoxedCopyFunc)cafemenu_tree_item_ref,
4858 (GBoxedFreeFunc)cafemenu_tree_item_unref);
4859 }
4860 return gtype;
4861}
4862
4863GType
4864cafemenu_tree_header_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 ("CafeMenuTreeHeader",
4870 (GBoxedCopyFunc)cafemenu_tree_item_ref,
4871 (GBoxedFreeFunc)cafemenu_tree_item_unref);
4872 }
4873 return gtype;
4874}
4875
4876GType
4877cafemenu_tree_alias_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 ("CafeMenuTreeAlias",
4883 (GBoxedCopyFunc)cafemenu_tree_item_ref,
4884 (GBoxedFreeFunc)cafemenu_tree_item_unref);
4885 }
4886 return gtype;
4887}
4888
4889GType
4890cafemenu_tree_flags_get_type (void)
4891{
4892 static GType enum_type_id = 0;
4893 if (G_UNLIKELY (!enum_type_id)(!enum_type_id))
4894 {
4895 static const GFlagsValue values[] = {
4896 { CAFEMENU_TREE_FLAGS_NONE, "CAFEMENU_TREE_FLAGS_NONE", "none" },
4897 { CAFEMENU_TREE_FLAGS_INCLUDE_EXCLUDED, "CAFEMENU_TREE_FLAGS_INCLUDE_EXCLUDED", "include-excluded" },
4898 { CAFEMENU_TREE_FLAGS_SHOW_EMPTY, "CAFEMENU_TREE_FLAGS_SHOW_EMPTY", "show-empty" },
4899 { CAFEMENU_TREE_FLAGS_INCLUDE_NODISPLAY, "CAFEMENU_TREE_FLAGS_INCLUDE_NODISPLAY", "include-nodisplay" },
4900 { CAFEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS, "CAFEMENU_TREE_FLAGS_SHOW_ALL_SEPARATORS", "show-all-separators" },
4901 { CAFEMENU_TREE_FLAGS_SORT_DISPLAY_NAME, "CAFEMENU_TREE_FLAGS_SORT_DISPLAY_NAME", "sort-display-name" },
4902 { CAFEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED, "CAFEMENU_TREE_FLAGS_INCLUDE_UNALLOCATED,", "include-unallocated" },
4903 { 0, NULL((void*)0), NULL((void*)0) }
4904 };
4905 enum_type_id = g_flags_register_static ("CafeMenuTreeFlags", values);
4906 }
4907 return enum_type_id;
4908}