9 # define alloca __builtin_alloca
11 # define alloca __alloca
12 #elif defined _MSC_VER
14 # define alloca _alloca
20 void *alloca (size_t);
23 #include <Ecore_File.h>
25 /* define macros and variable for using the eina logging system */
26 #define EFREET_MODULE_LOG_DOM _efreet_menu_log_dom
27 static int _efreet_menu_log_dom = -1;
30 #include "efreet_private.h"
31 #include "efreet_xml.h"
33 typedef struct Efreet_Menu_Move Efreet_Menu_Move;
35 struct Efreet_Menu_Move
37 const char *old_name; /**< The menu path to move from */
38 const char *new_name; /**< The menu path to move too */
41 typedef struct Efreet_Menu_Internal Efreet_Menu_Internal;
43 struct Efreet_Menu_Internal
47 const char *path; /**< The base file path */
48 const char *name; /**< The filename for this menu */
49 } file; /**< The menu file information */
53 const char *internal; /**< The menu name */
54 const char *name; /**< Name to use in the menus */
55 } name; /**< The names for this menu */
57 Efreet_Desktop *directory; /**< The directory */
58 Eina_List *directories; /**< All the directories set in the menu file */
60 Efreet_Menu_Move *current_move; /**< The current move */
62 Eina_List *app_dirs; /**< .desktop application directories */
64 Eina_List *app_pool; /**< application pool */
65 Eina_List *applications; /**< applications in this menu */
67 Eina_List *directory_dirs; /**< .directory file directories */
68 Eina_Hash *directory_cache; /**< .directory dirs */
70 Eina_List *moves; /**< List of moves to be handled by the menu */
71 Eina_List *filters; /**< Include and Exclude filters */
73 Efreet_Menu_Internal *parent; /**< Our parent menu */
74 Eina_List *sub_menus; /**< Our sub menus */
76 Eina_List *layout; /**< This menus layout */
77 Eina_List *default_layout; /**< Default layout */
78 signed char show_empty; /**< Whether to show empty menus */
79 signed char in_line; /**< Whether this meny can be inlined */
80 signed char inline_limit; /**< Number of elements which triggers inline */
81 signed char inline_header; /**< Whether we should use the header name when this menu is inlined */
82 signed char inline_alias; /**< Whether we should use the menu name when inlining */
84 unsigned char seen_allocated:1; /**< have we set the only_unallocated */
85 unsigned char only_unallocated:1; /**< Show only unallocated .desktops */
87 unsigned char seen_deleted:1; /**< Have we seen the deleted item yet */
88 unsigned char deleted:1; /**< The menu is deleted */
91 typedef struct Efreet_Menu_App_Dir Efreet_Menu_App_Dir;
93 struct Efreet_Menu_App_Dir
95 const char *path; /**< directory path */
96 const char *prefix; /**< If it's legacy it can have a prefix */
97 unsigned int legacy:1; /**< is this a legacy dir */
100 enum Efreet_Menu_Filter_Op_Type
102 EFREET_MENU_FILTER_OP_OR,
103 EFREET_MENU_FILTER_OP_AND,
104 EFREET_MENU_FILTER_OP_NOT
107 typedef enum Efreet_Menu_Filter_Op_Type Efreet_Menu_Filter_Op_Type;
109 enum Efreet_Menu_Filter_Type
111 EFREET_MENU_FILTER_INCLUDE,
112 EFREET_MENU_FILTER_EXCLUDE
115 typedef enum Efreet_Menu_Filter_Type Efreet_Menu_Filter_Type;
117 typedef struct Efreet_Menu_Filter_Op Efreet_Menu_Filter_Op;
119 struct Efreet_Menu_Filter_Op
121 Efreet_Menu_Filter_Op_Type type; /**< The type of operation */
122 Eina_List *categories; /**< The categories this op applies too */
123 Eina_List *filenames; /**< The filenames this op applies too */
125 Eina_List *filters; /**< Child filters */
127 unsigned char all:1; /**< Applies to all .desktop files */
130 typedef struct Efreet_Menu_Filter Efreet_Menu_Filter;
132 struct Efreet_Menu_Filter
134 Efreet_Menu_Filter_Type type; /**< The type of filter */
135 Efreet_Menu_Filter_Op *op; /**< The filter operations */
138 enum Efreet_Menu_Layout_Type
140 EFREET_MENU_LAYOUT_MENUNAME,
141 EFREET_MENU_LAYOUT_FILENAME,
142 EFREET_MENU_LAYOUT_SEPARATOR,
143 EFREET_MENU_LAYOUT_MERGE
146 typedef enum Efreet_Menu_Layout_Type Efreet_Menu_Layout_Type;
148 typedef struct Efreet_Menu_Layout Efreet_Menu_Layout;
150 struct Efreet_Menu_Layout
152 Efreet_Menu_Layout_Type type; /**< The type of layout */
153 const char *name; /**< The name of the element */
155 /* The items below are for Menuname Layout elements */
156 signed char show_empty; /**< Whether to show empty menus */
157 signed char in_line; /**< Whether this meny can be inlined */
158 signed char inline_limit; /**< Number of elements which triggers inline */
159 signed char inline_header; /**< Whether we should use the header name when this menu is inlined */
160 signed char inline_alias; /**< Whether we should use the menu name when inlining */
163 typedef struct Efreet_Menu_Desktop Efreet_Menu_Desktop;
165 struct Efreet_Menu_Desktop
167 Efreet_Desktop *desktop; /**< The desktop we refer too */
168 const char *id; /**< The desktop file id */
169 unsigned char allocated:1; /**< If this desktop has been allocated */
172 static const char *efreet_menu_prefix = NULL; /**< The $XDG_MENU_PREFIX env var */
173 Eina_List *efreet_menu_kde_legacy_dirs = NULL; /**< The directories to use for KDELegacy entries */
174 static const char *efreet_tag_menu = NULL;
175 static const char *efreet_menu_file = NULL; /**< A menu file set explicityl as default */
177 static Eina_Hash *efreet_merged_menus = NULL;
178 static Eina_Hash *efreet_merged_dirs = NULL;
180 static Eina_Hash *efreet_menu_handle_cbs = NULL;
181 static Eina_Hash *efreet_menu_filter_cbs = NULL;
182 static Eina_Hash *efreet_menu_move_cbs = NULL;
183 static Eina_Hash *efreet_menu_layout_cbs = NULL;
185 static const char *efreet_menu_prefix_get(void);
187 static Efreet_Menu_Internal *efreet_menu_by_name_find(Efreet_Menu_Internal *internal,
189 Efreet_Menu_Internal **parent);
190 static int efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name);
191 static int efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name);
193 static int efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal);
194 static int efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop);
196 static int efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old);
198 static int efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated);
199 static int efreet_menu_process_dirs(Efreet_Menu_Internal *internal);
200 static int efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal);
201 static int efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal,
205 static int efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal);
206 static int efreet_menu_directory_dir_scan(const char *path,
207 const char *relative_path,
209 static Efreet_Desktop *efreet_menu_directory_get(Efreet_Menu_Internal *internal,
211 static void efreet_menu_process_filters(Efreet_Menu_Internal *internal,
212 unsigned int only_unallocated);
213 static Eina_List *efreet_menu_process_app_pool(Eina_List *pool,
214 Eina_List *applications,
216 Efreet_Menu_Filter *filter,
217 unsigned int only_unallocated);
218 static int efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op,
219 Efreet_Menu_Desktop *md);
220 static int efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op,
221 Efreet_Menu_Desktop *md);
222 static int efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op,
223 Efreet_Menu_Desktop *md);
224 static int efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op,
225 Efreet_Menu_Desktop *md);
227 static Efreet_Menu *efreet_menu_layout_menu(Efreet_Menu_Internal *internal);
228 static Efreet_Menu *efreet_menu_layout_desktop(Efreet_Menu_Desktop *md);
229 static void efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal,
230 Efreet_Menu_Layout *layout);
231 static int efreet_menu_layout_is_empty(Efreet_Menu *entry);
233 static Efreet_Menu_Internal *efreet_menu_internal_new(void);
234 static void efreet_menu_internal_free(Efreet_Menu_Internal *internal);
235 static void efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal);
236 static void efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal);
237 static void efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal);
238 static void efreet_menu_create_directories_list(Efreet_Menu_Internal *internal);
239 static void efreet_menu_create_move_list(Efreet_Menu_Internal *internal);
240 static void efreet_menu_create_filter_list(Efreet_Menu_Internal *internal);
241 static void efreet_menu_create_layout_list(Efreet_Menu_Internal *internal);
242 static void efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal);
243 static const char *efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix);
245 static Efreet_Menu_App_Dir *efreet_menu_app_dir_new(void);
246 static void efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir);
248 static Efreet_Menu_Move *efreet_menu_move_new(void);
249 static void efreet_menu_move_free(Efreet_Menu_Move *move);
251 static Efreet_Menu_Filter *efreet_menu_filter_new(void);
252 static void efreet_menu_filter_free(Efreet_Menu_Filter *filter);
254 static Efreet_Menu_Layout *efreet_menu_layout_new(void);
255 static void efreet_menu_layout_free(Efreet_Menu_Layout *layout);
257 static Efreet_Menu_Filter_Op *efreet_menu_filter_op_new(void);
258 static void efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op);
260 static Efreet_Menu_Desktop *efreet_menu_desktop_new(void);
261 static void efreet_menu_desktop_free(Efreet_Menu_Desktop *md);
263 static Efreet_Menu *efreet_menu_entry_new(void);
265 static int efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml);
266 static int efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
268 static int efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
269 static int efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
270 static int efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
271 static int efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
272 static int efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
273 static int efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
274 static int efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
275 static int efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
276 static int efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
277 static int efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
278 static int efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
279 static int efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
280 static int efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
281 static int efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
282 static int efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
283 static int efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
284 static int efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
285 static int efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
286 static int efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
287 static int efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
288 static int efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
289 static int efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
290 static Efreet_Menu_Internal *efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root,
291 Efreet_Menu_Internal *parent,
292 const char *legacy_dir,
294 static int efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
295 static int efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
296 static int efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
297 static int efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
298 static int efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
299 static int efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
301 static int efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml,
302 Efreet_Menu_Filter_Type type);
303 static int efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
304 static int efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
305 Efreet_Menu_Filter_Op_Type type);
307 static int efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
308 static int efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
309 static int efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
310 static int efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
312 static int efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path);
313 static int efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path);
315 static int efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b);
317 static void efreet_menu_resolve_moves(Efreet_Menu_Internal *internal);
318 static void efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src);
320 static int efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b);
321 static int efreet_menu_cb_md_compare(const Efreet_Menu_Desktop *a, const Efreet_Menu_Desktop *b);
323 static int efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent);
324 static int efreet_menu_save_indent(FILE *f, int indent);
326 static void efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path);
329 efreet_menu_init(void)
336 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
338 {"Menu", efreet_menu_handle_sub_menu},
339 {"AppDir", efreet_menu_handle_app_dir},
340 {"DefaultAppDirs", efreet_menu_handle_default_app_dirs},
341 {"DirectoryDir", efreet_menu_handle_directory_dir},
342 {"DefaultDirectoryDirs", efreet_menu_handle_default_directory_dirs},
343 {"Name", efreet_menu_handle_name},
344 {"Directory", efreet_menu_handle_directory},
345 {"OnlyUnallocated", efreet_menu_handle_only_unallocated},
346 {"NotOnlyUnallocated", efreet_menu_handle_not_only_unallocated},
347 {"Deleted", efreet_menu_handle_deleted},
348 {"NotDeleted", efreet_menu_handle_not_deleted},
349 {"Include", efreet_menu_handle_include},
350 {"Exclude", efreet_menu_handle_exclude},
351 {"MergeFile", efreet_menu_handle_merge_file},
352 {"MergeDir", efreet_menu_handle_merge_dir},
353 {"DefaultMergeDirs", efreet_menu_handle_default_merge_dirs},
354 {"LegacyDir", efreet_menu_handle_legacy_dir},
355 {"KDELegacyDirs", efreet_menu_handle_kde_legacy_dirs},
356 {"Move", efreet_menu_handle_move},
357 {"Layout", efreet_menu_handle_layout},
358 {"DefaultLayout", efreet_menu_handle_default_layout},
365 int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
367 {"Filename", efreet_menu_handle_filename},
368 {"Category", efreet_menu_handle_category},
369 {"All", efreet_menu_handle_all},
370 {"And", efreet_menu_handle_and},
371 {"Or", efreet_menu_handle_or},
372 {"Not", efreet_menu_handle_not},
379 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
381 {"Old", efreet_menu_handle_old},
382 {"New", efreet_menu_handle_new},
389 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
391 {"Menuname", efreet_menu_handle_layout_menuname},
392 {"Filename", efreet_menu_handle_layout_filename},
393 {"Separator", efreet_menu_handle_layout_separator},
394 {"Merge", efreet_menu_handle_layout_merge},
398 _efreet_menu_log_dom = eina_log_domain_register
399 ("efreet_menu", EFREET_DEFAULT_LOG_COLOR);
400 if (_efreet_menu_log_dom < 0)
402 EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_menu");
406 efreet_menu_handle_cbs = eina_hash_string_superfast_new(NULL);
407 efreet_menu_filter_cbs = eina_hash_string_superfast_new(NULL);
408 efreet_menu_move_cbs = eina_hash_string_superfast_new(NULL);
409 efreet_menu_layout_cbs = eina_hash_string_superfast_new(NULL);
410 if (!efreet_menu_handle_cbs || !efreet_menu_filter_cbs
411 || !efreet_menu_move_cbs || !efreet_menu_layout_cbs)
413 eina_log_domain_unregister(_efreet_menu_log_dom);
414 _efreet_menu_log_dom = -1;
418 /* set Menu into it's own so we can check the XML is valid before trying
420 efreet_tag_menu = eina_stringshare_add(menu_cbs[0].key);
422 for (i = 0; menu_cbs[i].key; i++)
424 eina_hash_del(efreet_menu_handle_cbs,
427 eina_hash_add(efreet_menu_handle_cbs,
431 for (i = 0; filter_cbs[i].key; i++)
433 eina_hash_del(efreet_menu_filter_cbs,
436 eina_hash_add(efreet_menu_filter_cbs,
440 for (i = 0; move_cbs[i].key; i++)
442 eina_hash_del(efreet_menu_move_cbs,
445 eina_hash_add(efreet_menu_move_cbs,
449 for (i = 0; layout_cbs[i].key; i++)
451 eina_hash_del(efreet_menu_layout_cbs,
454 eina_hash_add(efreet_menu_layout_cbs,
462 efreet_menu_kde_legacy_init(void)
468 IF_FREE_LIST(efreet_menu_kde_legacy_dirs, eina_stringshare_del);
470 f = popen("kde-config --path apps", "r");
473 /* XXX if the return from kde-config is a line longer than PATH_MAX,
474 * this won't be correct (increase buffer and get the rest...) */
475 if (!fgets(buf, sizeof(buf), f))
477 ERR("Error initializing KDE legacy information");
486 efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
487 (void *)eina_stringshare_add(s));
493 efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
494 (void *)eina_stringshare_add(s));
501 efreet_menu_shutdown(void)
503 IF_RELEASE(efreet_menu_file);
505 IF_FREE_HASH(efreet_menu_handle_cbs);
506 IF_FREE_HASH(efreet_menu_filter_cbs);
507 IF_FREE_HASH(efreet_menu_move_cbs);
508 IF_FREE_HASH(efreet_menu_layout_cbs);
510 IF_FREE_LIST(efreet_menu_kde_legacy_dirs, eina_stringshare_del);
512 IF_FREE_HASH(efreet_merged_menus);
513 IF_FREE_HASH(efreet_merged_dirs);
515 IF_RELEASE(efreet_tag_menu);
517 eina_log_domain_unregister(_efreet_menu_log_dom);
518 _efreet_menu_log_dom = -1;
522 efreet_menu_new(const char *name)
526 EINA_SAFETY_ON_NULL_RETURN_VAL(name, NULL);
528 menu = efreet_menu_entry_new();
529 menu->type = EFREET_MENU_ENTRY_MENU;
530 menu->name = eina_stringshare_add(name);
535 efreet_menu_file_set(const char *file)
537 IF_RELEASE(efreet_menu_file);
538 efreet_menu_file = NULL;
539 if (file) efreet_menu_file = eina_stringshare_add(file);
543 efreet_menu_get(void)
547 Eina_List *config_dirs, *l;
550 /* prefer user set menu */
551 if (efreet_menu_file)
553 if (ecore_file_exists(efreet_menu_file))
554 return efreet_menu_parse(efreet_menu_file);
558 /* check the users config directory first */
559 snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
560 efreet_config_home_get(), efreet_menu_prefix_get());
561 if (ecore_file_exists(menu))
562 return efreet_menu_parse(menu);
564 /* fallback to the XDG_CONFIG_DIRS */
565 config_dirs = efreet_config_dirs_get();
566 EINA_LIST_FOREACH(config_dirs, l, dir)
568 snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
569 dir, efreet_menu_prefix_get());
570 if (ecore_file_exists(menu))
571 return efreet_menu_parse(menu);
578 efreet_menu_parse(const char *path)
581 Efreet_Menu_Internal *internal = NULL;
582 Efreet_Menu *entry = NULL;
584 EINA_SAFETY_ON_NULL_RETURN_VAL(path, NULL);
586 xml = efreet_xml_new(path);
587 if (!xml) return NULL;
589 /* make sure we've got a <Menu> to start with */
590 if (xml->tag != efreet_tag_menu)
592 WRN("Efreet_menu: Menu file didn't start with <Menu> tag.");
597 IF_FREE_HASH(efreet_merged_menus);
598 efreet_merged_menus = eina_hash_string_superfast_new(NULL);
600 IF_FREE_HASH(efreet_merged_dirs);
601 efreet_merged_dirs = eina_hash_string_superfast_new(NULL);
603 /* split apart the filename and the path */
604 internal = efreet_menu_internal_new();
605 if (!internal) return NULL;
607 /* Set default values */
608 internal->show_empty = 0;
609 internal->in_line = 0;
610 internal->inline_limit = 4;
611 internal->inline_header = 1;
612 internal->inline_alias = 0;
614 efreet_menu_path_set(internal, path);
615 if (!efreet_menu_handle_menu(internal, xml))
618 efreet_menu_internal_free(internal);
623 efreet_menu_resolve_moves(internal);
625 if (!efreet_menu_process_dirs(internal))
627 efreet_menu_internal_free(internal);
631 /* handle all .desktops */
632 if (!efreet_menu_process(internal, 0))
634 efreet_menu_internal_free(internal);
638 /* handle menus with only unallocated .desktops */
639 if (!efreet_menu_process(internal, 1))
641 efreet_menu_internal_free(internal);
646 entry = efreet_menu_layout_menu(internal);
647 efreet_menu_internal_free(internal);
652 efreet_menu_save(Efreet_Menu *menu, const char *path)
657 EINA_SAFETY_ON_NULL_RETURN_VAL(menu, 0);
658 EINA_SAFETY_ON_NULL_RETURN_VAL(path, 0);
660 f = fopen(path, "w");
662 fprintf(f, "<?xml version=\"1.0\"?>\n");
663 fprintf(f, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\" "
664 "\"http://standards.freedesktop.org/menu-spec/menu-1.0.dtd\">\n");
665 ret = efreet_menu_save_menu(menu, f, 0);
671 efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent)
675 efreet_menu_save_indent(f, indent);
676 fprintf(f, "<Menu>\n");
679 efreet_menu_save_indent(f, indent + 1);
680 fprintf(f, "<Name>%s</Name>\n", menu->name);
685 /* Only save these for the root element */
686 efreet_menu_save_indent(f, indent + 1);
687 fprintf(f, "<DefaultAppDirs/>\n");
688 efreet_menu_save_indent(f, indent + 1);
689 fprintf(f, "<DefaultDirectoryDirs/>\n");
694 efreet_menu_save_indent(f, indent + 1);
695 fprintf(f, "<Directory>%s</Directory>\n", menu->desktop->orig_path);
701 int has_desktop = 0, has_menu = 0;
703 efreet_menu_save_indent(f, indent + 1);
704 fprintf(f, "<Layout>\n");
705 EINA_LIST_FOREACH(menu->entries, l, entry)
707 if (entry->type == EFREET_MENU_ENTRY_MENU)
709 efreet_menu_save_indent(f, indent + 2);
710 fprintf(f, "<Menuname>%s</Menuname>\n", entry->id);
713 else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
715 efreet_menu_save_indent(f, indent + 2);
716 fprintf(f, "<Filename>%s</Filename>\n", entry->id);
719 else if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
721 efreet_menu_save_indent(f, indent + 2);
722 fprintf(f, "<Separator/>\n");
725 efreet_menu_save_indent(f, indent + 1);
726 fprintf(f, "</Layout>\n");
730 efreet_menu_save_indent(f, indent + 1);
731 fprintf(f, "<Include>\n");
732 EINA_LIST_FOREACH(menu->entries, l, entry)
734 if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
736 efreet_menu_save_indent(f, indent + 2);
737 fprintf(f, "<Filename>%s</Filename>\n", entry->id);
740 efreet_menu_save_indent(f, indent + 1);
741 fprintf(f, "</Include>\n");
746 EINA_LIST_FOREACH(menu->entries, l, entry)
748 if (entry->type == EFREET_MENU_ENTRY_MENU)
749 efreet_menu_save_menu(entry, f, indent + 1);
753 efreet_menu_save_indent(f, indent);
754 fprintf(f, "</Menu>\n");
759 efreet_menu_save_indent(FILE *f, int indent)
763 for (i = 0; i < indent; i++)
769 efreet_menu_desktop_insert(Efreet_Menu *menu, Efreet_Desktop *desktop, int pos)
774 EINA_SAFETY_ON_NULL_RETURN_VAL(menu, 0);
775 EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, 0);
777 id = efreet_util_path_to_file_id(desktop->orig_path);
780 entry = efreet_menu_entry_new();
781 entry->type = EFREET_MENU_ENTRY_DESKTOP;
782 entry->id = eina_stringshare_add(id);
783 entry->name = eina_stringshare_add(desktop->name);
784 if (desktop->icon) entry->icon = eina_stringshare_add(desktop->icon);
785 efreet_desktop_ref(desktop);
786 entry->desktop = desktop;
788 if (pos < 0 || (unsigned int)pos >= eina_list_count(menu->entries))
789 menu->entries = eina_list_append(menu->entries, entry);
792 menu->entries = eina_list_append_relative(menu->entries, entry,
793 eina_list_nth(menu->entries, pos));
799 efreet_menu_desktop_remove(Efreet_Menu *menu, Efreet_Desktop *desktop)
803 EINA_SAFETY_ON_NULL_RETURN_VAL(menu, 0);
804 EINA_SAFETY_ON_NULL_RETURN_VAL(desktop, 0);
806 entry = eina_list_search_unsorted(menu->entries,
807 EINA_COMPARE_CB(efreet_menu_cb_entry_compare_desktop),
811 menu->entries = eina_list_remove(menu->entries, entry);
812 efreet_menu_free(entry);
819 efreet_menu_dump(Efreet_Menu *menu, const char *indent)
823 EINA_SAFETY_ON_NULL_RETURN(menu);
824 EINA_SAFETY_ON_NULL_RETURN(indent);
826 INF("%s%s: ", indent, menu->name);
827 INF("%s", (menu->icon ? menu->icon : "No icon"));
829 /* XXX dump the rest of the menu info */
837 len = strlen(indent) + 3;
838 new_indent = alloca(len);
839 snprintf(new_indent, len, "%s ", indent);
841 EINA_LIST_FOREACH(menu->entries, l, entry)
843 if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
844 INF("%s|---", new_indent);
845 else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
846 INF("%s|-%s", new_indent, entry->name);
847 else if (entry->type == EFREET_MENU_ENTRY_MENU)
848 efreet_menu_dump(entry, new_indent);
849 else if (entry->type == EFREET_MENU_ENTRY_HEADER)
850 INF("%s|---%s", new_indent, entry->name);
857 * @param user_dir The user directory to work with
858 * @param system_dirs The system directories to work with
859 * @param suffix The path suffix to add
860 * @return Returns the list of directories
861 * @brief Creates the list of directories based on the user
862 * dir, system dirs and given suffix.
864 * Needs EAPI because of helper binaries
867 efreet_default_dirs_get(const char *user_dir, Eina_List *system_dirs,
872 Eina_List *list = NULL;
875 EINA_SAFETY_ON_NULL_RETURN_VAL(user_dir, NULL);
876 EINA_SAFETY_ON_NULL_RETURN_VAL(suffix, NULL);
878 snprintf(dir, sizeof(dir), "%s/%s", user_dir, suffix);
879 list = eina_list_append(list, eina_stringshare_add(dir));
881 EINA_LIST_FOREACH(system_dirs, l, xdg_dir)
883 snprintf(dir, sizeof(dir), "%s/%s", xdg_dir, suffix);
884 list = eina_list_append(list, eina_stringshare_add(dir));
892 * @return Returns a new Efreet_Menu_Internal struct
893 * @brief Allocates and initializes a new Efreet_Menu_Internal structure
895 static Efreet_Menu_Internal *
896 efreet_menu_internal_new(void)
898 Efreet_Menu_Internal *internal;
900 internal = NEW(Efreet_Menu_Internal, 1);
901 if (!internal) return NULL;
902 internal->show_empty = -1;
903 internal->in_line = -1;
904 internal->inline_limit = -1;
905 internal->inline_header = -1;
906 internal->inline_alias = -1;
912 * @param menu The menu to free
913 * @return Returns no value
914 * @brief Frees up the given menu structure
917 efreet_menu_internal_free(Efreet_Menu_Internal *internal)
919 if (!internal) return;
921 IF_RELEASE(internal->file.path);
922 IF_RELEASE(internal->file.name);
924 IF_RELEASE(internal->name.internal);
925 internal->name.name = NULL;
927 internal->applications = eina_list_free(internal->applications);
929 IF_FREE_LIST(internal->directories, eina_stringshare_del);
930 IF_FREE_LIST(internal->app_dirs, efreet_menu_app_dir_free);
931 IF_FREE_LIST(internal->app_pool, efreet_menu_desktop_free);
932 IF_FREE_LIST(internal->directory_dirs, eina_stringshare_del);
933 IF_FREE_HASH(internal->directory_cache);
935 IF_FREE_LIST(internal->moves, efreet_menu_move_free);
936 IF_FREE_LIST(internal->filters, efreet_menu_filter_free);
938 IF_FREE_LIST(internal->sub_menus, efreet_menu_internal_free);
940 IF_FREE_LIST(internal->layout, efreet_menu_layout_free);
941 IF_FREE_LIST(internal->default_layout, efreet_menu_layout_free);
948 * @return Returns the XDG_MENU_PREFIX env variable or "" if none set
949 * @brief Retrieves the XDG_MENU_PREFIX or "" if not set.
952 efreet_menu_prefix_get(void)
954 if (efreet_menu_prefix) return efreet_menu_prefix;
956 efreet_menu_prefix = getenv("XDG_MENU_PREFIX");
957 if (!efreet_menu_prefix) efreet_menu_prefix = "";
959 return efreet_menu_prefix;
964 * @param menu The menu to populate
965 * @param xml The xml dom tree to populate from
966 * @return Returns 1 if this XML tree is valid, 0 otherwise
967 * @brief Populates the given menu from the given xml structure
969 * We walk the Menu children backwards. The reason for this is so that we
970 * can deal with all the things that make us select the 'last' element
971 * (MergeFile, Directory, etc). We'll see the last one first and can deal
972 * with it right away.
975 efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml)
979 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
981 EINA_LIST_REVERSE_FOREACH(xml->children, l, child)
983 cb = eina_hash_find(efreet_menu_handle_cbs, child->tag);
986 if (!cb(internal, child))
991 WRN("Unknown XML tag: %s", child->tag);
1000 * @param parent The parent Menu
1001 * @param xml The xml that defines the menu
1002 * @return Returns 1 on success or 0 on failure
1003 * @brief Handles the sub-menu nodes of the XML file
1006 efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1008 Efreet_Menu_Internal *internal, *match;
1010 efreet_menu_create_sub_menu_list(parent);
1012 internal = efreet_menu_internal_new();
1013 if (!internal) return 0;
1014 internal->file.path = eina_stringshare_add(parent->file.path);
1015 if (!efreet_menu_handle_menu(internal, xml))
1017 efreet_menu_internal_free(internal);
1021 /* if this menu already exists we just take this one and stick it on the
1022 * start of the existing one */
1023 if ((match = eina_list_search_unsorted(parent->sub_menus,
1024 EINA_COMPARE_CB(efreet_menu_cb_menu_compare),
1028 efreet_menu_concatenate(match, internal);
1029 efreet_menu_internal_free(internal);
1032 parent->sub_menus = eina_list_prepend(parent->sub_menus, internal);
1039 * @param parent The parent menu
1040 * @param xml The xml tree
1041 * @return Returns 1 on success or 0 on failure
1042 * @brief Handles the AppDir tag
1045 efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1048 Efreet_Menu_App_Dir *app_dir;
1050 if (!parent || !xml) return 0;
1052 efreet_menu_create_app_dirs_list(parent);
1053 path = efreet_menu_path_get(parent, xml->text);
1054 if (!path) return 0;
1056 /* we've already got this guy in our list we can skip it */
1057 if (eina_list_search_unsorted(parent->app_dirs,
1058 EINA_COMPARE_CB(efreet_menu_cb_app_dirs_compare),
1061 eina_stringshare_del(path);
1065 app_dir = efreet_menu_app_dir_new();
1066 app_dir->path = path;
1068 parent->app_dirs = eina_list_prepend(parent->app_dirs, app_dir);
1075 * @param parent The parent menu
1077 * @return Returns 1 on success or 0 on failure
1078 * @brief Handles the DefaultAppDirs
1081 efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1083 Eina_List *prepend = NULL;
1087 if (!parent) return 0;
1089 efreet_menu_create_app_dirs_list(parent);
1090 dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
1092 EINA_LIST_FREE(dirs, dir)
1094 if (!eina_list_search_unsorted(parent->app_dirs,
1095 EINA_COMPARE_CB(efreet_menu_cb_app_dirs_compare),
1098 Efreet_Menu_App_Dir *app_dir;
1100 app_dir = efreet_menu_app_dir_new();
1101 app_dir->path = eina_stringshare_ref(dir);
1103 prepend = eina_list_append(prepend, app_dir);
1106 eina_stringshare_del(dir);
1108 parent->app_dirs = eina_list_merge(prepend, parent->app_dirs);
1115 * @param parent The parent menu
1116 * @param xml The xml tree
1117 * @return Returns 1 on success or 0 on failure
1118 * @brief Handles the DirectoryDir tag
1121 efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1125 if (!parent || !xml) return 0;
1127 efreet_menu_create_directory_dirs_list(parent);
1128 path = efreet_menu_path_get(parent, xml->text);
1129 if (!path) return 0;
1131 /* we've already got this guy in our list we can skip it */
1132 if (eina_list_search_unsorted(parent->directory_dirs, EINA_COMPARE_CB(strcmp), path))
1134 eina_stringshare_del(path);
1138 parent->directory_dirs = eina_list_prepend(parent->directory_dirs, path);
1145 * @param parent The parent menu
1147 * @return Returns 1 on success or 0 on failure
1148 * @brief Handles the DefaultDirectoryDirs tag
1151 efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1156 if (!parent) return 0;
1158 efreet_menu_create_directory_dirs_list(parent);
1159 dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
1160 "desktop-directories");
1161 EINA_LIST_FREE(dirs, dir)
1163 if (!eina_list_search_unsorted(parent->directory_dirs, EINA_COMPARE_CB(strcmp), dir))
1164 parent->directory_dirs = eina_list_prepend(parent->directory_dirs, eina_stringshare_ref(dir));
1165 eina_stringshare_del(dir);
1173 * @param parent The parent Menu
1174 * @param xml The xml to work with
1175 * @return Returns 1 on success or 0 on failure
1176 * @brief Sets the menu name from the given XML fragment.
1179 efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1181 /* not allowed to have two Name settings in a menu */
1182 if (parent->name.internal)
1184 INF("efreet_menu_handle_name() setting second name into menu");
1187 /* ignore the name if it is empty */
1188 if (!xml->text) return 1;
1190 /* ignore the name if it contains a / */
1191 if (strchr(xml->text, '/')) return 1;
1193 parent->name.internal = eina_stringshare_add(xml->text);
1200 * @param parent The parent menu
1201 * @param xml The xml tree
1202 * @return Returns 1 on success or 0 on failure
1203 * @brief Handles the Directory tag
1205 * This just adds the given directory path to a list which we'll walk once
1206 * we've traversed the entire menu into memory.
1209 efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1211 if (!parent || !xml) return 0;
1213 efreet_menu_create_directories_list(parent);
1214 parent->directories = eina_list_prepend(parent->directories, eina_stringshare_add(xml->text));
1221 * @param parent The parent menu
1222 * @param xml The xml tree
1223 * @return Returns 1 on success or 0 on failure
1224 * @brief Handles the OnlyUnallocated tag
1227 efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1229 if (!parent || !xml) return 0;
1231 /* a later instance has been seen so we can ignore this one */
1232 if (parent->seen_allocated) return 1;
1234 parent->seen_allocated = 1;
1235 parent->only_unallocated = 1;
1242 * @param parent The parent menu
1243 * @param xml The xml tree
1244 * @return Returns 1 on success or 0 on failure
1245 * @brief Handles the NotOnlyUnallocated tag
1248 efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1250 if (!parent || !xml) return 0;
1252 /* a later instance has been seen so we can ignore this one */
1253 if (parent->seen_allocated) return 1;
1255 parent->seen_allocated = 1;
1256 parent->only_unallocated = 0;
1263 * @param parent The parent menu
1264 * @param xml The xml tree
1265 * @return Returns 1 on success or 0 on failure
1266 * @brief Handles the Deleted tag
1269 efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1271 if (!parent || !xml) return 0;
1273 /* a later instance has been seen so we can ignore this one */
1274 if (parent->seen_deleted) return 1;
1276 parent->seen_deleted = 1;
1277 parent->deleted = 1;
1284 * @param parent The parent menu
1285 * @param xml The xml tree
1286 * @return Returns 1 on success or 0 on failure
1287 * @brief Handles the NotDeleted tag
1290 efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1292 if (!parent || !xml) return 0;
1294 /* a later instance has been seen so we can ignore this one */
1295 if (parent->seen_deleted) return 1;
1297 parent->seen_deleted = 1;
1298 parent->deleted = 0;
1305 * @param parent The parent menu
1306 * @param xml The XML tree to work with
1307 * @return Returns 1 on success or 0 on failure
1308 * @brief Handles parsing the Include tag and all subtags
1311 efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1313 return efreet_menu_handle_filter(parent, xml,
1314 EFREET_MENU_FILTER_INCLUDE);
1319 * @param parent The parent menu
1320 * @param xml The xml tree
1321 * @return Returns 1 on success or 0 on failure
1322 * @brief Handles the Exclude tag and all subtags
1325 efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1327 return efreet_menu_handle_filter(parent, xml,
1328 EFREET_MENU_FILTER_EXCLUDE);
1333 * @param op The filter operation
1334 * @param xml The xml tree
1335 * @return Returns 1 on success or 0 on failure
1336 * @brief Handles the Filename tag
1339 efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1341 if (!op || !xml) return 0;
1343 op->filenames = eina_list_append(op->filenames, eina_stringshare_add(xml->text));
1350 * @param op The filter operation
1351 * @param xml The xml tree
1352 * @return Returns 1 on success or 0 on failure
1353 * @brief Handles the Category tag
1356 efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1358 if (!op || !xml) return 0;
1361 op->categories = eina_list_append(op->categories, eina_stringshare_add(xml->text));
1368 * @param op The filter operation
1369 * @param xml The xml tree
1370 * @return Returns 1 on success or 0 on failure
1371 * @brief Handles the All tag and all subtags
1374 efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1376 if (!op || !xml) return 0;
1385 * @param op The filter operation
1386 * @param xml The xml tree
1387 * @return Returns 1 on success or 0 on failure
1388 * @brief Handles the And tag and all subtags
1391 efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1393 if (!op || !xml) return 0;
1395 return efreet_menu_handle_filter_child_op(op, xml,
1396 EFREET_MENU_FILTER_OP_AND);
1401 * @param op The filter operation
1402 * @param xml The xml tree
1403 * @return Returns 1 on success or 0 on failure
1404 * @brief Handles the Or tag and all subtags
1407 efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1409 if (!op || !xml) return 0;
1411 return efreet_menu_handle_filter_child_op(op, xml,
1412 EFREET_MENU_FILTER_OP_OR);
1417 * @param op The filter operation
1418 * @param xml The xml tree
1419 * @return Returns 1 on success or 0 on failure
1420 * @brief Handles the Not tag and all subtags
1423 efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1425 if (!op || !xml) return 0;
1427 return efreet_menu_handle_filter_child_op(op, xml,
1428 EFREET_MENU_FILTER_OP_NOT);
1433 * @param parent The parent menu
1434 * @param xml The xml tree
1435 * @return Returns 1 on success or 0 on failure
1436 * @brief Handles the MergeFile tag
1439 efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1442 const char *path = NULL;
1443 const char *attr = NULL;
1447 if (!parent || !xml) return 0;
1449 /* check to see if this is a path or parent type */
1450 attr = efreet_xml_attribute_get(xml, "type");
1451 if (attr && !strcmp(attr, "parent"))
1454 /* we're given a path */
1456 path = efreet_menu_path_get(parent, xml->text);
1458 /* need to find the next menu with the same name as ours in the config
1459 * dir after ours (if we're in a config dir) */
1462 Eina_List *search_dirs;
1463 const char *dir, *p;
1465 if (!parent->file.path)
1467 INF("efreet_menu_handle_merge_file() missing menu path ...");
1471 search_dirs = efreet_config_dirs_get();
1473 /* we need to find the next menu with the same name in the directory
1474 * after the on the the menu was found in. to do that we first check
1475 * if it's in the config_home_directory() if so we need to search
1476 * all of the dirs. If it isn't in the config home directory then we
1477 * scan the search dirs and look for it. The search_dirs list will
1478 * be left at the next pointer so we can start looking for the menu
1479 * from that point */
1481 dir = efreet_config_home_get();
1482 if (strncmp(dir, parent->file.path, eina_stringshare_strlen(dir)))
1484 EINA_LIST_FOREACH(search_dirs, l, dir)
1486 if (!strncmp(dir, parent->file.path, eina_stringshare_strlen(dir)))
1493 INF("efreet_menu_handle_merge_file() failed to find "
1494 "menu parent directory");
1498 /* the parent file path may have more path then just the base
1499 * directory so we need to append that as well */
1500 p = parent->file.path + eina_stringshare_strlen(dir);
1502 /* whatever dirs are left in the search dir we need to look for the
1503 * menu with the same relative filename */
1504 EINA_LIST_FOREACH(search_dirs, l, dir)
1506 char file[PATH_MAX];
1508 snprintf(file, sizeof(file), "%s/%s/%s", dir, (p ? p : ""),
1510 if (ecore_file_exists(file))
1512 path = eina_stringshare_add(file);
1518 /* nothing to do if no file found */
1519 if (!path) return 1;
1521 if (!efreet_menu_merge(parent, xml, path))
1524 eina_stringshare_del(path);
1531 * @param parent The parent menu to merge into
1532 * @param xml The XML to be merged
1533 * @param path The path to the .menu file to merge
1536 efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
1538 Efreet_Xml *merge_xml;
1539 Efreet_Menu_Internal *internal;
1542 if (!parent || !xml || !path) return 0;
1544 /* do nothing if the file doesn't exist */
1545 if (!ecore_file_exists(path)) return 1;
1547 if (!realpath(path, rp))
1549 INF("efreet_menu_merge() unable to get real path for %s", path);
1553 /* don't merge the same path twice */
1554 if (eina_hash_find(efreet_merged_menus, rp))
1559 eina_hash_add(efreet_merged_menus, rp, (void *)1);
1561 merge_xml = efreet_xml_new(rp);
1565 INF("efreet_menu_merge() failed to read in the "
1566 "merge file (%s)", rp);
1570 internal = efreet_menu_internal_new();
1571 if (!internal) return 0;
1572 efreet_menu_path_set(internal, path);
1573 efreet_menu_handle_menu(internal, merge_xml);
1574 efreet_menu_concatenate(parent, internal);
1575 efreet_menu_internal_free(internal);
1577 efreet_xml_del(merge_xml);
1584 * @param parent The parent menu
1585 * @param xml The xml tree
1586 * @return Returns 1 on success or 0 on failure
1587 * @brief Handles the MergeDir tag
1590 efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1595 if (!parent || !xml || !xml->text) return 0;
1597 path = efreet_menu_path_get(parent, xml->text);
1598 if (!path) return 1;
1599 if (!ecore_file_exists(path))
1601 eina_stringshare_del(path);
1605 ret = efreet_menu_merge_dir(parent, xml, path);
1606 eina_stringshare_del(path);
1613 * @param parent the parent menu of the merge
1614 * @param xml The xml tree
1615 * @param path The path to the merge directory
1616 * @return Returns 1 on success or 0 on failure
1617 * @brief Find all of the .menu files in the given directory and merge them
1618 * into the @a parent menu.
1621 efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
1624 Eina_File_Direct_Info *info;
1626 if (!parent || !xml || !path) return 0;
1628 /* check to see if we've merged this directory already */
1629 if (eina_hash_find(efreet_merged_dirs, path)) return 1;
1630 eina_hash_add(efreet_merged_dirs, path, (void *)1);
1632 it = eina_file_direct_ls(path);
1635 EINA_ITERATOR_FOREACH(it, info)
1639 p = strrchr(info->path + info->name_start, '.');
1641 if (strcmp(p, ".menu")) continue;
1643 if (!efreet_menu_merge(parent, xml, info->path))
1645 eina_iterator_free(it);
1649 eina_iterator_free(it);
1656 * @param parent The parent menu
1657 * @param xml The xml tree
1658 * @return Returns 1 on success or 0 on failure
1659 * @brief Handles the DefaultMergeDirs tag
1662 efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1665 char path[PATH_MAX], *p, *pp;
1667 char parent_path[PATH_MAX];
1671 if (!parent || !xml) return 0;
1673 prefix = efreet_menu_prefix_get();
1674 if (!strcmp(prefix, "gnome-") &&
1675 (!strcmp(parent->file.name, "gnome-applications.menu")))
1677 p = alloca(sizeof("applications"));
1678 memcpy(p, "applications", sizeof("applications"));
1680 else if ((!strcmp(prefix, "kde-") &&
1681 (!strcmp(parent->file.name, "kde-applications.menu"))))
1683 p = alloca(sizeof("applications"));
1684 memcpy(p, "applications", sizeof("applications"));
1691 len = strlen(parent->file.name) + 1;
1693 memcpy(p, parent->file.name, len);
1694 s = strrchr(p, '.');
1697 snprintf(path, sizeof(path), "menus/%s-merged", p);
1699 dirs = efreet_default_dirs_get(efreet_config_home_get(),
1700 efreet_config_dirs_get(), path);
1702 EINA_LIST_FREE(dirs, pp)
1704 efreet_menu_merge_dir(parent, xml, pp);
1705 eina_stringshare_del(pp);
1708 /* Also check the path of the parent file */
1709 snprintf(parent_path, sizeof(parent_path), "%s/%s", parent->file.path, path);
1710 efreet_menu_merge_dir(parent, xml, parent_path);
1718 * @param parent The parent menu
1719 * @param xml The xml tree
1720 * @return Returns 1 on success or 0 on failure
1721 * @brief Handles the LegacyDir tag
1724 efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1726 Efreet_Menu_Internal *legacy;
1728 if (!parent || !xml) return 0;
1730 legacy = efreet_menu_handle_legacy_dir_helper(NULL, parent, xml->text,
1731 efreet_xml_attribute_get(xml, "prefix"));
1734 efreet_menu_concatenate(parent, legacy);
1735 efreet_menu_internal_free(legacy);
1744 * @param parent The parent menu
1745 * @param legacy_dir The legacy directory path
1746 * @param prefix The legacy directory prefix if one set
1747 * @return Returns the Efreet_Menu_Internal representing the legacy hierarchy
1748 * @brief Handles the process of merging @a legacy_dir into @a parent menu
1750 static Efreet_Menu_Internal *
1751 efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root,
1752 Efreet_Menu_Internal *parent,
1753 const char *legacy_dir,
1757 Efreet_Menu_Internal *legacy_internal;
1758 Efreet_Menu_Filter *filter;
1759 Efreet_Menu_App_Dir *app_dir;
1763 if (!parent || !legacy_dir) return 0;
1765 path = efreet_menu_path_get(parent, legacy_dir);
1767 /* nothing to do if the legacy path doesn't exist */
1768 if (!path || !ecore_file_exists(path))
1770 eina_stringshare_del(path);
1774 legacy_internal = efreet_menu_internal_new();
1775 if (!legacy_internal)
1777 legacy_internal->name.internal = eina_stringshare_add(ecore_file_file_get(path));
1779 /* add the legacy dir as an app dir */
1780 app_dir = efreet_menu_app_dir_new();
1781 app_dir->path = eina_stringshare_add(path);
1782 app_dir->legacy = 1;
1783 if (prefix && !strchr(prefix, '/')) app_dir->prefix = eina_stringshare_add(prefix);
1785 efreet_menu_create_app_dirs_list(legacy_internal);
1786 legacy_internal->app_dirs = eina_list_append(legacy_internal->app_dirs, app_dir);
1790 /* XXX This seems wrong, but it makes efreet pass the fdo tests */
1791 app_dir = efreet_menu_app_dir_new();
1792 app_dir->path = eina_stringshare_add(path);
1793 app_dir->legacy = 1;
1794 if (prefix && !strchr(prefix, '/')) app_dir->prefix = eina_stringshare_add(prefix);
1795 root->app_dirs = eina_list_append(root->app_dirs, app_dir);
1799 /* add the legacy dir as a directory dir */
1800 efreet_menu_create_directory_dirs_list(legacy_internal);
1801 legacy_internal->directory_dirs = eina_list_append(legacy_internal->directory_dirs, eina_stringshare_add(path));
1803 /* setup a filter for all the conforming .desktop files in the legacy
1805 filter = efreet_menu_filter_new();
1808 efreet_menu_internal_free(legacy_internal);
1811 filter->type = EFREET_MENU_FILTER_INCLUDE;
1813 filter->op->type = EFREET_MENU_FILTER_OP_OR;
1815 efreet_menu_create_filter_list(legacy_internal);
1816 legacy_internal->filters = eina_list_append(legacy_internal->filters, filter);
1818 it = eina_file_direct_ls(path);
1821 Eina_File_Direct_Info *info;
1823 EINA_ITERATOR_FOREACH(it, info)
1825 Efreet_Desktop *desktop = NULL;
1830 fname = info->path + info->name_start;
1831 /* recurse into sub directories */
1832 if (ecore_file_is_dir(info->path))
1834 Efreet_Menu_Internal *ret;
1836 ret = efreet_menu_handle_legacy_dir_helper(root ? root : legacy_internal,
1837 legacy_internal, info->path, prefix);
1840 efreet_menu_internal_free(legacy_internal);
1841 eina_stringshare_del(path);
1842 eina_iterator_free(it);
1846 efreet_menu_create_sub_menu_list(legacy_internal);
1847 legacy_internal->sub_menus = eina_list_prepend(legacy_internal->sub_menus, ret);
1852 if (!strcmp(fname, ".directory"))
1854 legacy_internal->directory = efreet_desktop_get(info->path);
1855 if (legacy_internal->directory
1856 && legacy_internal->directory->type != EFREET_DESKTOP_TYPE_DIRECTORY)
1858 efreet_desktop_free(legacy_internal->directory);
1859 legacy_internal->directory = NULL;
1864 exten = strrchr(fname, '.');
1866 if (exten && !strcmp(exten, ".desktop"))
1867 desktop = efreet_desktop_get(info->path);
1869 if (!desktop) continue;
1871 /* if the .desktop has categories it isn't legacy */
1872 if (efreet_desktop_category_count_get(desktop) != 0)
1874 efreet_desktop_free(desktop);
1878 /* XXX: This will disappear when the .desktop is free'd */
1879 efreet_desktop_category_add(desktop, "Legacy");
1883 snprintf(buf, sizeof(buf), "%s%s", prefix, fname);
1884 filter->op->filenames = eina_list_append(filter->op->filenames, eina_stringshare_add(buf));
1887 filter->op->filenames = eina_list_append(filter->op->filenames, eina_stringshare_add(fname));
1890 efreet_desktop_free(desktop);
1892 eina_iterator_free(it);
1895 eina_stringshare_del(path);
1896 return legacy_internal;
1901 * @param parent The parent menu
1903 * @return Returns 1 on success or 0 on failure
1904 * @brief Handles the KDELegacyDirs tag
1907 efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1912 if (!parent) return 0;
1914 if (!efreet_menu_kde_legacy_dirs) return 1;
1916 /* XXX if one _helper() call succeeds, we return success. should this be flipped?
1917 * (return fail if on of them failed) */
1918 EINA_LIST_FOREACH(efreet_menu_kde_legacy_dirs, l, dir)
1920 Efreet_Menu_Internal *kde;
1922 kde = efreet_menu_handle_legacy_dir_helper(NULL, parent, dir, "kde");
1925 efreet_menu_concatenate(parent, kde);
1926 efreet_menu_internal_free(kde);
1936 * @param parent The parent menu
1937 * @param xml The xml tree
1938 * @return Returns 1 on success or 0 on failure
1939 * @brief Handles the Move tag and all subtags
1942 efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1947 if (!parent || !xml) return 0;
1949 efreet_menu_create_move_list(parent);
1951 EINA_LIST_FOREACH(xml->children, l, child)
1953 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
1955 cb = eina_hash_find(efreet_menu_move_cbs, child->tag);
1958 if (!cb(parent, child))
1963 INF("efreet_menu_handle_move() unknown tag found "
1964 "in Move (%s)", child->tag);
1969 parent->current_move = NULL;
1976 * @param parent The parent menu
1977 * @param xml The xml tree
1978 * @return Returns 1 on success or 0 on failure
1979 * @brief Handles the Old tag
1982 efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1984 Efreet_Menu_Move *move;
1986 if (!parent || !xml || !xml->text) return 0;
1988 if (parent->current_move)
1990 INF("efreet_menu_handle_old() saw second <Old> "
1991 "before seeing <New>");
1995 /* If we already moved this menu, remove the old move */
1996 /* XXX This seems wrong, but it makes efreet pass the fdo tests */
1998 move = eina_list_search_unsorted(parent->moves,
1999 EINA_COMPARE_CB(efreet_menu_cb_move_compare),
2003 efreet_menu_move_free(move);
2004 parent->moves = eina_list_remove(parent->moves, move);
2008 move = efreet_menu_move_new();
2009 move->old_name = eina_stringshare_add(xml->text);
2011 parent->current_move = move;
2012 parent->moves = eina_list_append(parent->moves, move);
2019 * @param parent The parent menu
2020 * @param xml The xml tree
2021 * @return Returns 1 on success or 0 on failure
2022 * @brief Handles the New tag
2025 efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2027 if (!parent || !xml || !xml->text) return 0;
2029 if (!parent->current_move)
2031 INF("efreet_menu_handle_new() saw New before seeing Old");
2035 parent->current_move->new_name = eina_stringshare_add(xml->text);
2036 parent->current_move = NULL;
2043 * @param parent The parent menu
2044 * @param xml The xml tree
2045 * @return Returns 1 on success or 0 on failure
2046 * @brief Handles the Layout tag and all subtags
2049 efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2054 if (!parent || !xml) return 0;
2056 /* We use the last existing layout */
2057 if (parent->layout) return 1;
2059 efreet_menu_create_layout_list(parent);
2061 EINA_LIST_FOREACH(xml->children, l, child)
2063 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
2065 cb = eina_hash_find(efreet_menu_layout_cbs, child->tag);
2068 if (!cb(parent, child, 0))
2073 INF("efreet_menu_handle_move() unknown tag found "
2074 "in Layout (%s)", child->tag);
2084 * @param parent The parent menu
2085 * @param xml The xml tree
2086 * @return Returns 1 on success or 0 on failure
2087 * @brief Handles the DefaultLayout tag
2090 efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2096 if (!parent || !xml) return 0;
2098 /* We use the last existing layout */
2099 if (parent->default_layout) return 1;
2101 val = efreet_xml_attribute_get(xml, "show_empty");
2102 if (val) parent->show_empty = !strcmp(val, "true");
2104 val = efreet_xml_attribute_get(xml, "inline");
2105 if (val) parent->in_line = !strcmp(val, "true");
2107 val = efreet_xml_attribute_get(xml, "inline_limit");
2108 if (val) parent->inline_limit = atoi(val);
2110 val = efreet_xml_attribute_get(xml, "inline_header");
2111 if (val) parent->inline_header = !strcmp(val, "true");
2113 val = efreet_xml_attribute_get(xml, "inline_alias");
2114 if (val) parent->inline_alias = !strcmp(val, "true");
2116 efreet_menu_create_default_layout_list(parent);
2118 EINA_LIST_FOREACH(xml->children, l, child)
2120 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
2122 cb = eina_hash_find(efreet_menu_layout_cbs, child->tag);
2125 if (!cb(parent, child, 1))
2130 INF("efreet_menu_handle_move() unknown tag found in "
2131 "DefaultLayout (%s)", child->tag);
2140 efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2142 Efreet_Menu_Layout *layout;
2145 if (!parent || !xml) return 0;
2149 INF("efreet_menu_handle_layout_menuname() The Menuname tag in "
2150 "layout needs a filename.");
2154 layout = efreet_menu_layout_new();
2155 layout->type = EFREET_MENU_LAYOUT_MENUNAME;
2156 layout->name = eina_stringshare_add(xml->text);
2158 val = efreet_xml_attribute_get(xml, "show_empty");
2159 if (val) layout->show_empty = !strcmp(val, "true");
2161 val = efreet_xml_attribute_get(xml, "inline");
2162 if (val) layout->in_line = !strcmp(val, "true");
2164 val = efreet_xml_attribute_get(xml, "inline_limit");
2165 if (val) layout->inline_limit = atoi(val);
2167 val = efreet_xml_attribute_get(xml, "inline_header");
2168 if (val) layout->inline_header = !strcmp(val, "true");
2170 val = efreet_xml_attribute_get(xml, "inline_alias");
2171 if (val) layout->inline_alias = !strcmp(val, "true");
2173 if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2174 else parent->layout = eina_list_append(parent->layout, layout);
2180 efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2182 Efreet_Menu_Layout *layout;
2184 if (!parent || !xml) return 0;
2188 INF("efreet_menu_handle_layout_filename() The Filename tag in "
2189 "layout needs a filename.");
2193 layout = efreet_menu_layout_new();
2194 layout->type = EFREET_MENU_LAYOUT_FILENAME;
2195 layout->name = eina_stringshare_add(xml->text);
2197 if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2198 else parent->layout = eina_list_append(parent->layout, layout);
2204 efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2206 Efreet_Menu_Layout *layout;
2208 if (!parent || !xml) return 0;
2210 layout = efreet_menu_layout_new();
2211 layout->type = EFREET_MENU_LAYOUT_SEPARATOR;
2213 parent->default_layout = eina_list_append(parent->default_layout, layout);
2215 parent->layout = eina_list_append(parent->layout, layout);
2220 efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2222 Efreet_Menu_Layout *layout;
2225 if (!parent || !xml) return 0;
2227 attr = efreet_xml_attribute_get(xml, "type");
2230 INF("efreet_menu_handle_layout_merge() The Merge tag in layout "
2231 "needs a type attribute.");
2235 if (strcmp(attr, "files") && strcmp(attr, "menus") && strcmp(attr, "all"))
2237 INF("efreet_menu_handle_layout_merge() The type attribute for "
2238 "the Merge tag contains an unknown value (%s).", attr);
2242 layout = efreet_menu_layout_new();
2243 layout->type = EFREET_MENU_LAYOUT_MERGE;
2244 layout->name = eina_stringshare_add(attr);
2246 if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2247 else parent->layout = eina_list_append(parent->layout, layout);
2254 * @param parent The parent menu
2255 * @param xml The XML tree to parse
2256 * @param type The type of filter
2257 * @return Returns 1 on success or 0 on failure
2258 * @brief Parses the given XML tree and adds the filter to the parent menu
2261 efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml,
2262 Efreet_Menu_Filter_Type type)
2264 Efreet_Menu_Filter *filter;
2266 efreet_menu_create_filter_list(parent);
2268 /* filters have a default or relationship */
2269 filter = efreet_menu_filter_new();
2270 if (!filter) return 0;
2271 filter->type = type;
2272 filter->op->type = EFREET_MENU_FILTER_OP_OR;
2274 if (!efreet_menu_handle_filter_op(filter->op, xml))
2276 efreet_menu_filter_free(filter);
2280 parent->filters = eina_list_prepend(parent->filters, filter);
2287 * @param op The operation to work with
2288 * @param xml The XML tree representing this operation
2289 * @return Returns 1 on success or 0 on failure
2290 * @brief Parses the given XML tree and populates the operation
2293 efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
2298 EINA_LIST_FOREACH(xml->children, l, child)
2300 int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
2302 cb = eina_hash_find(efreet_menu_filter_cbs, child->tag);
2310 INF("efreet_menu_handle_filter_op() unknown tag in filter (%s)", child->tag);
2319 * @return Returns a new Efreet_Menu_Filter on success or NULL on failure
2320 * @brief Creates and initializes an Efreet_Menu_Filter object
2322 static Efreet_Menu_Filter *
2323 efreet_menu_filter_new(void)
2325 Efreet_Menu_Filter *filter;
2327 filter = NEW(Efreet_Menu_Filter, 1);
2328 if (!filter) return NULL;
2329 filter->op = efreet_menu_filter_op_new();
2341 * @param filter The filter to work with
2342 * @return Returns no data
2343 * @brief Frees the given filter and all data
2346 efreet_menu_filter_free(Efreet_Menu_Filter *filter)
2348 if (!filter) return;
2350 if (filter->op) efreet_menu_filter_op_free(filter->op);
2358 * @return Returns a new Efreet_Menu_Layout on success or NULL on failure
2359 * @brief Creates and initializes an Efreet_Menu_Layout object
2361 static Efreet_Menu_Layout *
2362 efreet_menu_layout_new(void)
2364 Efreet_Menu_Layout *layout;
2366 layout = NEW(Efreet_Menu_Layout, 1);
2367 layout->show_empty = -1;
2368 layout->in_line = -1;
2369 layout->inline_limit = -1;
2370 layout->inline_header = -1;
2371 layout->inline_alias = -1;
2378 * @param filter The filter to work with
2379 * @return Returns no data
2380 * @brief Frees the given filter and all data
2383 efreet_menu_layout_free(Efreet_Menu_Layout *layout)
2385 if (!layout) return;
2387 IF_RELEASE(layout->name);
2393 * @return Returns a new Efreet_Menu_Filter_Op on success or NULL on failure
2394 * @brief Creates and initializes an Efreet_Menu_Filter_Op structure
2396 static Efreet_Menu_Filter_Op *
2397 efreet_menu_filter_op_new(void)
2399 Efreet_Menu_Filter_Op *op;
2401 op = NEW(Efreet_Menu_Filter_Op, 1);
2408 * @param op The operation to work with
2409 * @return Returns no value.
2410 * @brief Frees the given operation and all sub data
2413 efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op)
2417 IF_FREE_LIST(op->categories, eina_stringshare_del);
2418 IF_FREE_LIST(op->filenames, eina_stringshare_del);
2419 IF_FREE_LIST(op->filters, efreet_menu_filter_op_free);
2426 * @return Returns a new Efreet_Menu_Desktop on success or NULL on failure
2427 * @brief Creates and returns an Efreet_Menu_Desktop
2429 static Efreet_Menu_Desktop *
2430 efreet_menu_desktop_new(void)
2432 Efreet_Menu_Desktop *md;
2434 md = NEW(Efreet_Menu_Desktop, 1);
2441 * @param md The Efreet_Menu_Desktop to free
2442 * @return Returns no value
2443 * @brief Frees the given structure
2446 efreet_menu_desktop_free(Efreet_Menu_Desktop *md)
2449 if (md->desktop) efreet_desktop_free(md->desktop);
2455 * @return Returns a new Efreet_Menu on success or NULL on failure
2456 * @brief Creates and returns an Efreet_Menu
2458 static Efreet_Menu *
2459 efreet_menu_entry_new(void)
2463 entry = NEW(Efreet_Menu, 1);
2469 efreet_menu_free(Efreet_Menu *entry)
2475 IF_RELEASE(entry->name);
2476 IF_RELEASE(entry->icon);
2477 EINA_LIST_FREE(entry->entries, sub)
2478 efreet_menu_free(sub);
2479 IF_RELEASE(entry->id);
2480 if (entry->desktop) efreet_desktop_free(entry->desktop);
2486 * @param op The op to add a child too
2487 * @param xml The XML tree of the child
2488 * @param type The type of child to add
2489 * @return Returns 1 on success or 0 on failure
2490 * @brief Parses the given XML tree and populates a new child operation.
2493 efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
2494 Efreet_Menu_Filter_Op_Type type)
2496 Efreet_Menu_Filter_Op *child_op;
2498 child_op = efreet_menu_filter_op_new();
2499 child_op->type = type;
2501 if (!efreet_menu_handle_filter_op(child_op, xml))
2503 efreet_menu_filter_op_free(child_op);
2507 op->filters = eina_list_append(op->filters, child_op);
2514 * @param menu The menu to work with
2515 * @param only_unallocated Do we only look for unallocated items?
2516 * @return Returns 1 if we've successfully processed the menu, 0 otherwise
2517 * @brief Handles the processing of the menu data to retrieve the .desktop
2518 * files for the menu
2521 efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2525 /* a menu _MUST_ have a name */
2526 if (!internal->name.internal || (internal->name.internal[0] == '\0'))
2529 /* handle filtering out .desktop files as needed. This deals with all
2531 efreet_menu_process_filters(internal, only_unallocated);
2533 if (internal->sub_menus)
2535 Efreet_Menu_Internal *sub_internal;
2537 EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
2539 sub_internal->parent = internal;
2540 efreet_menu_process(sub_internal, only_unallocated);
2547 /* This will walk through all of the app dirs and load all the .desktop
2548 * files into the cache for the menu. The .desktop files will have their
2549 * allocated flag set to 0 */
2551 efreet_menu_process_dirs(Efreet_Menu_Internal *internal)
2555 /* Scan application directories for .desktop files */
2556 if (!efreet_menu_app_dirs_process(internal))
2559 /* Scan directory directories for .directory file */
2560 if (!efreet_menu_directory_dirs_process(internal))
2563 if (internal->sub_menus)
2565 Efreet_Menu_Internal *sub_internal;
2567 EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
2569 sub_internal->parent = internal;
2570 efreet_menu_process_dirs(sub_internal);
2579 * @param menu the menu to process
2580 * @param only_unallocated Only handle menus that deal with unallocated items
2581 * @return Returns no value
2582 * @brief Handles the processing of the filters attached to the given menu.
2584 * For each include filter we'll add the items to our applications array. Each
2585 * exclude filter will remove items from the applications array
2588 efreet_menu_process_filters(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2590 Efreet_Menu_Filter *filter;
2591 Efreet_Menu_Desktop *md;
2596 /* nothing to do if we're checking the other option */
2597 if (only_unallocated != internal->only_unallocated) return;
2599 internal->applications = eina_list_free(internal->applications);
2601 if (!internal->filters) return;
2603 EINA_LIST_FOREACH(internal->filters, l, filter)
2605 /* skip excludes until we get an include */
2606 if (!included && (filter->type == EFREET_MENU_FILTER_EXCLUDE))
2610 if (filter->type == EFREET_MENU_FILTER_INCLUDE)
2614 matches = eina_hash_string_superfast_new(NULL);
2615 internal->applications = efreet_menu_process_app_pool(internal->app_pool, internal->applications,
2616 matches, filter, internal->only_unallocated);
2617 if (internal->parent)
2619 Efreet_Menu_Internal *parent;
2621 parent = internal->parent;
2623 internal->applications = efreet_menu_process_app_pool(parent->app_pool,
2624 internal->applications, matches, filter,
2625 internal->only_unallocated);
2626 } while ((parent = parent->parent));
2628 eina_hash_free(matches);
2632 /* check each item in our menu so far and see if it's excluded */
2633 l = internal->applications;
2634 while ((md = eina_list_data_get(l)))
2636 ll = eina_list_next(l);
2637 if (efreet_menu_filter_matches(filter->op, md))
2638 internal->applications = eina_list_remove_list(internal->applications, l);
2644 /* sort the menu applications. we do this in process filters so it will only
2645 * be done once per menu.*/
2646 if (eina_list_count(internal->applications))
2650 EINA_LIST_FOREACH_SAFE(internal->applications, l, l2, md)
2652 if (md->desktop->no_display)
2653 internal->applications = eina_list_remove_list(internal->applications, l);
2655 internal->applications = eina_list_sort(internal->applications,
2656 eina_list_count(internal->applications),
2657 EINA_COMPARE_CB(efreet_menu_cb_md_compare));
2663 * @param pool The app pool to iterate
2664 * @param applications The list of applications to append too
2665 * @param matches The hash of previously matched ids
2666 * @param filter The menu filter to run on the pool items
2667 * @param only_unallocated Do we check only unallocated pool items?
2668 * @return Returns no value.
2669 * @brief This will iterate the items in @a pool and append them to @a
2670 * applications if they match the @a filter given and aren't previoulsy entered
2671 * in @a matches. If @a only_unallocated is set we'll only only at the
2672 * .desktop files that haven't been previoulsy matched
2675 efreet_menu_process_app_pool(Eina_List *pool, Eina_List *applications,
2676 Eina_Hash *matches, Efreet_Menu_Filter *filter,
2677 unsigned int only_unallocated)
2679 Efreet_Menu_Desktop *md;
2682 EINA_LIST_FOREACH(pool, l, md)
2684 if (eina_hash_find(matches, md->id)) continue;
2685 if (only_unallocated && md->allocated) continue;
2686 if (efreet_menu_filter_matches(filter->op, md))
2688 applications = eina_list_append(applications, md);
2689 eina_hash_direct_add(matches, (void *)md->id, md);
2693 return applications;
2698 * @param op The filter operation to execute
2699 * @param md The desktop to run the filter on
2700 * @return Returns 1 if this desktop matches the given filter, 0 otherwise
2701 * @brief This will execute the given @a filter on the given desktop
2704 efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2706 if (op->type == EFREET_MENU_FILTER_OP_OR)
2707 return efreet_menu_filter_or_matches(op, md);
2709 if (op->type == EFREET_MENU_FILTER_OP_AND)
2710 return efreet_menu_filter_and_matches(op, md);
2712 if (op->type == EFREET_MENU_FILTER_OP_NOT)
2713 return efreet_menu_filter_not_matches(op, md);
2720 * @param op The filter operation to execute
2721 * @param md The desktop to execute on
2722 * @return Returns 1 if the desktop matches, 0 otherwise
2723 * @brief Executes the OR operation, @a op, on the desktop, @a md.
2726 efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2728 Efreet_Menu_Filter_Op *child;
2732 if (op->all) return 1;
2734 if (op->categories && md->desktop->categories)
2736 EINA_LIST_FOREACH(op->categories, l, t)
2738 if (eina_list_search_unsorted(md->desktop->categories,
2739 EINA_COMPARE_CB(strcmp), t))
2746 EINA_LIST_FOREACH(op->filenames, l, t)
2747 if (t == md->id) return 1;
2752 EINA_LIST_FOREACH(op->filters, l, child)
2754 if (efreet_menu_filter_matches(child, md))
2764 * @param op The filter operation to execute
2765 * @param md The desktop to execute on
2766 * @return Returns 1 if the desktop matches, 0 otherwise
2767 * @brief Executes the AND operation, @a op, on the desktop, @a md.
2770 efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2772 Efreet_Menu_Filter_Op *child;
2778 if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
2781 EINA_LIST_FOREACH(op->categories, l, t)
2783 if (!eina_list_search_unsorted(md->desktop->categories,
2784 EINA_COMPARE_CB(strcmp), t))
2791 EINA_LIST_FOREACH(op->filenames, l, t)
2793 if (t != md->id) return 0;
2799 EINA_LIST_FOREACH(op->filters, l, child)
2801 if (!efreet_menu_filter_matches(child, md))
2811 * @param op The filter operation to execute
2812 * @param md The desktop to execute on
2813 * @return Returns 1 if the desktop matches, 0 otherwise
2814 * @brief Executes the NOT operation, @a op, on the desktop, @a md.
2817 efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2819 Efreet_Menu_Filter_Op *child;
2823 /* !all means no desktops match */
2824 if (op->all) return 0;
2828 if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
2831 EINA_LIST_FOREACH(op->categories, l, t)
2833 if (eina_list_search_unsorted(md->desktop->categories,
2834 EINA_COMPARE_CB(strcmp), t))
2841 EINA_LIST_FOREACH(op->filenames, l, t)
2843 if (t == md->id) return 0;
2849 EINA_LIST_FOREACH(op->filters, l, child)
2851 if (efreet_menu_filter_matches(child, md))
2861 * @param dest The destination menu
2862 * @param src The source menu
2863 * @return Returns no value
2864 * @brief Takes the child elements of the menu @a src and puts then on the
2865 * _start_ of the menu @a dest.
2868 efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src)
2870 Efreet_Menu_Internal *submenu;
2872 if (!dest || !src) return;
2874 if (!dest->directory && src->directory)
2876 dest->directory = src->directory;
2877 src->directory = NULL;
2880 if (!dest->seen_allocated && src->seen_allocated)
2882 dest->only_unallocated = src->only_unallocated;
2883 dest->seen_allocated = 1;
2886 if (!dest->seen_deleted && src->seen_deleted)
2888 dest->deleted = src->deleted;
2889 dest->seen_deleted = 1;
2892 if (src->directories)
2894 efreet_menu_create_directories_list(dest);
2895 dest->directories = eina_list_merge(src->directories, dest->directories);
2896 src->directories = NULL;
2901 efreet_menu_create_app_dirs_list(dest);
2902 dest->app_dirs = eina_list_merge(src->app_dirs, dest->app_dirs);
2903 src->app_dirs = NULL;
2906 if (src->directory_dirs)
2908 efreet_menu_create_directory_dirs_list(dest);
2909 dest->directory_dirs = eina_list_merge(src->directory_dirs, dest->directory_dirs);
2910 src->directory_dirs = NULL;
2915 efreet_menu_create_move_list(dest);
2916 dest->moves = eina_list_merge(src->moves, dest->moves);
2922 efreet_menu_create_filter_list(dest);
2923 dest->filters = eina_list_merge(src->filters, dest->filters);
2924 src->filters = NULL;
2929 efreet_menu_create_sub_menu_list(dest);
2931 while ((submenu = eina_list_data_get(eina_list_last(src->sub_menus))))
2933 Efreet_Menu_Internal *match;
2935 src->sub_menus = eina_list_remove(src->sub_menus, submenu);
2936 /* if this menu is in the list already we just add to that */
2937 if ((match = eina_list_search_unsorted(dest->sub_menus,
2938 EINA_COMPARE_CB(efreet_menu_cb_menu_compare),
2941 efreet_menu_concatenate(match, submenu);
2942 efreet_menu_internal_free(submenu);
2945 dest->sub_menus = eina_list_prepend(dest->sub_menus, submenu);
2952 * @param menu The menu to work with
2953 * @return Returns no value
2954 * @brief Handles any \<Move\> commands in the menus
2957 efreet_menu_resolve_moves(Efreet_Menu_Internal *internal)
2959 Efreet_Menu_Internal *child;
2960 Efreet_Menu_Move *move;
2963 /* child moves are handled before parent moves */
2964 if (internal->sub_menus)
2966 EINA_LIST_FOREACH(internal->sub_menus, l, child)
2967 efreet_menu_resolve_moves(child);
2970 /* nothing to do if this menu has no moves */
2971 if (!internal->moves) return;
2973 EINA_LIST_FOREACH(internal->moves, l, move)
2975 Efreet_Menu_Internal *origin, *dest, *parent;
2977 /* if the origin path doesn't exist we do nothing */
2978 origin = efreet_menu_by_name_find(internal, move->old_name, &parent);
2979 if (!origin) continue;
2981 /* remove the origin menu from the parent */
2982 parent->sub_menus = eina_list_remove(parent->sub_menus, origin);
2984 /* if the destination path doesn't exist we just rename the origin
2985 * menu and append to the parents list of children */
2986 dest = efreet_menu_by_name_find(internal, move->new_name, &parent);
2989 char *path, *tmp, *t;
2992 /* if the dest path has /'s in it then we need to add menus to
2993 * fill out the paths */
2994 len = strlen(move->new_name) + 1;
2996 memcpy(t, move->new_name, len);
2998 path = strchr(tmp, '/');
3001 Efreet_Menu_Internal *ancestor;
3005 ancestor = efreet_menu_internal_new();
3006 if (!ancestor) goto error;
3007 ancestor->name.internal = eina_stringshare_add(tmp);
3009 efreet_menu_create_sub_menu_list(parent);
3010 parent->sub_menus = eina_list_append(parent->sub_menus, ancestor);
3014 path = strchr(tmp, '/');
3017 IF_RELEASE(origin->name.internal);
3018 origin->name.internal = eina_stringshare_add(tmp);
3020 efreet_menu_create_sub_menu_list(parent);
3021 parent->sub_menus = eina_list_append(parent->sub_menus, origin);
3025 efreet_menu_concatenate(dest, origin);
3026 efreet_menu_internal_free(origin);
3030 IF_FREE_LIST(internal->moves, efreet_menu_move_free);
3035 * @param menu The menu to start searching from
3036 * @param name The menu name to find
3037 * @param parent The parent of the found menu
3038 * @return Returns the menu with the given @a name or NULL if none found
3039 * @brief Searches the menu tree starting at @a menu looking for a menu with
3042 static Efreet_Menu_Internal *
3043 efreet_menu_by_name_find(Efreet_Menu_Internal *internal, const char *name, Efreet_Menu_Internal **parent)
3045 char *part, *tmp, *ptr;
3048 if (parent) *parent = internal;
3050 /* find the correct parent menu */
3051 len = strlen(name) + 1;
3053 memcpy(tmp, name, len);
3055 part = strchr(ptr, '/');
3060 if (!(internal = eina_list_search_unsorted(internal->sub_menus,
3061 EINA_COMPARE_CB(efreet_menu_cb_compare_names),
3068 part = strchr(ptr, '/');
3071 if (parent) *parent = internal;
3073 /* find the menu in the parent list */
3074 if (!(internal = eina_list_search_unsorted(internal->sub_menus,
3075 EINA_COMPARE_CB(efreet_menu_cb_compare_names),
3085 efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path)
3090 len = strlen(path) + 1;
3092 memcpy(tmp, path, len);
3093 p = strrchr(tmp, '/');
3099 internal->file.path = eina_stringshare_add(tmp);
3100 internal->file.name = eina_stringshare_add(p);
3106 * @return Returns a new Efreet_Menu_Move struct on success or NULL on failure
3107 * @brief Creates an returns a new Efreet_Menu_Move struct or NULL on failure
3109 static Efreet_Menu_Move *
3110 efreet_menu_move_new(void)
3112 Efreet_Menu_Move *move;
3114 move = NEW(Efreet_Menu_Move, 1);
3121 * @param move The Efreet_Menu_Move to free
3122 * @return Returns no value.
3123 * @brief Frees the given move structure
3126 efreet_menu_move_free(Efreet_Menu_Move *move)
3130 IF_RELEASE(move->old_name);
3131 IF_RELEASE(move->new_name);
3138 * @return Returns a new Efreet_Menu_App_Dir on success or NULL on failure
3139 * @brief Creates and initializes a new Efreet_Menu_App_Dir structure
3141 static Efreet_Menu_App_Dir *
3142 efreet_menu_app_dir_new(void)
3144 Efreet_Menu_App_Dir *dir;
3146 dir = NEW(Efreet_Menu_App_Dir, 1);
3153 * @param dir The Efreet_Menu_App_Dir to free
3154 * @return Returns no value.
3155 * @brief Frees the given dir structure
3158 efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir)
3162 IF_RELEASE(dir->path);
3163 IF_RELEASE(dir->prefix);
3169 * @param a The app dir to compare too
3170 * @param b The path to compare too
3171 * @return Returns 0 if the strings are equals, != 0 otherwise
3172 * @brief Compares the too strings
3175 efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b)
3177 if (!a->path || !b) return 1;
3178 if (a->path == b) return 0;
3179 return strcmp(a->path, b);
3183 efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal)
3185 if (!internal || internal->sub_menus) return;
3187 internal->sub_menus = NULL;
3191 efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal)
3193 if (!internal || internal->app_dirs) return;
3195 internal->app_dirs = NULL;
3199 efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal)
3201 if (!internal || internal->directory_dirs) return;
3203 internal->directory_dirs = NULL;
3207 efreet_menu_create_move_list(Efreet_Menu_Internal *internal)
3209 if (!internal || internal->moves) return;
3211 internal->moves = NULL;
3215 efreet_menu_create_filter_list(Efreet_Menu_Internal *internal)
3217 if (!internal || internal->filters) return;
3219 internal->filters = NULL;
3223 efreet_menu_create_layout_list(Efreet_Menu_Internal *internal)
3225 if (!internal || internal->layout) return;
3227 internal->layout = NULL;
3231 efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal)
3233 if (!internal || internal->default_layout) return;
3235 internal->default_layout = NULL;
3239 efreet_menu_create_directories_list(Efreet_Menu_Internal *internal)
3241 if (!internal || internal->directories) return;
3243 internal->directories = NULL;
3247 efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix)
3249 char path[PATH_MAX];
3252 /* see if we've got an absolute or relative path */
3253 if (suffix[0] == '/')
3254 snprintf(path, sizeof(path), "%s", suffix);
3258 if (!internal->file.path)
3260 INF("efreet_menu_handle_app_dir() missing menu path ...");
3263 snprintf(path, sizeof(path), "%s/%s", internal->file.path, suffix);
3267 while (path[len] == '/') path[len--] = '\0';
3269 return eina_stringshare_add(path);
3273 efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b)
3275 if (!a->name.internal || !b->name.internal) return 1;
3276 if (a->name.internal == b->name.internal) return 0;
3277 return strcmp(a->name.internal, b->name.internal);
3281 efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal)
3283 Efreet_Menu_App_Dir *app_dir;
3284 Efreet_Menu_Desktop *md;
3287 EINA_LIST_FREE(internal->app_pool, md)
3288 efreet_menu_desktop_free(md);
3290 EINA_LIST_FOREACH(internal->app_dirs, l, app_dir)
3291 efreet_menu_app_dir_scan(internal, app_dir->path, app_dir->prefix, app_dir->legacy);
3297 efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal, const char *path, const char *id, int legacy)
3299 Efreet_Desktop *desktop;
3300 Efreet_Menu_Desktop *menu_desktop;
3301 char buf2[PATH_MAX];
3303 Eina_File_Direct_Info *info;
3305 it = eina_file_direct_ls(path);
3308 EINA_ITERATOR_FOREACH(it, info)
3312 fname = info->path + info->name_start;
3314 snprintf(buf2, sizeof(buf2), "%s-%s", id, fname);
3316 strcpy(buf2, fname);
3318 if (ecore_file_is_dir(info->path))
3321 efreet_menu_app_dir_scan(internal, info->path, buf2, legacy);
3327 ext = strrchr(fname, '.');
3329 if (!ext || strcmp(ext, ".desktop")) continue;
3330 desktop = efreet_desktop_get(info->path);
3332 if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_APPLICATION)
3334 if (desktop) efreet_desktop_free(desktop);
3337 /* Don't add two files with the same id in the app pool */
3338 if (eina_list_search_unsorted(internal->app_pool,
3339 EINA_COMPARE_CB(efreet_menu_cb_md_compare_ids),
3342 if (desktop) efreet_desktop_free(desktop);
3346 menu_desktop = efreet_menu_desktop_new();
3347 menu_desktop->desktop = desktop;
3348 menu_desktop->id = eina_stringshare_add(buf2);
3349 internal->app_pool = eina_list_prepend(internal->app_pool, menu_desktop);
3352 eina_iterator_free(it);
3359 * @param menu The menu to work with
3360 * @return Returns 1 on success or 0 on failure
3361 * @brief Process the directory dirs in @a menu
3364 efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal)
3369 if (internal->directory_dirs)
3371 internal->directory_cache =
3372 eina_hash_string_superfast_new(EINA_FREE_CB(efreet_desktop_free));
3374 EINA_LIST_REVERSE_FOREACH(internal->directory_dirs, l, path)
3375 efreet_menu_directory_dir_scan(path, NULL, internal->directory_cache);
3378 if (internal->directories)
3380 EINA_LIST_REVERSE_FOREACH(internal->directories, l, path)
3382 internal->directory = efreet_menu_directory_get(internal, path);
3383 if (internal->directory) break;
3386 if (!internal->directory)
3387 internal->name.name = internal->name.internal;
3389 internal->name.name = internal->directory->name;
3396 * @param path The path to scan
3397 * @param relative_path The relative portion of the path
3398 * @param cache The cache to populate
3399 * @return Returns 1 on success or 0 on failure
3400 * @brief Scans the given directory dir for .directory files and adds the
3401 * applications to the cache
3404 efreet_menu_directory_dir_scan(const char *path, const char *relative_path,
3407 Efreet_Desktop *desktop;
3408 char buf2[PATH_MAX];
3410 Eina_File_Direct_Info *info;
3413 it = eina_file_direct_ls(path);
3416 EINA_ITERATOR_FOREACH(it, info)
3420 fname = info->path + info->name_start;
3422 snprintf(buf2, sizeof(buf2), "%s/%s", relative_path, fname);
3424 strcpy(buf2, fname);
3426 if (ecore_file_is_dir(info->path))
3427 efreet_menu_directory_dir_scan(info->path, buf2, cache);
3431 ext = strrchr(fname, '.');
3432 if (!ext || strcmp(ext, ".directory")) continue;
3434 desktop = efreet_desktop_get(info->path);
3435 if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_DIRECTORY)
3437 efreet_desktop_free(desktop);
3441 eina_hash_del(cache, buf2, NULL);
3442 eina_hash_add(cache, buf2, desktop);
3445 eina_iterator_free(it);
3452 * @param menu The menu to work with
3453 * @param path The path to work with
3454 * @return Returns the desktop file for this path or NULL if none exists
3455 * @brief Finds the desktop file for the given path.
3457 static Efreet_Desktop *
3458 efreet_menu_directory_get(Efreet_Menu_Internal *internal, const char *path)
3460 Efreet_Desktop *dir;
3462 if (internal->directory_cache)
3464 dir = eina_hash_find(internal->directory_cache, path);
3465 if (dir) return dir;
3468 if (internal->parent)
3469 return efreet_menu_directory_get(internal->parent, path);
3476 * @param a The first desktop
3477 * @param b The second desktop
3478 * @return Returns the comparison of the desktop files
3479 * @brief Compares the desktop files.
3482 efreet_menu_cb_md_compare(const Efreet_Menu_Desktop *a, const Efreet_Menu_Desktop *b)
3485 return strcmp(ecore_file_file_get(a->desktop->orig_path), ecore_file_file_get(b->desktop->orig_path));
3487 if (a->desktop->name == b->desktop->name) return 0;
3488 return strcasecmp(a->desktop->name, b->desktop->name);
3493 efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name)
3495 if (internal->name.internal == name) return 0;
3496 return strcmp(internal->name.internal, name);
3500 efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name)
3502 if (md->id == name) return 0;
3503 return strcmp(md->id, name);
3506 static Efreet_Menu *
3507 efreet_menu_layout_menu(Efreet_Menu_Internal *internal)
3510 Eina_List *layout = NULL;
3513 if (internal->parent)
3515 /* Copy default layout rules */
3516 if (internal->show_empty == -1) internal->show_empty = internal->parent->show_empty;
3517 if (internal->in_line == -1) internal->in_line = internal->parent->in_line;
3518 if (internal->inline_limit == -1) internal->inline_limit = internal->parent->inline_limit;
3519 if (internal->inline_header == -1) internal->inline_header = internal->parent->inline_header;
3520 if (internal->inline_alias == -1) internal->inline_alias = internal->parent->inline_alias;
3523 if (internal->layout)
3524 layout = internal->layout;
3526 else if (internal->parent)
3528 Efreet_Menu_Internal *parent;
3529 parent = internal->parent;
3532 layout = parent->default_layout;
3533 parent = parent->parent;
3534 } while (!layout && parent);
3538 entry = efreet_menu_entry_new();
3539 entry->type = EFREET_MENU_ENTRY_MENU;
3540 entry->id = eina_stringshare_add(internal->name.internal);
3541 entry->name = eina_stringshare_add(internal->name.name);
3542 if (internal->directory)
3544 entry->icon = eina_stringshare_add(internal->directory->icon);
3545 efreet_desktop_ref(internal->directory);
3546 entry->desktop = internal->directory;
3548 entry->entries = NULL;
3551 if (internal->sub_menus)
3553 internal->sub_menus = eina_list_sort(internal->sub_menus,
3555 EINA_COMPARE_CB(efreet_menu_cb_menu_compare));
3561 Efreet_Menu_Layout *lay;
3563 EINA_LIST_FOREACH(layout, l, lay)
3564 efreet_menu_layout_entries_get(entry, internal, lay);
3568 /* Default layout, first menus, then desktop */
3569 if (internal->sub_menus)
3571 Efreet_Menu_Internal *sub;
3573 EINA_LIST_FOREACH(internal->sub_menus, l, sub)
3575 Efreet_Menu *sub_entry;
3576 if ((sub->directory && sub->directory->no_display) || sub->deleted) continue;
3577 sub_entry = efreet_menu_layout_menu(sub);
3578 /* Don't show empty menus */
3579 if (!sub_entry->entries)
3581 efreet_menu_free(sub_entry);
3584 entry->entries = eina_list_append(entry->entries, sub_entry);
3588 if (internal->applications)
3590 Efreet_Menu_Desktop *md;
3592 EINA_LIST_FOREACH(internal->applications, l, md)
3594 Efreet_Menu *sub_entry;
3595 sub_entry = efreet_menu_layout_desktop(md);
3596 entry->entries = eina_list_append(entry->entries, sub_entry);
3601 /* Don't keep this list around if it is empty */
3606 static Efreet_Menu *
3607 efreet_menu_layout_desktop(Efreet_Menu_Desktop *md)
3612 entry = efreet_menu_entry_new();
3613 entry->type = EFREET_MENU_ENTRY_DESKTOP;
3614 entry->id = eina_stringshare_add(md->id);
3615 entry->name = eina_stringshare_add(md->desktop->name);
3616 if (md->desktop->icon) entry->icon = eina_stringshare_add(md->desktop->icon);
3617 efreet_desktop_ref(md->desktop);
3618 entry->desktop = md->desktop;
3624 efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal,
3625 Efreet_Menu_Layout *layout)
3627 Efreet_Menu *sub_entry;
3629 if (internal->sub_menus && layout->type == EFREET_MENU_LAYOUT_MENUNAME)
3631 Efreet_Menu_Internal *sub;
3633 /* Efreet_Menu_Layout might be from DefaultLayout, so we need a local copy */
3634 int show_empty, in_line, inline_limit, inline_header, inline_alias;
3636 if (layout->show_empty == -1) show_empty = internal->show_empty;
3637 else show_empty = layout->show_empty;
3639 if (layout->in_line == -1) in_line = internal->in_line;
3640 else in_line = layout->in_line;
3642 if (layout->inline_limit == -1) inline_limit = internal->inline_limit;
3643 else inline_limit = layout->inline_limit;
3645 if (layout->inline_header == -1) inline_header = internal->inline_header;
3646 else inline_header = layout->inline_header;
3648 if (layout->inline_alias == -1) inline_alias = internal->inline_alias;
3649 else inline_alias = layout->inline_alias;
3651 sub = eina_list_search_unsorted(internal->sub_menus,
3652 EINA_COMPARE_CB(efreet_menu_cb_compare_names), layout->name);
3655 if (!(sub->directory && sub->directory->no_display) && !sub->deleted)
3657 sub_entry = efreet_menu_layout_menu(sub);
3658 if (!show_empty && efreet_menu_layout_is_empty(sub_entry))
3659 efreet_menu_free(sub_entry);
3661 ((inline_limit == 0) ||
3662 (!sub_entry->entries ||
3663 (inline_limit > 0 && eina_list_count(sub_entry->entries) <= (unsigned int)inline_limit))))
3666 if (!sub_entry->entries)
3668 /* Can't inline an empty submenu */
3669 entry->entries = eina_list_append(entry->entries, sub_entry);
3671 else if (inline_alias && (eina_list_count(sub_entry->entries) == 1))
3675 tmp = eina_list_data_get(sub_entry->entries);
3676 sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3677 IF_RELEASE(tmp->name);
3678 tmp->name = sub_entry->name;
3679 sub_entry->name = NULL;
3680 IF_RELEASE(tmp->icon);
3681 tmp->icon = sub_entry->icon;
3682 sub_entry->icon = NULL;
3683 entry->entries = eina_list_append(entry->entries, tmp);
3684 efreet_menu_free(sub_entry);
3692 tmp = efreet_menu_entry_new();
3693 tmp->type = EFREET_MENU_ENTRY_HEADER;
3694 tmp->name = sub_entry->name;
3695 sub_entry->name = NULL;
3696 tmp->icon = sub_entry->icon;
3697 sub_entry->icon = NULL;
3698 entry->entries = eina_list_append(entry->entries, tmp);
3700 while ((tmp = eina_list_data_get(sub_entry->entries)))
3702 sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3703 entry->entries = eina_list_append(entry->entries, tmp);
3705 efreet_menu_free(sub_entry);
3709 entry->entries = eina_list_append(entry->entries, sub_entry);
3711 internal->sub_menus = eina_list_remove(internal->sub_menus, sub);
3712 efreet_menu_internal_free(sub);
3715 else if (internal->applications && layout->type == EFREET_MENU_LAYOUT_FILENAME)
3717 Efreet_Menu_Desktop *md;
3718 md = eina_list_search_unsorted(internal->applications,
3719 EINA_COMPARE_CB(efreet_menu_cb_md_compare_ids), layout->name);
3722 sub_entry = efreet_menu_layout_desktop(md);
3723 entry->entries = eina_list_append(entry->entries, sub_entry);
3724 internal->applications = eina_list_remove(internal->applications, md);
3727 else if (layout->type == EFREET_MENU_LAYOUT_MERGE)
3729 if (internal->applications && !strcmp(layout->name, "files"))
3731 Efreet_Menu_Desktop *md;
3733 while ((md = eina_list_data_get(internal->applications)))
3735 internal->applications = eina_list_remove_list(internal->applications,
3736 internal->applications);
3737 sub_entry = eina_list_search_unsorted(entry->entries,
3738 EINA_COMPARE_CB(efreet_menu_cb_entry_compare_desktop),
3742 sub_entry = efreet_menu_layout_desktop(md);
3743 entry->entries = eina_list_append(entry->entries, sub_entry);
3746 internal->applications = eina_list_free(internal->applications);
3748 else if (internal->sub_menus && !strcmp(layout->name, "menus"))
3750 Efreet_Menu_Internal *sub;
3752 while ((sub = eina_list_data_get(internal->sub_menus)))
3754 internal->sub_menus = eina_list_remove_list(internal->sub_menus, internal->sub_menus);
3755 if ((sub->directory && sub->directory->no_display) || sub->deleted)
3757 efreet_menu_internal_free(sub);
3760 sub_entry = eina_list_search_unsorted(entry->entries,
3761 EINA_COMPARE_CB(efreet_menu_cb_entry_compare_menu),
3765 sub_entry = efreet_menu_layout_menu(sub);
3766 if (!internal->show_empty && efreet_menu_layout_is_empty(sub_entry))
3767 efreet_menu_free(sub_entry);
3768 else if (internal->in_line &&
3769 ((internal->inline_limit == 0) ||
3770 (!sub_entry->entries ||
3771 (internal->inline_limit > 0 && eina_list_count(sub_entry->entries) <= (unsigned int)internal->inline_limit))))
3774 if (!sub_entry->entries)
3776 /* Can't inline an empty submenu */
3777 entry->entries = eina_list_append(entry->entries, sub_entry);
3779 else if (internal->inline_alias && (eina_list_count(sub_entry->entries) == 1))
3783 tmp = eina_list_data_get(sub_entry->entries);
3784 sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3785 eina_stringshare_del(tmp->name);
3786 tmp->name = sub_entry->name;
3787 sub_entry->name = NULL;
3788 IF_RELEASE(tmp->icon);
3789 if (sub_entry->icon)
3791 tmp->icon = sub_entry->icon;
3792 sub_entry->icon = NULL;
3794 entry->entries = eina_list_append(entry->entries, tmp);
3795 efreet_menu_free(sub_entry);
3801 if (internal->inline_header)
3803 tmp = efreet_menu_entry_new();
3804 tmp->type = EFREET_MENU_ENTRY_HEADER;
3805 tmp->name = sub_entry->name;
3806 sub_entry->name = NULL;
3807 if (sub_entry->icon) tmp->icon = sub_entry->icon;
3808 sub_entry->icon = NULL;
3809 entry->entries = eina_list_append(entry->entries, tmp);
3811 while ((tmp = eina_list_data_get(sub_entry->entries)))
3813 sub_entry->entries = eina_list_remove_list(sub_entry->entries,
3814 sub_entry->entries);
3815 entry->entries = eina_list_append(entry->entries, tmp);
3817 efreet_menu_free(sub_entry);
3821 entry->entries = eina_list_append(entry->entries, sub_entry);
3823 efreet_menu_internal_free(sub);
3825 IF_FREE_LIST(internal->sub_menus, efreet_menu_internal_free);
3827 else if (internal->sub_menus && !strcmp(layout->name, "all"))
3831 orig = layout->name;
3832 layout->name = "menus";
3833 efreet_menu_layout_entries_get(entry, internal, layout);
3834 layout->name = "files";
3835 efreet_menu_layout_entries_get(entry, internal, layout);
3836 layout->name = orig;
3839 else if (layout->type == EFREET_MENU_LAYOUT_SEPARATOR)
3841 sub_entry = efreet_menu_entry_new();
3842 sub_entry->type = EFREET_MENU_ENTRY_SEPARATOR;
3843 entry->entries = eina_list_append(entry->entries, sub_entry);
3848 efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal)
3850 if (entry->type != EFREET_MENU_ENTRY_MENU) return 1;
3851 if (!entry->name || !internal->name.name) return 1;
3852 if (entry->name == internal->name.name) return 0;
3853 return strcmp(entry->name, internal->name.name);
3857 efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop)
3859 if (entry->type != EFREET_MENU_ENTRY_DESKTOP) return -1;
3860 if (!entry->name || !desktop->name) return -1;
3861 if (entry->name == desktop->name) return 0;
3862 return strcmp(entry->name, desktop->name);
3866 efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old)
3868 if (!move->old_name || !old) return 1;
3869 if (move->old_name == old) return 0;
3874 efreet_menu_layout_is_empty(Efreet_Menu *entry)
3876 Efreet_Menu *sub_entry;
3879 if (!entry->entries) return 1;
3881 EINA_LIST_FOREACH(entry->entries, l, sub_entry)
3883 if (sub_entry->type == EFREET_MENU_ENTRY_MENU) return 0;
3884 if (sub_entry->type == EFREET_MENU_ENTRY_DESKTOP) return 0;