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