1 /* vim: set sw=4 ts=4 sts=4 et: */
3 #include "efreet_private.h"
8 typedef struct Efreet_Menu_Move Efreet_Menu_Move;
12 * Info on a menu movement
14 struct Efreet_Menu_Move
16 char *old_name; /**< The menu path to move from */
17 char *new_name; /**< The menu path to move too */
21 * Efreet_Menu_Internal
23 typedef struct Efreet_Menu_Internal Efreet_Menu_Internal;
26 * Efreet_Menu_Internal
27 * Contains the information about a menu
29 struct Efreet_Menu_Internal
33 char *path; /**< The base file path */
34 char *name; /**< The filename for this menu */
35 } file; /**< The menu file information */
39 const char *internal; /**< The menu name */
40 const char *name; /**< Name to use in the menus */
41 } name; /**< The names for this menu */
43 Efreet_Desktop *directory; /**< The directory */
44 Eina_List *directories; /**< All the directories set in the menu file */
46 Efreet_Menu_Move *current_move; /**< The current move */
48 Eina_List *app_dirs; /**< .desktop application directories */
50 Eina_List *app_pool; /**< application pool */
51 Eina_List *applications; /**< applications in this menu */
53 Eina_List *directory_dirs; /**< .directory file directories */
54 Eina_Hash *directory_cache; /**< .directory dirs */
56 Eina_List *moves; /**< List of moves to be handled by the menu */
57 Eina_List *filters; /**< Include and Exclude filters */
59 Efreet_Menu_Internal *parent; /**< Our parent menu */
60 Eina_List *sub_menus; /**< Our sub menus */
62 Eina_List *layout; /**< This menus layout */
63 Eina_List *default_layout; /**< Default layout */
64 signed char show_empty; /**< Whether to show empty menus */
65 signed char in_line; /**< Whether this meny can be inlined */
66 signed char inline_limit; /**< Number of elements which triggers inline */
67 signed char inline_header; /**< Whether we should use the header name when this menu is inlined */
68 signed char inline_alias; /**< Whether we should use the menu name when inlining */
70 unsigned char seen_allocated:1; /**< have we set the only_unallocated */
71 unsigned char only_unallocated:1; /**< Show only unallocated .desktops */
73 unsigned char seen_deleted:1; /**< Have we seen the deleted item yet */
74 unsigned char deleted:1; /**< The menu is deleted */
80 typedef struct Efreet_Menu_App_Dir Efreet_Menu_App_Dir;
83 * Holds information on an app dir
85 struct Efreet_Menu_App_Dir
87 char *path; /**< directory path */
88 char *prefix; /**< If it's legacy it can have a prefix */
89 unsigned int legacy:1; /**< is this a legacy dir */
93 * The type of operations we can perform with a filter
95 enum Efreet_Menu_Filter_Op_Type
97 EFREET_MENU_FILTER_OP_OR,
98 EFREET_MENU_FILTER_OP_AND,
99 EFREET_MENU_FILTER_OP_NOT
103 * Efreet_Menu_Filter_Op_Type
105 typedef enum Efreet_Menu_Filter_Op_Type Efreet_Menu_Filter_Op_Type;
110 enum Efreet_Menu_Filter_Type
112 EFREET_MENU_FILTER_INCLUDE,
113 EFREET_MENU_FILTER_EXCLUDE
117 * Efreet_Menu_Filter_Type
119 typedef enum Efreet_Menu_Filter_Type Efreet_Menu_Filter_Type;
122 * Efreet_Menu_Filter_Op
124 typedef struct Efreet_Menu_Filter_Op Efreet_Menu_Filter_Op;
127 * Efreet_Menu_Filter_Op
128 * Contains information on a filter operation
130 struct Efreet_Menu_Filter_Op
132 Efreet_Menu_Filter_Op_Type type; /**< The type of operation */
133 Eina_List *categories; /**< The categories this op applies too */
134 Eina_List *filenames; /**< The filenames this op applies too */
136 Eina_List *filters; /**< Child filters */
138 unsigned char all:1; /**< Applies to all .desktop files */
144 typedef struct Efreet_Menu_Filter Efreet_Menu_Filter;
148 * Stores information on a filter
150 struct Efreet_Menu_Filter
152 Efreet_Menu_Filter_Type type; /**< The type of filter */
153 Efreet_Menu_Filter_Op *op; /**< The filter operations */
159 enum Efreet_Menu_Layout_Type
161 EFREET_MENU_LAYOUT_MENUNAME,
162 EFREET_MENU_LAYOUT_FILENAME,
163 EFREET_MENU_LAYOUT_SEPARATOR,
164 EFREET_MENU_LAYOUT_MERGE
168 * Efreet_Menu_Layout_Type
170 typedef enum Efreet_Menu_Layout_Type Efreet_Menu_Layout_Type;
175 typedef struct Efreet_Menu_Layout Efreet_Menu_Layout;
179 * Stores information on a layout
181 struct Efreet_Menu_Layout
183 Efreet_Menu_Layout_Type type; /**< The type of layout */
184 char *name; /**< The name of the element */
186 /* The items below are for Menuname Layout elements */
187 signed char show_empty; /**< Whether to show empty menus */
188 signed char in_line; /**< Whether this meny can be inlined */
189 signed char inline_limit; /**< Number of elements which triggers inline */
190 signed char inline_header; /**< Whether we should use the header name when this menu is inlined */
191 signed char inline_alias; /**< Whether we should use the menu name when inlining */
195 * Efreet_Menu_Desktop
197 typedef struct Efreet_Menu_Desktop Efreet_Menu_Desktop;
200 * Efreet_Menu_Desktop
201 * Stores information on a desktop for the menu
203 struct Efreet_Menu_Desktop
205 Efreet_Desktop *desktop; /**< The desktop we refer too */
206 const char *id; /**< The desktop file id */
207 unsigned char allocated:1; /**< If this desktop has been allocated */
210 static char *efreet_menu_prefix = NULL; /**< The $XDG_MENU_PREFIX env var */
211 Eina_List *efreet_menu_kde_legacy_dirs = NULL; /**< The directories to use for KDELegacy entries */
212 static const char *efreet_tag_menu = NULL;
213 static char *efreet_menu_file = NULL; /**< A menu file set explicityl as default */
215 static Eina_Hash *efreet_merged_menus = NULL;
216 static Eina_Hash *efreet_merged_dirs = NULL;
218 static Eina_Hash *efreet_menu_handle_cbs = NULL;
219 static Eina_Hash *efreet_menu_filter_cbs = NULL;
220 static Eina_Hash *efreet_menu_move_cbs = NULL;
221 static Eina_Hash *efreet_menu_layout_cbs = NULL;
223 static const char *efreet_menu_prefix_get(void);
225 static Efreet_Menu_Internal *efreet_menu_by_name_find(Efreet_Menu_Internal *internal,
227 Efreet_Menu_Internal **parent);
228 static int efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name);
229 static int efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name);
231 static int efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal);
232 static int efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop);
234 static int efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old);
236 static int efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated);
237 static int efreet_menu_process_dirs(Efreet_Menu_Internal *internal);
238 static int efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal);
239 static int efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal,
243 static int efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal);
244 static int efreet_menu_directory_dir_scan(const char *path,
245 const char *relative_path,
247 static Efreet_Desktop *efreet_menu_directory_get(Efreet_Menu_Internal *internal,
249 static void efreet_menu_process_filters(Efreet_Menu_Internal *internal,
250 unsigned int only_unallocated);
251 static Eina_List * efreet_menu_process_app_pool(Eina_List *pool, Eina_List *applications,
253 Efreet_Menu_Filter *filter,
254 unsigned int only_unallocated);
255 static int efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op,
256 Efreet_Menu_Desktop *md);
257 static int efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op,
258 Efreet_Menu_Desktop *md);
259 static int efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op,
260 Efreet_Menu_Desktop *md);
261 static int efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op,
262 Efreet_Menu_Desktop *md);
264 static Efreet_Menu *efreet_menu_layout_menu(Efreet_Menu_Internal *internal);
265 static Efreet_Menu *efreet_menu_layout_desktop(Efreet_Menu_Desktop *md);
266 static void efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal,
267 Efreet_Menu_Layout *layout);
268 static int efreet_menu_layout_is_empty(Efreet_Menu *entry);
270 static Efreet_Menu_Internal *efreet_menu_internal_new(void);
271 static void efreet_menu_internal_free(Efreet_Menu_Internal *internal);
272 static void efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal);
273 static void efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal);
274 static void efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal);
275 static void efreet_menu_create_directories_list(Efreet_Menu_Internal *internal);
276 static void efreet_menu_create_move_list(Efreet_Menu_Internal *internal);
277 static void efreet_menu_create_filter_list(Efreet_Menu_Internal *internal);
278 static void efreet_menu_create_layout_list(Efreet_Menu_Internal *internal);
279 static void efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal);
280 static char *efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix);
282 static Efreet_Menu_App_Dir *efreet_menu_app_dir_new(void);
283 static void efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir);
285 static Efreet_Menu_Move *efreet_menu_move_new(void);
286 static void efreet_menu_move_free(Efreet_Menu_Move *move);
288 static Efreet_Menu_Filter *efreet_menu_filter_new(void);
289 static void efreet_menu_filter_free(Efreet_Menu_Filter *filter);
291 static Efreet_Menu_Layout *efreet_menu_layout_new(void);
292 static void efreet_menu_layout_free(Efreet_Menu_Layout *layout);
294 static Efreet_Menu_Filter_Op *efreet_menu_filter_op_new(void);
295 static void efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op);
297 static Efreet_Menu_Desktop *efreet_menu_desktop_new(void);
298 static void efreet_menu_desktop_free(Efreet_Menu_Desktop *md);
300 static Efreet_Menu *efreet_menu_entry_new(void);
302 static int efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml);
303 static int efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
305 static int efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
306 static int efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
307 static int efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
308 static int efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
309 static int efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
310 static int efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
311 static int efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
312 static int efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
313 static int efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
314 static int efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
315 static int efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
316 static int efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
317 static int efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
318 static int efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
319 static int efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
320 static int efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
321 static int efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
322 static int efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
323 static int efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
324 static int efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
325 static int efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
326 static int efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
327 static Efreet_Menu_Internal *efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root,
328 Efreet_Menu_Internal *parent,
329 const char *legacy_dir,
331 static int efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
332 static int efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
333 static int efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
334 static int efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
335 static int efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
336 static int efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
338 static int efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml,
339 Efreet_Menu_Filter_Type type);
340 static int efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
341 static int efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
342 Efreet_Menu_Filter_Op_Type type);
344 static int efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
345 static int efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
346 static int efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
347 static int efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
349 static int efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path);
350 static int efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path);
352 static int efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b);
354 static void efreet_menu_resolve_moves(Efreet_Menu_Internal *internal);
355 static void efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src);
357 static int efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b);
358 static int efreet_menu_cb_md_compare(Efreet_Menu_Desktop *a, Efreet_Menu_Desktop *b);
360 static int efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent);
361 static int efreet_menu_save_indent(FILE *f, int indent);
363 static void efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path);
366 * @return Returns 1 on success, 0 on failure
367 * @brief Initializes the Efreet Menu system.
370 efreet_menu_init(void)
377 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
379 {"Menu", efreet_menu_handle_sub_menu},
380 {"AppDir", efreet_menu_handle_app_dir},
381 {"DefaultAppDirs", efreet_menu_handle_default_app_dirs},
382 {"DirectoryDir", efreet_menu_handle_directory_dir},
383 {"DefaultDirectoryDirs", efreet_menu_handle_default_directory_dirs},
384 {"Name", efreet_menu_handle_name},
385 {"Directory", efreet_menu_handle_directory},
386 {"OnlyUnallocated", efreet_menu_handle_only_unallocated},
387 {"NotOnlyUnallocated", efreet_menu_handle_not_only_unallocated},
388 {"Deleted", efreet_menu_handle_deleted},
389 {"NotDeleted", efreet_menu_handle_not_deleted},
390 {"Include", efreet_menu_handle_include},
391 {"Exclude", efreet_menu_handle_exclude},
392 {"MergeFile", efreet_menu_handle_merge_file},
393 {"MergeDir", efreet_menu_handle_merge_dir},
394 {"DefaultMergeDirs", efreet_menu_handle_default_merge_dirs},
395 {"LegacyDir", efreet_menu_handle_legacy_dir},
396 {"KDELegacyDirs", efreet_menu_handle_kde_legacy_dirs},
397 {"Move", efreet_menu_handle_move},
398 {"Layout", efreet_menu_handle_layout},
399 {"DefaultLayout", efreet_menu_handle_default_layout},
406 int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
408 {"Filename", efreet_menu_handle_filename},
409 {"Category", efreet_menu_handle_category},
410 {"All", efreet_menu_handle_all},
411 {"And", efreet_menu_handle_and},
412 {"Or", efreet_menu_handle_or},
413 {"Not", efreet_menu_handle_not},
420 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
422 {"Old", efreet_menu_handle_old},
423 {"New", efreet_menu_handle_new},
430 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
432 {"Menuname", efreet_menu_handle_layout_menuname},
433 {"Filename", efreet_menu_handle_layout_filename},
434 {"Separator", efreet_menu_handle_layout_separator},
435 {"Merge", efreet_menu_handle_layout_merge},
439 if (!eina_stringshare_init()) return 0;
440 if (!efreet_xml_init()) return 0;
442 efreet_menu_handle_cbs = eina_hash_string_superfast_new(NULL);
443 efreet_menu_filter_cbs = eina_hash_string_superfast_new(NULL);
444 efreet_menu_move_cbs = eina_hash_string_superfast_new(NULL);
445 efreet_menu_layout_cbs = eina_hash_string_superfast_new(NULL);
446 if (!efreet_menu_handle_cbs || !efreet_menu_filter_cbs
447 || !efreet_menu_move_cbs || !efreet_menu_layout_cbs)
450 /* set Menu into it's own so we can check the XML is valid before trying
452 efreet_tag_menu = eina_stringshare_add(menu_cbs[0].key);
454 for (i = 0; menu_cbs[i].key != NULL; i++)
456 eina_hash_del(efreet_menu_handle_cbs,
459 eina_hash_add(efreet_menu_handle_cbs,
463 for (i = 0; filter_cbs[i].key != NULL; i++)
465 eina_hash_del(efreet_menu_filter_cbs,
468 eina_hash_add(efreet_menu_filter_cbs,
472 for (i = 0; move_cbs[i].key != NULL; i++)
474 eina_hash_del(efreet_menu_move_cbs,
477 eina_hash_add(efreet_menu_move_cbs,
481 for (i = 0; layout_cbs[i].key != NULL; i++)
483 eina_hash_del(efreet_menu_layout_cbs,
486 eina_hash_add(efreet_menu_layout_cbs,
494 * @return Returns no value
495 * @brief Initialize legacy kde support. This function blocks while
496 * the kde-config script is run.
499 efreet_menu_kde_legacy_init(void)
505 IF_FREE_LIST(efreet_menu_kde_legacy_dirs, eina_stringshare_del);
507 f = popen("kde-config --path apps", "r");
510 /* XXX if the return from kde-config is a line longer than PATH_MAX,
511 * this won't be correct (increase buffer and get the rest...) */
512 if (!fgets(buf, PATH_MAX, f))
514 printf("Error initializing KDE legacy information\n");
523 efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
524 (void *)eina_stringshare_add(s));
530 efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
531 (void *)eina_stringshare_add(s));
538 * @return Returns no value
539 * @brief Shuts down the Efreet menu system
542 efreet_menu_shutdown(void)
544 IF_FREE(efreet_menu_prefix);
545 IF_FREE(efreet_menu_file);
547 IF_FREE_HASH(efreet_menu_handle_cbs);
548 IF_FREE_HASH(efreet_menu_filter_cbs);
549 IF_FREE_HASH(efreet_menu_move_cbs);
550 IF_FREE_HASH(efreet_menu_layout_cbs);
552 IF_FREE_LIST(efreet_menu_kde_legacy_dirs, eina_stringshare_del);
554 IF_FREE_HASH(efreet_merged_menus);
555 IF_FREE_HASH(efreet_merged_dirs);
557 IF_RELEASE(efreet_tag_menu);
559 efreet_xml_shutdown();
560 eina_stringshare_shutdown();
564 * @param name The internal name of the menu
565 * @return Returns the Efreet_Menu on success or
567 * @brief Creates a new menu
570 efreet_menu_new(const char *name)
576 printf("Error creating a new menu, name is missing\n");
579 menu = efreet_menu_entry_new();
580 menu->type = EFREET_MENU_ENTRY_MENU;
581 menu->name = eina_stringshare_add(name);
586 efreet_menu_file_set(const char *file)
588 IF_FREE(efreet_menu_file);
589 efreet_menu_file = NULL;
590 if (file) efreet_menu_file = strdup(file);
594 * @return Returns the Efreet_Menu_Internal representation of the default menu or
596 * @brief Creates the default menu representation
599 efreet_menu_get(void)
603 Eina_List *config_dirs, *l;
605 /* check the users config directory first */
606 snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
607 efreet_config_home_get(), efreet_menu_prefix_get());
608 if (ecore_file_exists(menu))
609 return efreet_menu_parse(menu);
612 if (efreet_menu_file)
614 if (ecore_file_exists(efreet_menu_file))
615 return efreet_menu_parse(efreet_menu_file);
619 /* fallback to the XDG_CONFIG_DIRS */
620 config_dirs = efreet_config_dirs_get();
621 EINA_LIST_FOREACH(config_dirs, l, dir)
623 snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
624 dir, efreet_menu_prefix_get());
625 if (ecore_file_exists(menu))
626 return efreet_menu_parse(menu);
633 * @param path: The path of the menu to load
634 * @return Returns the Efreet_Menu_Internal representation on success or NULL on
636 * @brief Parses the given .menu file and creates the menu representation
639 efreet_menu_parse(const char *path)
642 Efreet_Menu_Internal *internal = NULL;
643 Efreet_Menu *entry = NULL;
644 Eina_List *search_dirs;
646 xml = efreet_xml_new(path);
647 if (!xml) return NULL;
649 /* make sure we've got a <Menu> to start with */
650 if (xml->tag != efreet_tag_menu)
652 printf("Menu file didn't start with <Menu> tag.\n");
657 IF_FREE_HASH(efreet_merged_menus);
658 efreet_merged_menus = eina_hash_string_superfast_new(NULL);
660 IF_FREE_HASH(efreet_merged_dirs);
661 efreet_merged_dirs = eina_hash_string_superfast_new(NULL);
663 /* split appart the filename and the path */
664 internal = efreet_menu_internal_new();
666 /* Set default values */
667 internal->show_empty = 0;
668 internal->in_line = 0;
669 internal->inline_limit = 4;
670 internal->inline_header = 1;
671 internal->inline_alias = 0;
673 efreet_menu_path_set(internal, path);
674 if (!efreet_menu_handle_menu(internal, xml))
677 efreet_menu_internal_free(internal);
682 efreet_menu_resolve_moves(internal);
684 if (!efreet_menu_process_dirs(internal))
686 efreet_menu_internal_free(internal);
690 /* handle all .desktops */
691 if (!efreet_menu_process(internal, 0))
693 efreet_menu_internal_free(internal);
697 /* handle menus with only unallocated .desktops */
698 if (!efreet_menu_process(internal, 1))
700 efreet_menu_internal_free(internal);
705 entry = efreet_menu_layout_menu(internal);
706 efreet_menu_internal_free(internal);
711 * @param menu: The menu to work with
712 * @param path: The path where the menu should be saved
713 * @return Returns 1 on success, 0 on failure
714 * @brief Saves the menu to file
717 efreet_menu_save(Efreet_Menu *menu, const char *path)
722 f = fopen(path, "w");
724 fprintf(f, "<?xml version=\"1.0\"?>\n");
725 fprintf(f, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\" "
726 "\"http://standards.freedesktop.org/menu-spec/menu-1.0.dtd\">\n");
727 ret = efreet_menu_save_menu(menu, f, 0);
733 efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent)
737 efreet_menu_save_indent(f, indent);
738 fprintf(f, "<Menu>\n");
741 efreet_menu_save_indent(f, indent + 1);
742 fprintf(f, "<Name>%s</Name>\n", menu->name);
747 /* Only save these for the root element */
748 efreet_menu_save_indent(f, indent + 1);
749 fprintf(f, "<DefaultAppDirs/>\n");
750 efreet_menu_save_indent(f, indent + 1);
751 fprintf(f, "<DefaultDirectoryDirs/>\n");
756 efreet_menu_save_indent(f, indent + 1);
757 fprintf(f, "<Directory>%s</Directory>\n", menu->desktop->orig_path);
763 int has_desktop = 0, has_menu = 0;
765 efreet_menu_save_indent(f, indent + 1);
766 fprintf(f, "<Layout>\n");
767 EINA_LIST_FOREACH(menu->entries, l, entry)
769 if (entry->type == EFREET_MENU_ENTRY_MENU)
771 efreet_menu_save_indent(f, indent + 2);
772 fprintf(f, "<Menuname>%s</Menuname>\n", entry->id);
775 else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
777 efreet_menu_save_indent(f, indent + 2);
778 fprintf(f, "<Filename>%s</Filename>\n", entry->id);
781 else if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
783 efreet_menu_save_indent(f, indent + 2);
784 fprintf(f, "<Separator/>\n");
787 efreet_menu_save_indent(f, indent + 1);
788 fprintf(f, "</Layout>\n");
792 efreet_menu_save_indent(f, indent + 1);
793 fprintf(f, "<Include>\n");
794 EINA_LIST_FOREACH(menu->entries, l, entry)
796 if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
798 efreet_menu_save_indent(f, indent + 2);
799 fprintf(f, "<Filename>%s</Filename>\n", entry->id);
802 efreet_menu_save_indent(f, indent + 1);
803 fprintf(f, "</Include>\n");
808 EINA_LIST_FOREACH(menu->entries, l, entry)
810 if (entry->type == EFREET_MENU_ENTRY_MENU)
811 efreet_menu_save_menu(entry, f, indent + 1);
815 efreet_menu_save_indent(f, indent);
816 fprintf(f, "</Menu>\n");
821 efreet_menu_save_indent(FILE *f, int indent)
825 for (i = 0; i < indent; i++)
831 * @param menu: The menu to work with
832 * @param desktop: The desktop to insert
833 * @param pos: The position to place the new desktop
834 * @return Returns 1 on success, 0 on failure
835 * @brief Insert a desktop element in a menu structure. Only accepts desktop files
836 * in default directories.
839 efreet_menu_desktop_insert(Efreet_Menu *menu, Efreet_Desktop *desktop, int pos)
844 if (!desktop || !menu) return 0;
845 id = efreet_util_path_to_file_id(desktop->orig_path);
848 entry = efreet_menu_entry_new();
849 entry->type = EFREET_MENU_ENTRY_DESKTOP;
850 entry->id = eina_stringshare_add(id);
851 entry->name = eina_stringshare_add(desktop->name);
852 if (desktop->icon) entry->icon = eina_stringshare_add(desktop->icon);
853 efreet_desktop_ref(desktop);
854 entry->desktop = desktop;
856 if (pos < 0 || pos >= eina_list_count(menu->entries))
857 menu->entries = eina_list_append(menu->entries, entry);
860 menu->entries = eina_list_append_relative(menu->entries, entry,
861 eina_list_nth(menu->entries, pos));
867 * @param menu: The menu to work with
868 * @param desktop: The desktop to remove
869 * @return Returns 1 on success, 0 on failure
870 * @brief Remove a desktop element in a menu structure. Only accepts desktop files
871 * in default directories.
874 efreet_menu_desktop_remove(Efreet_Menu *menu, Efreet_Desktop *desktop)
878 if (!desktop || !menu) return 0;
880 entry = eina_list_search_unsorted(menu->entries,
881 EINA_COMPARE_CB(efreet_menu_cb_entry_compare_desktop),
885 menu->entries = eina_list_remove(menu->entries, entry);
886 efreet_menu_free(entry);
893 * @param menu: The menu to work with
894 * @param menu: The menu to work with
895 * @param indent: The indent level to print the menu at
896 * @return Returns no value
897 * @brief Dumps the contents of the menu to the command line
900 efreet_menu_dump(Efreet_Menu *menu, const char *indent)
904 printf("%s%s: ", indent, menu->name);
905 printf("%s\n", (menu->icon ? menu->icon : "No icon"));
907 /* XXX dump the rest of the menu info */
915 len = strlen(indent) + 3;
916 new_indent = malloc(sizeof(char *) * len);
917 snprintf(new_indent, len, "%s ", indent);
919 EINA_LIST_FOREACH(menu->entries, l, entry)
921 if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
922 printf("%s|---\n", new_indent);
923 else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
924 printf("%s|-%s\n", new_indent, entry->name);
925 else if (entry->type == EFREET_MENU_ENTRY_MENU)
926 efreet_menu_dump(entry, new_indent);
927 else if (entry->type == EFREET_MENU_ENTRY_HEADER)
928 printf("%s|---%s\n", new_indent, entry->name);
936 * @param user_dir: The user directory to work with
937 * @param system_dirs: The system directories to work with
938 * @param suffix: The path suffix to add
939 * @return Returns the list of directories
940 * @brief Creates the list of directories based on the user
941 * dir, system dirs and given suffix.
944 efreet_default_dirs_get(const char *user_dir, Eina_List *system_dirs,
949 Eina_List *list = NULL;
952 snprintf(dir, sizeof(dir), "%s/%s", user_dir, suffix);
953 list = eina_list_append(list, strdup(dir));
955 EINA_LIST_FOREACH(system_dirs, l, xdg_dir)
957 snprintf(dir, sizeof(dir), "%s/%s", xdg_dir, suffix);
958 list = eina_list_append(list, strdup(dir));
966 * @return Returns a new Efreet_Menu_Internal struct
967 * @brief Allocates and initializes a new Efreet_Menu_Internal structure
969 static Efreet_Menu_Internal *
970 efreet_menu_internal_new(void)
972 Efreet_Menu_Internal *internal;
974 internal = NEW(Efreet_Menu_Internal, 1);
975 internal->show_empty = -1;
976 internal->in_line = -1;
977 internal->inline_limit = -1;
978 internal->inline_header = -1;
979 internal->inline_alias = -1;
985 * @param menu: The menu to free
986 * @return Returns no value
987 * @brief Frees up the given menu structure
990 efreet_menu_internal_free(Efreet_Menu_Internal *internal)
994 if (!internal) return;
996 IF_FREE(internal->file.path);
997 IF_FREE(internal->file.name);
999 IF_RELEASE(internal->name.internal);
1000 internal->name.name = NULL;
1002 internal->applications = eina_list_free(internal->applications);
1004 IF_FREE_LIST(internal->directories, free);
1005 IF_FREE_LIST(internal->app_dirs, efreet_menu_app_dir_free);
1006 IF_FREE_LIST(internal->app_pool, efreet_menu_desktop_free);
1007 IF_FREE_LIST(internal->directory_dirs, free);
1008 IF_FREE_HASH(internal->directory_cache);
1010 IF_FREE_LIST(internal->moves, efreet_menu_move_free);
1011 IF_FREE_LIST(internal->filters, efreet_menu_filter_free);
1013 IF_FREE_LIST(internal->sub_menus, efreet_menu_internal_free);
1015 IF_FREE_LIST(internal->layout, efreet_menu_layout_free);
1016 IF_FREE_LIST(internal->default_layout, efreet_menu_layout_free);
1023 * @return Returns the XDG_MENU_PREFIX env variable or "" if none set
1024 * @brief Retrieves the XDG_MENU_PREFIX or "" if not set.
1027 efreet_menu_prefix_get(void)
1031 if (efreet_menu_prefix) return efreet_menu_prefix;
1033 prefix = getenv("XDG_MENU_PREFIX");
1034 if (prefix) efreet_menu_prefix = strdup(prefix);
1035 else efreet_menu_prefix = strdup("");
1037 return efreet_menu_prefix;
1042 * @param menu: The menu to populate
1043 * @param xml: The xml dom tree to populate from
1044 * @return Returns 1 if this XML tree is valid, 0 otherwise
1045 * @brief Populates the given menu from the given xml structure
1047 * We walk the Menu children backwards. The reason for this is so that we
1048 * can deal with all the things that make us select the 'last' element
1049 * (MergeFile, Directory, etc). We'll see the last one first and can deal
1050 * with it right away.
1053 efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml)
1057 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
1059 EINA_LIST_REVERSE_FOREACH(xml->children, l, child)
1061 cb = eina_hash_find(efreet_menu_handle_cbs, child->tag);
1064 if (!cb(internal, child))
1069 printf("Unknown XML tag: %s\n", child->tag);
1078 * @param parent: The parent Menu
1079 * @param xml: The xml that defines the menu
1080 * @return Returns 1 on success or 0 on failure
1081 * @brief Handles the sub-menu nodes of the XML file
1084 efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1086 Efreet_Menu_Internal *internal, *match;
1088 efreet_menu_create_sub_menu_list(parent);
1090 internal = efreet_menu_internal_new();
1091 internal->file.path = strdup(parent->file.path);
1092 if (!efreet_menu_handle_menu(internal, xml))
1094 efreet_menu_internal_free(internal);
1098 /* if this menu already exists we just take this one and stick it on the
1099 * start of the existing one */
1100 if ((match = eina_list_search_unsorted(parent->sub_menus,
1101 EINA_COMPARE_CB(efreet_menu_cb_menu_compare),
1105 efreet_menu_concatenate(match, internal);
1106 efreet_menu_internal_free(internal);
1109 parent->sub_menus = eina_list_prepend(parent->sub_menus, internal);
1116 * @param parent: The parent menu
1117 * @param xml: The xml tree
1118 * @return Returns 1 on success or 0 on failure
1119 * @brief Handles the AppDir tag
1122 efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1125 Efreet_Menu_App_Dir *app_dir;
1127 if (!parent || !xml) return 0;
1129 efreet_menu_create_app_dirs_list(parent);
1130 path = efreet_menu_path_get(parent, xml->text);
1131 if (!path) return 0;
1133 /* we've already got this guy in our list we can skip it */
1134 if (eina_list_search_unsorted(parent->app_dirs,
1135 EINA_COMPARE_CB(efreet_menu_cb_app_dirs_compare),
1142 app_dir = efreet_menu_app_dir_new();
1143 app_dir->path = path;
1145 parent->app_dirs = eina_list_prepend(parent->app_dirs, app_dir);
1152 * @param parent: The parent menu
1153 * @param xml: UNUSED
1154 * @return Returns 1 on success or 0 on failure
1155 * @brief Handles the DefaultAppDirs
1158 efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1160 Eina_List *prepend = NULL;
1164 if (!parent) return 0;
1166 efreet_menu_create_app_dirs_list(parent);
1167 dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
1169 EINA_LIST_FREE(dirs, dir)
1171 Efreet_Menu_App_Dir *app_dir;
1173 if (eina_list_search_unsorted(parent->app_dirs,
1174 EINA_COMPARE_CB(efreet_menu_cb_app_dirs_compare),
1178 app_dir = efreet_menu_app_dir_new();
1179 app_dir->path = strdup(dir);
1181 prepend = eina_list_append(prepend, app_dir);
1185 parent->app_dirs = eina_list_merge(prepend, parent->app_dirs);
1192 * @param parent: The parent menu
1193 * @param xml: The xml tree
1194 * @return Returns 1 on success or 0 on failure
1195 * @brief Handles the DirectoryDir tag
1198 efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1202 if (!parent || !xml) return 0;
1204 efreet_menu_create_directory_dirs_list(parent);
1205 path = efreet_menu_path_get(parent, xml->text);
1206 if (!path) return 0;
1208 /* we've already got this guy in our list we can skip it */
1209 if (eina_list_search_unsorted(parent->directory_dirs, EINA_COMPARE_CB(strcmp), path))
1215 parent->directory_dirs = eina_list_prepend(parent->directory_dirs, path);
1222 * @param parent: The parent menu
1223 * @param xml: UNUSED
1224 * @return Returns 1 on success or 0 on failure
1225 * @brief Handles the DefaultDirectoryDirs tag
1228 efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1230 Eina_List *dirs, *l;
1233 if (!parent) return 0;
1235 efreet_menu_create_directory_dirs_list(parent);
1236 dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
1237 "desktop-directories");
1238 EINA_LIST_FOREACH(dirs, l, dir)
1240 if (eina_list_search_unsorted(parent->directory_dirs, EINA_COMPARE_CB(strcmp), dir))
1243 parent->directory_dirs = eina_list_prepend(parent->directory_dirs, strdup(dir));
1248 free(eina_list_data_get(dirs));
1249 dirs = eina_list_remove_list(dirs, dirs);
1257 * @param parent: The parent Menu
1258 * @param xml: The xml to work with
1259 * @return Returns 1 on success or 0 on failure
1260 * @brief Sets the menu name from the given XML fragment.
1263 efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1265 /* not allowed to have two Name settings in a menu */
1266 if (parent->name.internal)
1268 printf("efreet_menu_handle_name() setting second name into menu\n");
1272 /* ignore the name if it contains a / */
1273 if (strchr(xml->text, '/')) return 1;
1275 parent->name.internal = eina_stringshare_add(xml->text);
1282 * @param parent: The parent menu
1283 * @param xml: The xml tree
1284 * @return Returns 1 on success or 0 on failure
1285 * @brief Handles the Directory tag
1287 * This just adds the given directory path to a list which we'll walk once
1288 * we've traversed the entire menu into memory.
1291 efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1293 if (!parent || !xml) return 0;
1295 efreet_menu_create_directories_list(parent);
1296 parent->directories = eina_list_prepend(parent->directories, strdup(xml->text));
1303 * @param parent: The parent menu
1304 * @param xml: The xml tree
1305 * @return Returns 1 on success or 0 on failure
1306 * @brief Handles the OnlyUnallocated tag
1309 efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1311 if (!parent || !xml) return 0;
1313 /* a later instance has been seen so we can ignore this one */
1314 if (parent->seen_allocated) return 1;
1316 parent->seen_allocated = 1;
1317 parent->only_unallocated = 1;
1324 * @param parent: The parent menu
1325 * @param xml: The xml tree
1326 * @return Returns 1 on success or 0 on failure
1327 * @brief Handles the NotOnlyUnallocated tag
1330 efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1332 if (!parent || !xml) return 0;
1334 /* a later instance has been seen so we can ignore this one */
1335 if (parent->seen_allocated) return 1;
1337 parent->seen_allocated = 1;
1338 parent->only_unallocated = 0;
1345 * @param parent: The parent menu
1346 * @param xml: The xml tree
1347 * @return Returns 1 on success or 0 on failure
1348 * @brief Handles the Deleted tag
1351 efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1353 if (!parent || !xml) return 0;
1355 /* a later instance has been seen so we can ignore this one */
1356 if (parent->seen_deleted) return 1;
1358 parent->seen_deleted = 1;
1359 parent->deleted = 1;
1366 * @param parent: The parent menu
1367 * @param xml: The xml tree
1368 * @return Returns 1 on success or 0 on failure
1369 * @brief Handles the NotDeleted tag
1372 efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1374 if (!parent || !xml) return 0;
1376 /* a later instance has been seen so we can ignore this one */
1377 if (parent->seen_deleted) return 1;
1379 parent->seen_deleted = 1;
1380 parent->deleted = 0;
1387 * @param parent: The parent menu
1388 * @param xml: The XML tree to work with
1389 * @return Returns 1 on success or 0 on failure
1390 * @brief Handles parsing the Include tag and all subtags
1393 efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1395 return efreet_menu_handle_filter(parent, xml,
1396 EFREET_MENU_FILTER_INCLUDE);
1401 * @param parent: The parent menu
1402 * @param xml: The xml tree
1403 * @return Returns 1 on success or 0 on failure
1404 * @brief Handles the Exclude tag and all subtags
1407 efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1409 return efreet_menu_handle_filter(parent, xml,
1410 EFREET_MENU_FILTER_EXCLUDE);
1415 * @param op: The filter operation
1416 * @param xml: The xml tree
1417 * @return Returns 1 on success or 0 on failure
1418 * @brief Handles the Filename tag
1421 efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1423 if (!op || !xml) return 0;
1425 op->filenames = eina_list_append(op->filenames, strdup(xml->text));
1432 * @param op: The filter operation
1433 * @param xml: The xml tree
1434 * @return Returns 1 on success or 0 on failure
1435 * @brief Handles the Category tag
1438 efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1440 if (!op || !xml) return 0;
1443 op->categories = eina_list_append(op->categories, strdup(xml->text));
1450 * @param op: The filter operation
1451 * @param xml: The xml tree
1452 * @return Returns 1 on success or 0 on failure
1453 * @brief Handles the All tag and all subtags
1456 efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1458 if (!op || !xml) return 0;
1467 * @param op: The filter operation
1468 * @param xml: The xml tree
1469 * @return Returns 1 on success or 0 on failure
1470 * @brief Handles the And tag and all subtags
1473 efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1475 if (!op || !xml) return 0;
1477 return efreet_menu_handle_filter_child_op(op, xml,
1478 EFREET_MENU_FILTER_OP_AND);
1483 * @param op: The filter operation
1484 * @param xml: The xml tree
1485 * @return Returns 1 on success or 0 on failure
1486 * @brief Handles the Or tag and all subtags
1489 efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1491 if (!op || !xml) return 0;
1493 return efreet_menu_handle_filter_child_op(op, xml,
1494 EFREET_MENU_FILTER_OP_OR);
1499 * @param op: The filter operation
1500 * @param xml: The xml tree
1501 * @return Returns 1 on success or 0 on failure
1502 * @brief Handles the Not tag and all subtags
1505 efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1507 if (!op || !xml) return 0;
1509 return efreet_menu_handle_filter_child_op(op, xml,
1510 EFREET_MENU_FILTER_OP_NOT);
1515 * @param parent: The parent menu
1516 * @param xml: The xml tree
1517 * @return Returns 1 on success or 0 on failure
1518 * @brief Handles the MergeFile tag
1521 efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1525 const char *attr = NULL;
1529 if (!parent || !xml) return 0;
1531 /* check to see if this is a path or parent type */
1532 attr = efreet_xml_attribute_get(xml, "type");
1533 if (attr && !strcmp(attr, "parent"))
1536 /* we're given a path */
1538 path = efreet_menu_path_get(parent, xml->text);
1540 /* need to find the next menu with the same name as ours in the config
1541 * dir after ours (if we're in a config dir) */
1544 Eina_List *search_dirs;
1545 const char *dir, *p;
1548 if (!parent->file.path)
1550 printf("efreet_menu_handle_merge_file() missing menu path ...\n");
1554 search_dirs = efreet_config_dirs_get();
1556 /* we need to find the next menu with the same name in the directory
1557 * after the on the the menu was found in. to do that we first check
1558 * if it's in the config_home_directory() if so we need to search
1559 * all of the dirs. If it isn't in the config home directory then we
1560 * scan the search dirs and look for it. The search_dirs list will
1561 * be left at the next pointer so we can start looking for the menu
1562 * from that point */
1564 dir = efreet_config_home_get();
1566 if (strncmp(dir, parent->file.path, len))
1568 EINA_LIST_FOREACH(search_dirs, l, dir)
1570 if (!strncmp(dir, parent->file.path, len))
1577 printf("efreet_menu_handle_merge_file() failed to find "
1578 "menu parent directory\n");
1582 /* the parent file path may have more path then just the base
1583 * directory so we need to append that as well */
1584 p = parent->file.path + len;
1586 /* whatever dirs are left in the search dir we need to look for the
1587 * menu with the same relative filename */
1588 EINA_LIST_FOREACH(search_dirs, l, dir)
1590 char file[PATH_MAX];
1592 snprintf(file, sizeof(file), "%s/%s/%s", dir, (p ? p : ""),
1594 if (ecore_file_exists(file))
1596 path = strdup(file);
1602 /* nothing to do if no file found */
1603 if (!path) return 1;
1605 if (!efreet_menu_merge(parent, xml, path))
1615 * @param parent: The parent menu to merge into
1616 * @param xml: The XML to be merged
1617 * @param path: The path to the .menu file to merge
1620 efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
1622 Efreet_Xml *merge_xml;
1623 Efreet_Menu_Internal *internal;
1626 if (!parent || !xml || !path) return 0;
1628 /* do nothing if the file doesn't exist */
1629 if (!ecore_file_exists(path)) return 1;
1631 realpath = ecore_file_realpath(path);
1632 if (realpath[0] == '\0')
1634 printf("efreet_menu_merge() unable to get real path for %s\n", path);
1638 /* don't merge the same path twice */
1639 if (eina_hash_find(efreet_merged_menus, realpath))
1642 eina_hash_add(efreet_merged_menus, realpath, (void *)1);
1644 merge_xml = efreet_xml_new(realpath);
1649 printf("efreet_menu_merge() failed to read in the "
1650 "merge file (%s)\n", realpath);
1654 internal = efreet_menu_internal_new();
1655 efreet_menu_path_set(internal, path);
1656 efreet_menu_handle_menu(internal, merge_xml);
1657 efreet_menu_concatenate(parent, internal);
1658 efreet_menu_internal_free(internal);
1660 efreet_xml_del(merge_xml);
1667 * @param parent: The parent menu
1668 * @param xml: The xml tree
1669 * @return Returns 1 on success or 0 on failure
1670 * @brief Handles the MergeDir tag
1673 efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1678 if (!parent || !xml || !xml->text) return 0;
1680 path = efreet_menu_path_get(parent, xml->text);
1681 if (!path) return 1;
1682 if (!ecore_file_exists(path))
1688 ret = efreet_menu_merge_dir(parent, xml, path);
1696 * @param parent: the parent menu of the merge
1697 * @param xml: The xml tree
1698 * @param path: The path to the merge directory
1699 * @return Returns 1 on success or 0 on failure
1700 * @brief Find all of the .menu files in the given directory and merge them
1701 * into the @a parent menu.
1704 efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
1706 char dir_path[PATH_MAX];
1708 struct dirent *file;
1710 if (!parent || !xml || !path) return 0;
1712 /* check to see if we've merged this directory already */
1713 if (eina_hash_find(efreet_merged_dirs, path)) return 1;
1714 eina_hash_add(efreet_merged_dirs, path, (void *)1);
1716 files = opendir(path);
1717 if (!files) return 1;
1719 while ((file = readdir(files)))
1723 if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
1724 p = strrchr(file->d_name, '.');
1726 if (strcmp(p, ".menu")) continue;
1728 snprintf(dir_path, PATH_MAX, "%s/%s", path, file->d_name);
1729 if (!efreet_menu_merge(parent, xml, dir_path))
1742 * @param parent: The parent menu
1743 * @param xml: The xml tree
1744 * @return Returns 1 on success or 0 on failure
1745 * @brief Handles the DefaultMergeDirs tag
1748 efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1751 char path[PATH_MAX], *p;
1754 if (!parent || !xml) return 0;
1756 prefix = efreet_menu_prefix_get();
1757 if (!strcmp(prefix, "gnome-") &&
1758 (!strcmp(parent->file.name, "gnome-applications.menu")))
1759 p = strdup("applications");
1761 else if ((!strcmp(prefix, "kde-") &&
1762 (!strcmp(parent->file.name, "kde-applications.menu"))))
1763 p = strdup("applications");
1769 p = strdup(parent->file.name);
1770 s = strrchr(p, '.');
1773 snprintf(path, sizeof(path), "menus/%s-merged", p);
1776 dirs = efreet_default_dirs_get(efreet_config_home_get(),
1777 efreet_config_dirs_get(), path);
1779 while ((p = eina_list_data_get(dirs)))
1781 dirs = eina_list_remove_list(dirs, dirs);
1782 efreet_menu_merge_dir(parent, xml, p);
1791 * @param parent: The parent menu
1792 * @param xml: The xml tree
1793 * @return Returns 1 on success or 0 on failure
1794 * @brief Handles the LegacyDir tag
1797 efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1799 Efreet_Menu_Internal *legacy;
1801 if (!parent || !xml) return 0;
1803 legacy = efreet_menu_handle_legacy_dir_helper(NULL, parent, xml->text,
1804 efreet_xml_attribute_get(xml, "prefix"));
1805 efreet_menu_concatenate(parent, legacy);
1806 efreet_menu_internal_free(legacy);
1814 * @param parent: The parent menu
1815 * @param legacy_dir: The legacy directory path
1816 * @param prefix: The legacy directory prefix if one set
1817 * @return Returns the Efreet_Menu_Internal representing the legacy hierarchy
1818 * @brief Handles the process of merging @a legacy_dir into @a parent menu
1820 static Efreet_Menu_Internal *
1821 efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root,
1822 Efreet_Menu_Internal *parent,
1823 const char *legacy_dir,
1826 char *path, file_path[PATH_MAX];
1827 Efreet_Menu_Internal *legacy_internal;
1828 Efreet_Menu_Filter *filter;
1829 Efreet_Menu_App_Dir *app_dir;
1830 int path_len, count = 0;
1832 struct dirent *file;
1834 if (!parent || !legacy_dir) return 0;
1836 path = efreet_menu_path_get(parent, legacy_dir);
1838 /* nothing to do if the legacy path doesn't exist */
1839 if (!ecore_file_exists(path))
1845 legacy_internal = efreet_menu_internal_new();
1846 legacy_internal->name.internal = eina_stringshare_add(ecore_file_file_get(path));
1848 /* add the legacy dir as an app dir */
1849 app_dir = efreet_menu_app_dir_new();
1850 app_dir->path = strdup(path);
1851 app_dir->legacy = 1;
1852 if (prefix && !strchr(prefix, '/')) app_dir->prefix = strdup(prefix);
1854 efreet_menu_create_app_dirs_list(legacy_internal);
1855 legacy_internal->app_dirs = eina_list_append(legacy_internal->app_dirs, app_dir);
1859 /* XXX This seems wrong, but it makes efreet pass the fdo tests */
1860 app_dir = efreet_menu_app_dir_new();
1861 app_dir->path = strdup(path);
1862 app_dir->legacy = 1;
1863 if (prefix && !strchr(prefix, '/')) app_dir->prefix = strdup(prefix);
1864 root->app_dirs = eina_list_append(root->app_dirs, app_dir);
1868 /* add the legacy dir as a directory dir */
1869 efreet_menu_create_directory_dirs_list(legacy_internal);
1870 legacy_internal->directory_dirs = eina_list_append(legacy_internal->directory_dirs, strdup(path));
1872 /* setup a filter for all the conforming .desktop files in the legacy
1874 filter = efreet_menu_filter_new();
1875 filter->type = EFREET_MENU_FILTER_INCLUDE;
1877 filter->op->type = EFREET_MENU_FILTER_OP_OR;
1879 efreet_menu_create_filter_list(legacy_internal);
1880 legacy_internal->filters = eina_list_append(legacy_internal->filters, filter);
1882 path_len = strlen(path);
1883 files = opendir(path);
1884 while ((file = readdir(files)))
1886 Efreet_Desktop *desktop = NULL;
1890 if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
1891 file_path[0] = '\0';
1892 ecore_strlcpy(file_path, path, PATH_MAX);
1893 ecore_strlcpy(file_path + path_len, "/", PATH_MAX - path_len);
1894 ecore_strlcpy(file_path + path_len + 1, file->d_name, PATH_MAX - path_len - 1);
1896 /* recurse into sub directories */
1897 if (ecore_file_is_dir(file_path))
1899 Efreet_Menu_Internal *ret;
1901 ret = efreet_menu_handle_legacy_dir_helper(root ? root : legacy_internal,
1902 legacy_internal, file_path, prefix);
1905 efreet_menu_internal_free(legacy_internal);
1911 efreet_menu_create_sub_menu_list(legacy_internal);
1912 legacy_internal->sub_menus = eina_list_prepend(legacy_internal->sub_menus, ret);
1917 if (!strcmp(file->d_name, ".directory"))
1919 legacy_internal->directory = efreet_desktop_get(file_path);
1920 if (legacy_internal->directory
1921 && legacy_internal->directory->type != EFREET_DESKTOP_TYPE_DIRECTORY)
1923 efreet_desktop_free(legacy_internal->directory);
1924 legacy_internal->directory = NULL;
1929 exten = strrchr(file->d_name, '.');
1931 if (exten && !strcmp(exten, ".desktop"))
1932 desktop = efreet_desktop_get(file_path);
1934 if (!desktop) continue;
1936 /* if the .desktop has categories it isn't legacy */
1937 if (efreet_desktop_category_count_get(desktop) != 0)
1940 /* XXX: This will disappear when the .desktop is free'd */
1941 efreet_desktop_category_add(desktop, "Legacy");
1945 snprintf(buf, PATH_MAX, "%s%s", prefix, file->d_name);
1946 filter->op->filenames = eina_list_append(filter->op->filenames, strdup(buf));
1949 filter->op->filenames = eina_list_append(filter->op->filenames, strdup(file->d_name));
1952 efreet_desktop_free(desktop);
1957 return legacy_internal;
1962 * @param parent: The parent menu
1963 * @param xml: UNUSED
1964 * @return Returns 1 on success or 0 on failure
1965 * @brief Handles the KDELegacyDirs tag
1968 efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1973 if (!parent) return 0;
1975 if (!efreet_menu_kde_legacy_dirs) return 1;
1977 /* XXX if one _helper() call succeeds, we return success. should this be flipped?
1978 * (return fail if on of them failed) */
1979 EINA_LIST_FOREACH(efreet_menu_kde_legacy_dirs, l, dir)
1981 Efreet_Menu_Internal *kde;
1983 kde = efreet_menu_handle_legacy_dir_helper(NULL, parent, dir, "kde");
1986 efreet_menu_concatenate(parent, kde);
1987 efreet_menu_internal_free(kde);
1997 * @param parent: The parent menu
1998 * @param xml: The xml tree
1999 * @return Returns 1 on success or 0 on failure
2000 * @brief Handles the Move tag and all subtags
2003 efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2008 if (!parent || !xml) return 0;
2010 efreet_menu_create_move_list(parent);
2012 EINA_LIST_FOREACH(xml->children, l, child)
2014 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
2016 cb = eina_hash_find(efreet_menu_move_cbs, child->tag);
2019 if (!cb(parent, child))
2024 printf("efreet_menu_handle_move() unknown tag found "
2025 "in Move (%s)\n", child->tag);
2030 parent->current_move = NULL;
2037 * @param parent: The parent menu
2038 * @param xml: The xml tree
2039 * @return Returns 1 on success or 0 on failure
2040 * @brief Handles the Old tag
2043 efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2045 Efreet_Menu_Move *move;
2047 if (!parent || !xml || !xml->text) return 0;
2049 if (parent->current_move)
2051 printf("efreet_menu_handle_old() saw second <Old> "
2052 "before seeing <New>\n");
2056 /* If we already moved this menu, remove the old move */
2057 /* XXX This seems wrong, but it makes efreet pass the fdo tests */
2059 move = eina_list_search_unsorted(parent->moves,
2060 EINA_COMPARE_CB(efreet_menu_cb_move_compare),
2064 efreet_menu_move_free(move);
2065 parent->moves = eina_list_remove(parent->moves, move);
2069 move = efreet_menu_move_new();
2070 move->old_name = strdup(xml->text);
2072 parent->current_move = move;
2073 parent->moves = eina_list_append(parent->moves, move);
2080 * @param parent: The parent menu
2081 * @param xml: The xml tree
2082 * @return Returns 1 on success or 0 on failure
2083 * @brief Handles the New tag
2086 efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2088 if (!parent || !xml || !xml->text) return 0;
2090 if (!parent->current_move)
2092 printf("efreet_menu_handle_new() saw New before seeing Old\n");
2096 parent->current_move->new_name = strdup(xml->text);
2097 parent->current_move = NULL;
2104 * @param parent: The parent menu
2105 * @param xml: The xml tree
2106 * @return Returns 1 on success or 0 on failure
2107 * @brief Handles the Layout tag and all subtags
2110 efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2115 if (!parent || !xml) return 0;
2117 /* We use the last existing layout */
2118 if (parent->layout) return 1;
2120 efreet_menu_create_layout_list(parent);
2122 EINA_LIST_FOREACH(xml->children, l, child)
2124 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
2126 cb = eina_hash_find(efreet_menu_layout_cbs, child->tag);
2129 if (!cb(parent, child, 0))
2134 printf("efreet_menu_handle_move() unknown tag found "
2135 "in Layout (%s)\n", child->tag);
2145 * @param parent: The parent menu
2146 * @param xml: The xml tree
2147 * @return Returns 1 on success or 0 on failure
2148 * @brief Handles the DefaultLayout tag
2151 efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2157 if (!parent || !xml) return 0;
2159 /* We use the last existing layout */
2160 if (parent->default_layout) return 1;
2162 val = efreet_xml_attribute_get(xml, "show_empty");
2163 if (val) parent->show_empty = !strcmp(val, "true");
2165 val = efreet_xml_attribute_get(xml, "inline");
2166 if (val) parent->in_line = !strcmp(val, "true");
2168 val = efreet_xml_attribute_get(xml, "inline_limit");
2169 if (val) parent->inline_limit = atoi(val);
2171 val = efreet_xml_attribute_get(xml, "inline_header");
2172 if (val) parent->inline_header = !strcmp(val, "true");
2174 val = efreet_xml_attribute_get(xml, "inline_alias");
2175 if (val) parent->inline_alias = !strcmp(val, "true");
2177 efreet_menu_create_default_layout_list(parent);
2179 EINA_LIST_FOREACH(xml->children, l, child)
2181 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
2183 cb = eina_hash_find(efreet_menu_layout_cbs, child->tag);
2186 if (!cb(parent, child, 1))
2191 printf("efreet_menu_handle_move() unknown tag found in "
2192 "DefaultLayout (%s)\n", child->tag);
2201 efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2203 Efreet_Menu_Layout *layout;
2206 if (!parent || !xml) return 0;
2210 printf("efreet_menu_handle_layout_menuname() The Menuname tag in "
2211 "layout needs a filename.\n");
2215 layout = efreet_menu_layout_new();
2216 layout->type = EFREET_MENU_LAYOUT_MENUNAME;
2217 layout->name = strdup(xml->text);
2219 val = efreet_xml_attribute_get(xml, "show_empty");
2220 if (val) layout->show_empty = !strcmp(val, "true");
2222 val = efreet_xml_attribute_get(xml, "inline");
2223 if (val) layout->in_line = !strcmp(val, "true");
2225 val = efreet_xml_attribute_get(xml, "inline_limit");
2226 if (val) layout->inline_limit = atoi(val);
2228 val = efreet_xml_attribute_get(xml, "inline_header");
2229 if (val) layout->inline_header = !strcmp(val, "true");
2231 val = efreet_xml_attribute_get(xml, "inline_alias");
2232 if (val) layout->inline_alias = !strcmp(val, "true");
2234 if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2235 else parent->layout = eina_list_append(parent->layout, layout);
2241 efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2243 Efreet_Menu_Layout *layout;
2245 if (!parent || !xml) return 0;
2249 printf("efreet_menu_handle_layout_filename() The Filename tag in "
2250 "layout needs a filename.\n");
2254 layout = efreet_menu_layout_new();
2255 layout->type = EFREET_MENU_LAYOUT_FILENAME;
2256 layout->name = strdup(xml->text);
2258 if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2259 else parent->layout = eina_list_append(parent->layout, layout);
2265 efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2267 Efreet_Menu_Layout *layout;
2269 if (!parent || !xml) return 0;
2271 layout = efreet_menu_layout_new();
2272 layout->type = EFREET_MENU_LAYOUT_SEPARATOR;
2274 parent->default_layout = eina_list_append(parent->default_layout, layout);
2276 parent->layout = eina_list_append(parent->layout, layout);
2281 efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2283 Efreet_Menu_Layout *layout;
2286 if (!parent || !xml) return 0;
2288 attr = efreet_xml_attribute_get(xml, "type");
2291 printf("efreet_menu_handle_layout_merge() The Merge tag in layout "
2292 "needs a type attribute.\n");
2296 if (strcmp(attr, "files") && strcmp(attr, "menus") && strcmp(attr, "all"))
2298 printf("efreet_menu_handle_layout_merge() The type attribute for "
2299 "the Merge tag contains an unknown value (%s).\n", attr);
2303 layout = efreet_menu_layout_new();
2304 layout->type = EFREET_MENU_LAYOUT_MERGE;
2305 layout->name = strdup(attr);
2307 if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2308 else parent->layout = eina_list_append(parent->layout, layout);
2315 * @param parent: The parent menu
2316 * @param xml: The XML tree to parse
2317 * @param type: The type of filter
2318 * @return Returns 1 on success or 0 on failure
2319 * @brief Parses the given XML tree and adds the filter to the parent menu
2322 efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml,
2323 Efreet_Menu_Filter_Type type)
2325 Efreet_Menu_Filter *filter;
2327 efreet_menu_create_filter_list(parent);
2329 /* filters have a default or relationship */
2330 filter = efreet_menu_filter_new();
2331 filter->type = type;
2332 filter->op->type = EFREET_MENU_FILTER_OP_OR;
2334 if (!efreet_menu_handle_filter_op(filter->op, xml))
2336 efreet_menu_filter_free(filter);
2340 parent->filters = eina_list_prepend(parent->filters, filter);
2347 * @param op: The operation to work with
2348 * @param xml: The XML tree representing this operation
2349 * @return Returns 1 on success or 0 on failure
2350 * @brief Parses the given XML tree and populates the operation
2353 efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
2358 EINA_LIST_FOREACH(xml->children, l, child)
2360 int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
2362 cb = eina_hash_find(efreet_menu_filter_cbs, child->tag);
2370 printf("efreet_menu_handle_filter_op() unknown tag in filter (%s)\n", child->tag);
2379 * @return Returns a new Efreet_Menu_Filter on success or NULL on failure
2380 * @brief Creates and initializes an Efreet_Menu_Filter object
2382 static Efreet_Menu_Filter *
2383 efreet_menu_filter_new(void)
2385 Efreet_Menu_Filter *filter;
2387 filter = NEW(Efreet_Menu_Filter, 1);
2388 filter->op = efreet_menu_filter_op_new();
2400 * @param filter: The filter to work with
2401 * @return Returns no data
2402 * @brief Frees the given filter and all data
2405 efreet_menu_filter_free(Efreet_Menu_Filter *filter)
2407 if (!filter) return;
2409 if (filter->op) efreet_menu_filter_op_free(filter->op);
2417 * @return Returns a new Efreet_Menu_Layout on success or NULL on failure
2418 * @brief Creates and initializes an Efreet_Menu_Layout object
2420 static Efreet_Menu_Layout *
2421 efreet_menu_layout_new(void)
2423 Efreet_Menu_Layout *layout;
2425 layout = NEW(Efreet_Menu_Layout, 1);
2426 layout->show_empty = -1;
2427 layout->in_line = -1;
2428 layout->inline_limit = -1;
2429 layout->inline_header = -1;
2430 layout->inline_alias = -1;
2437 * @param filter: The filter to work with
2438 * @return Returns no data
2439 * @brief Frees the given filter and all data
2442 efreet_menu_layout_free(Efreet_Menu_Layout *layout)
2444 if (!layout) return;
2446 IF_FREE(layout->name);
2452 * @return Returns a new Efreet_Menu_Filter_Op on success or NULL on failure
2453 * @brief Creates and initializes an Efreet_Menu_Filter_Op structure
2455 static Efreet_Menu_Filter_Op *
2456 efreet_menu_filter_op_new(void)
2458 Efreet_Menu_Filter_Op *op;
2460 op = NEW(Efreet_Menu_Filter_Op, 1);
2467 * @param op: The operation to work with
2468 * @return Returns no value.
2469 * @brief Frees the given operation and all sub data
2472 efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op)
2476 IF_FREE_LIST(op->categories, free);
2477 IF_FREE_LIST(op->filenames, free);
2478 IF_FREE_LIST(op->filters, efreet_menu_filter_op_free);
2485 * @return Returns a new Efreet_Menu_Desktop on success or NULL on failure
2486 * @brief Creates and returns an Efreet_Menu_Desktop
2488 static Efreet_Menu_Desktop *
2489 efreet_menu_desktop_new(void)
2491 Efreet_Menu_Desktop *md;
2493 md = NEW(Efreet_Menu_Desktop, 1);
2500 * @param md: The Efreet_Menu_Desktop to free
2501 * @return Returns no value
2502 * @brief Frees the given structure
2505 efreet_menu_desktop_free(Efreet_Menu_Desktop *md)
2508 if (md->desktop) efreet_desktop_free(md->desktop);
2514 * @return Returns a new Efreet_Menu on success or NULL on failure
2515 * @brief Creates and returns an Efreet_Menu
2517 static Efreet_Menu *
2518 efreet_menu_entry_new(void)
2522 entry = NEW(Efreet_Menu, 1);
2529 * @param entry: The Efreet_Menu to free
2530 * @return Returns no value
2531 * @brief Frees the given structure
2534 efreet_menu_free(Efreet_Menu *entry)
2536 IF_RELEASE(entry->name);
2537 IF_RELEASE(entry->icon);
2538 entry->entries = eina_list_free(entry->entries);
2539 IF_RELEASE(entry->id);
2540 if (entry->desktop) efreet_desktop_free(entry->desktop);
2546 * @param op: The op to add a child too
2547 * @param xml: The XML tree of the child
2548 * @param type: The type of child to add
2549 * @return Returns 1 on success or 0 on failure
2550 * @brief Parses the given XML tree and populates a new child operation.
2553 efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
2554 Efreet_Menu_Filter_Op_Type type)
2556 Efreet_Menu_Filter_Op *child_op;
2558 child_op = efreet_menu_filter_op_new();
2559 child_op->type = type;
2561 if (!efreet_menu_handle_filter_op(child_op, xml))
2563 efreet_menu_filter_op_free(child_op);
2567 op->filters = eina_list_append(op->filters, child_op);
2574 * @param menu: The menu to work with
2575 * @param only_unallocated: Do we only look for unallocated items?
2576 * @return Returns 1 if we've successfully processed the menu, 0 otherwise
2577 * @brief Handles the processing of the menu data to retrieve the .desktop
2578 * files for the menu
2581 efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2585 /* a menu _MUST_ have a name */
2586 if (!internal->name.internal || (internal->name.internal[0] == '\0'))
2589 /* handle filtering out .desktop files as needed. This deals with all
2591 efreet_menu_process_filters(internal, only_unallocated);
2593 if (internal->sub_menus)
2595 Efreet_Menu_Internal *sub_internal;
2597 EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
2599 sub_internal->parent = internal;
2600 efreet_menu_process(sub_internal, only_unallocated);
2607 /* This will walk through all of the app dirs and load all the .desktop
2608 * files into the cache for the menu. The .desktop files will have their
2609 * allocated flag set to 0 */
2611 efreet_menu_process_dirs(Efreet_Menu_Internal *internal)
2615 /* Scan application directories for .desktop files */
2616 if (!efreet_menu_app_dirs_process(internal))
2619 /* Scan directory directories for .directory file */
2620 if (!efreet_menu_directory_dirs_process(internal))
2623 if (internal->sub_menus)
2625 Efreet_Menu_Internal *sub_internal;
2627 EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
2629 sub_internal->parent = internal;
2630 efreet_menu_process_dirs(sub_internal);
2639 * @param menu: the menu to process
2640 * @param only_unallocated: Only handle menus taht deal with unallocated items
2641 * @return Returns no value
2642 * @brief Handles the processing of the filters attached to the given menu.
2644 * For each include filter we'll add the items to our applications array. Each
2645 * exclude filter will remove items from the applications array
2648 efreet_menu_process_filters(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2650 Efreet_Menu_Filter *filter;
2651 Efreet_Menu_Desktop *md;
2656 /* nothing to do if we're checking the other option */
2657 if (only_unallocated != internal->only_unallocated) return;
2659 while (internal->applications)
2660 internal->applications = eina_list_remove_list(internal->applications,
2661 internal->applications);
2663 if (!internal->filters) return;
2665 EINA_LIST_FOREACH(internal->filters, l, filter)
2667 /* skip excludes until we get an include */
2668 if (!included && (filter->type == EFREET_MENU_FILTER_EXCLUDE))
2672 if (filter->type == EFREET_MENU_FILTER_INCLUDE)
2676 matches = eina_hash_string_superfast_new(NULL);
2677 internal->applications = efreet_menu_process_app_pool(internal->app_pool, internal->applications,
2678 matches, filter, internal->only_unallocated);
2679 if (internal->parent)
2681 Efreet_Menu_Internal *parent;
2683 parent = internal->parent;
2685 internal->applications = efreet_menu_process_app_pool(parent->app_pool,
2686 internal->applications, matches, filter,
2687 internal->only_unallocated);
2688 } while ((parent = parent->parent));
2690 eina_hash_free(matches);
2694 /* check each item in our menu so far and see if it's excluded */
2695 l = internal->applications;
2696 while ((md = eina_list_data_get(l)))
2698 ll = eina_list_next(l);
2699 if (efreet_menu_filter_matches(filter->op, md))
2700 internal->applications = eina_list_remove_list(internal->applications, l);
2706 /* sort the menu applications. we do this in process filters so it will only
2707 * be done once per menu.*/
2708 if (internal->applications)
2712 count = eina_list_count(internal->applications);
2716 Efreet_Menu_Desktop *md;
2718 sheap = ecore_sheap_new(
2719 ECORE_COMPARE_CB(efreet_menu_cb_md_compare), count);
2720 while ((md = eina_list_data_get(internal->applications)))
2722 internal->applications = eina_list_remove_list(internal->applications,
2723 internal->applications);
2724 ecore_sheap_insert(sheap, md);
2726 while ((md = ecore_sheap_extract(sheap)))
2728 if (md->desktop->no_display) continue;
2729 internal->applications = eina_list_append(internal->applications, md);
2732 ecore_sheap_destroy(sheap);
2739 * @param pool: The app pool to iterate
2740 * @param applications: The list of applications to append too
2741 * @param matches: The hash of previously matched ids
2742 * @param filter: The menu filter to run on the pool items
2743 * @param only_unallocated: Do we check only unallocated pool items?
2744 * @return Returns no value.
2745 * @brief This will iterate the items in @a pool and append them to @a
2746 * applications if they match the @a filter given and aren't previoulsy entered
2747 * in @a matches. If @a only_unallocated is set we'll only only at the
2748 * .desktop files that haven't been previoulsy matched
2751 efreet_menu_process_app_pool(Eina_List *pool, Eina_List *applications,
2753 Efreet_Menu_Filter *filter,
2754 unsigned int only_unallocated)
2756 Efreet_Menu_Desktop *md;
2759 if (!pool) return NULL;
2761 EINA_LIST_FOREACH(pool, l, md)
2763 if (eina_hash_find(matches, md->id)) continue;
2764 if (only_unallocated && md->allocated) continue;
2765 if (efreet_menu_filter_matches(filter->op, md))
2767 applications = eina_list_append(applications, md);
2768 eina_hash_add(matches, (void *)md->id, md);
2772 return applications;
2777 * @param op: The filter operation to execute
2778 * @param md: The desktop to run the filter on
2779 * @return Returns 1 if this desktop matches the given filter, 0 otherwise
2780 * @brief This will execute the given @a filter on the given desktop
2783 efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2785 if (op->type == EFREET_MENU_FILTER_OP_OR)
2786 return efreet_menu_filter_or_matches(op, md);
2788 if (op->type == EFREET_MENU_FILTER_OP_AND)
2789 return efreet_menu_filter_and_matches(op, md);
2791 if (op->type == EFREET_MENU_FILTER_OP_NOT)
2792 return efreet_menu_filter_not_matches(op, md);
2799 * @param op: The filter operation to execute
2800 * @param md: The desktop to execute on
2801 * @return Returns 1 if the desktop matches, 0 otherwise
2802 * @brief Executes the OR operation, @a op, on the desktop, @a md.
2805 efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2807 Efreet_Menu_Filter_Op *child;
2811 if (op->all) return 1;
2813 if (op->categories && md->desktop->categories)
2815 EINA_LIST_FOREACH(op->categories, l, t)
2817 if (eina_list_search_unsorted(md->desktop->categories,
2818 EINA_COMPARE_CB(strcmp), t))
2825 EINA_LIST_FOREACH(op->filenames, l, t)
2826 if (!strcmp(t, md->id)) return 1;
2831 EINA_LIST_FOREACH(op->filters, l, child)
2833 if (efreet_menu_filter_matches(child, md))
2843 * @param op: The filter operation to execute
2844 * @param md: The desktop to execute on
2845 * @return Returns 1 if the desktop matches, 0 otherwise
2846 * @brief Executes the AND operation, @a op, on the desktop, @a md.
2849 efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2851 Efreet_Menu_Filter_Op *child;
2857 if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
2860 EINA_LIST_FOREACH(op->categories, l, t)
2862 if (!eina_list_search_unsorted(md->desktop->categories,
2863 EINA_COMPARE_CB(strcmp), t))
2870 EINA_LIST_FOREACH(op->filenames, l, t)
2872 if (strcmp(t, md->id)) return 0;
2878 EINA_LIST_FOREACH(op->filters, l, child)
2880 if (!efreet_menu_filter_matches(child, md))
2890 * @param op: The filter operation to execute
2891 * @param md: The desktop to execute on
2892 * @return Returns 1 if the desktop matches, 0 otherwise
2893 * @brief Executes the NOT operation, @a op, on the desktop, @a md.
2896 efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2898 Efreet_Menu_Filter_Op *child;
2902 /* !all means no desktops match */
2903 if (op->all) return 0;
2907 if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
2910 EINA_LIST_FOREACH(op->categories, l, t)
2912 if (eina_list_search_unsorted(md->desktop->categories,
2913 EINA_COMPARE_CB(strcmp), t))
2920 EINA_LIST_FOREACH(op->filenames, l, t)
2922 if (!strcmp(t, md->id)) return 0;
2928 EINA_LIST_FOREACH(op->filters, l, child)
2930 if (efreet_menu_filter_matches(child, md))
2940 * @param dest: The destination menu
2941 * @param src: The source menu
2942 * @return Returns no value
2943 * @brief Takes the child elements of the menu @a src and puts then on the
2944 * _start_ of the menu @a dest.
2947 efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src)
2949 Efreet_Menu_Internal *submenu;
2951 if (!dest || !src) return;
2953 if (!dest->directory && src->directory)
2955 dest->directory = src->directory;
2956 src->directory = NULL;
2959 if (!dest->seen_allocated && src->seen_allocated)
2961 dest->only_unallocated = src->only_unallocated;
2962 dest->seen_allocated = 1;
2965 if (!dest->seen_deleted && src->seen_deleted)
2967 dest->deleted = src->deleted;
2968 dest->seen_deleted = 1;
2971 if (src->directories)
2973 efreet_menu_create_directories_list(dest);
2974 dest->directories = eina_list_merge(src->directories, dest->directories);
2975 src->directories = NULL;
2980 efreet_menu_create_app_dirs_list(dest);
2981 dest->app_dirs = eina_list_merge(src->app_dirs, dest->app_dirs);
2982 src->app_dirs = NULL;
2985 if (src->directory_dirs)
2987 efreet_menu_create_directory_dirs_list(dest);
2988 dest->directory_dirs = eina_list_merge(src->directory_dirs, dest->directory_dirs);
2989 src->directory_dirs = NULL;
2994 efreet_menu_create_move_list(dest);
2995 dest->moves = eina_list_merge(src->moves, dest->moves);
3001 efreet_menu_create_filter_list(dest);
3002 dest->filters = eina_list_merge(src->filters, dest->filters);
3003 src->filters = NULL;
3008 efreet_menu_create_sub_menu_list(dest);
3010 while ((submenu = eina_list_data_get(eina_list_last(src->sub_menus))))
3012 Efreet_Menu_Internal *match;
3014 src->sub_menus = eina_list_remove(src->sub_menus, submenu);
3015 /* if this menu is in the list already we just add to that */
3016 if ((match = eina_list_search_unsorted(dest->sub_menus,
3017 EINA_COMPARE_CB(efreet_menu_cb_menu_compare),
3020 efreet_menu_concatenate(match, submenu);
3021 efreet_menu_internal_free(submenu);
3024 dest->sub_menus = eina_list_prepend(dest->sub_menus, submenu);
3031 * @param menu: The menu to work with
3032 * @return Returns no value
3033 * @brief Handles any \<Move\> commands in the menus
3036 efreet_menu_resolve_moves(Efreet_Menu_Internal *internal)
3038 Efreet_Menu_Internal *child;
3039 Efreet_Menu_Move *move;
3042 /* child moves are handled before parent moves */
3043 if (internal->sub_menus)
3045 EINA_LIST_FOREACH(internal->sub_menus, l, child)
3046 efreet_menu_resolve_moves(child);
3049 /* nothing to do if this menu has no moves */
3050 if (!internal->moves) return;
3052 EINA_LIST_FOREACH(internal->moves, l, move)
3054 Efreet_Menu_Internal *origin, *dest, *parent;
3056 /* if the origin path doesn't exist we do nothing */
3057 origin = efreet_menu_by_name_find(internal, move->old_name, &parent);
3058 if (!origin) continue;
3060 /* remove the origin menu from the parent */
3061 parent->sub_menus = eina_list_remove(parent->sub_menus, origin);
3063 /* if the destination path doesn't exist we just rename the origin
3064 * menu and append to the parents list of children */
3065 dest = efreet_menu_by_name_find(internal, move->new_name, &parent);
3068 char *path, *tmp, *t;
3070 /* if the dest path has /'s in it then we need to add menus to
3071 * fill out the paths */
3072 t = strdup(move->new_name);
3074 path = strchr(tmp, '/');
3077 Efreet_Menu_Internal *ancestor;
3081 ancestor = efreet_menu_internal_new();
3082 ancestor->name.internal = eina_stringshare_add(tmp);
3084 efreet_menu_create_sub_menu_list(parent);
3085 parent->sub_menus = eina_list_append(parent->sub_menus, ancestor);
3089 path = strchr(tmp, '/');
3092 IF_RELEASE(origin->name.internal);
3093 origin->name.internal = eina_stringshare_add(tmp);
3095 efreet_menu_create_sub_menu_list(parent);
3096 parent->sub_menus = eina_list_append(parent->sub_menus, origin);
3102 efreet_menu_concatenate(dest, origin);
3103 efreet_menu_internal_free(origin);
3106 IF_FREE_LIST(internal->moves, efreet_menu_move_free);
3111 * @param menu: The menu to start searching from
3112 * @param name: The menu name to find
3113 * @param parent: The parent of the found menu
3114 * @return Returns the menu with the given @a name or NULL if none found
3115 * @brief Searches the menu tree starting at @a menu looking for a menu with
3118 static Efreet_Menu_Internal *
3119 efreet_menu_by_name_find(Efreet_Menu_Internal *internal, const char *name, Efreet_Menu_Internal **parent)
3121 char *part, *tmp, *ptr;
3123 if (parent) *parent = internal;
3125 /* find the correct parent menu */
3128 part = strchr(ptr, '/');
3133 if (!(internal = eina_list_search_unsorted(internal->sub_menus,
3134 EINA_COMPARE_CB(efreet_menu_cb_compare_names),
3142 part = strchr(ptr, '/');
3145 if (parent) *parent = internal;
3147 /* find the menu in the parent list */
3148 if (!(internal = eina_list_search_unsorted(internal->sub_menus,
3149 EINA_COMPARE_CB(efreet_menu_cb_compare_names),
3161 efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path)
3166 p = strrchr(tmp, '/');
3172 internal->file.path = strdup(tmp);
3173 internal->file.name = strdup(p);
3180 * @return Returns a new Efreet_Menu_Move struct on success or NULL on failure
3181 * @brief Creates an returns a new Efreet_Menu_Move struct or NULL on failure
3183 static Efreet_Menu_Move *
3184 efreet_menu_move_new(void)
3186 Efreet_Menu_Move *move;
3188 move = NEW(Efreet_Menu_Move, 1);
3195 * @param move: The Efreet_Menu_Move to free
3196 * @return Returns no value.
3197 * @brief Frees the given move structure
3200 efreet_menu_move_free(Efreet_Menu_Move *move)
3204 IF_FREE(move->old_name);
3205 IF_FREE(move->new_name);
3212 * @return Returns a new Efreet_Menu_App_Dir on success or NULL on failure
3213 * @brief Creates and initializes a new Efreet_Menu_App_Dir structure
3215 static Efreet_Menu_App_Dir *
3216 efreet_menu_app_dir_new(void)
3218 Efreet_Menu_App_Dir *dir;
3220 dir = NEW(Efreet_Menu_App_Dir, 1);
3227 * @param dir: The Efreet_Menu_App_Dir to free
3228 * @return Returns no value.
3229 * @brief Frees the given dir structure
3232 efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir)
3237 IF_FREE(dir->prefix);
3243 * @param a: The app dir to compare too
3244 * @param b: The path to compare too
3245 * @return Returns 1 if the strings are equals, 0 otherwise
3246 * @brief Compares the too strings
3249 efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b)
3251 return ecore_str_compare(a->path, b);
3255 efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal)
3257 if (!internal || internal->sub_menus) return;
3259 internal->sub_menus = NULL;
3263 efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal)
3265 if (!internal || internal->app_dirs) return;
3267 internal->app_dirs = NULL;
3271 efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal)
3273 if (!internal || internal->directory_dirs) return;
3275 internal->directory_dirs = NULL;
3279 efreet_menu_create_move_list(Efreet_Menu_Internal *internal)
3281 if (!internal || internal->moves) return;
3283 internal->moves = NULL;
3287 efreet_menu_create_filter_list(Efreet_Menu_Internal *internal)
3289 if (!internal || internal->filters) return;
3291 internal->filters = NULL;
3295 efreet_menu_create_layout_list(Efreet_Menu_Internal *internal)
3297 if (!internal || internal->layout) return;
3299 internal->layout = NULL;
3303 efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal)
3305 if (!internal || internal->default_layout) return;
3307 internal->default_layout = NULL;
3311 efreet_menu_create_directories_list(Efreet_Menu_Internal *internal)
3313 if (!internal || internal->directories) return;
3315 internal->directories = NULL;
3319 efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix)
3321 char path[PATH_MAX];
3324 /* see if we've got an absolute or relative path */
3325 if (suffix[0] == '/')
3326 snprintf(path, sizeof(path), "%s", suffix);
3330 if (!internal->file.path)
3332 printf("efreet_menu_handle_app_dir() missing menu path ...\n");
3335 snprintf(path, sizeof(path), "%s/%s", internal->file.path, suffix);
3339 while (path[len] == '/') path[len--] = '\0';
3341 return strdup(path);
3345 efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b)
3347 return ecore_str_compare(a->name.internal, b->name.internal);
3351 efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal)
3353 Efreet_Menu_App_Dir *app_dir;
3354 Efreet_Menu_Desktop *md;
3357 EINA_LIST_FREE(internal->app_pool, md)
3358 efreet_menu_desktop_free(md);
3360 EINA_LIST_FOREACH(internal->app_dirs, l, app_dir)
3361 efreet_menu_app_dir_scan(internal, app_dir->path, app_dir->prefix, app_dir->legacy);
3367 efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal, const char *path, const char *id, int legacy)
3369 Efreet_Desktop *desktop;
3370 Efreet_Menu_Desktop *menu_desktop;
3372 char buf[PATH_MAX], buf2[PATH_MAX];
3373 struct dirent *file;
3376 files = opendir(path);
3377 if (!files) return 1;
3379 while ((file = readdir(files)))
3381 if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
3382 snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name);
3384 snprintf(buf2, PATH_MAX, "%s-%s", id, file->d_name);
3386 strcpy(buf2, file->d_name);
3388 if (ecore_file_is_dir(buf))
3391 efreet_menu_app_dir_scan(internal, buf, buf2, legacy);
3395 ext = strrchr(buf, '.');
3397 if (!ext || strcmp(ext, ".desktop")) continue;
3398 desktop = efreet_desktop_get(buf);
3400 if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_APPLICATION)
3402 if (desktop) efreet_desktop_free(desktop);
3405 /* Don't add two files with the same id in the app pool */
3406 if (eina_list_search_unsorted(internal->app_pool,
3407 EINA_COMPARE_CB(efreet_menu_cb_md_compare_ids),
3411 menu_desktop = efreet_menu_desktop_new();
3412 menu_desktop->desktop = desktop;
3413 menu_desktop->id = eina_stringshare_add(buf2);
3414 internal->app_pool = eina_list_prepend(internal->app_pool, menu_desktop);
3424 * @param menu: The menu to work with
3425 * @return Returns 1 on success or 0 on failure
3426 * @brief Process the directory dirs in @a menu
3429 efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal)
3434 if (internal->directory_dirs)
3436 internal->directory_cache =
3437 eina_hash_string_superfast_new(EINA_FREE_CB(efreet_desktop_free));
3439 EINA_LIST_REVERSE_FOREACH(internal->directory_dirs, l, path)
3440 efreet_menu_directory_dir_scan(path, NULL, internal->directory_cache);
3443 if (internal->directories)
3445 EINA_LIST_REVERSE_FOREACH(internal->directories, l, path)
3447 internal->directory = efreet_menu_directory_get(internal, path);
3448 if (internal->directory) break;
3451 if (!internal->directory)
3452 internal->name.name = internal->name.internal;
3454 internal->name.name = internal->directory->name;
3461 * @param path: The path to scan
3462 * @param relative_path: The relative portion of the path
3463 * @param cache: The cache to populate
3464 * @return Returns 1 on success or 0 on failure
3465 * @brief Scans the given directory dir for .directory files and adds the
3466 * applications to the cache
3469 efreet_menu_directory_dir_scan(const char *path, const char *relative_path,
3472 Efreet_Desktop *desktop;
3474 char buf[PATH_MAX], buf2[PATH_MAX];
3475 struct dirent *file;
3478 files = opendir(path);
3479 if (!files) return 1;
3481 while ((file = readdir(files)))
3483 if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
3484 snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name);
3486 snprintf(buf2, PATH_MAX, "%s/%s", relative_path, file->d_name);
3488 strcpy(buf2, file->d_name);
3490 if (ecore_file_is_dir(buf))
3491 efreet_menu_directory_dir_scan(buf, buf2, cache);
3495 ext = strrchr(buf, '.');
3496 if (!ext || strcmp(ext, ".directory")) continue;
3498 desktop = efreet_desktop_get(buf);
3499 if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_DIRECTORY)
3501 efreet_desktop_free(desktop);
3505 eina_hash_del(cache, buf2, NULL);
3506 eina_hash_add(cache, buf2, desktop);
3516 * @param menu: The menu to work with
3517 * @param path: The path to work with
3518 * @return Returns the desktop file for this path or NULL if none exists
3519 * @brief Finds the desktop file for the given path.
3521 static Efreet_Desktop *
3522 efreet_menu_directory_get(Efreet_Menu_Internal *internal, const char *path)
3524 Efreet_Desktop *dir;
3526 if (internal->directory_cache)
3528 dir = eina_hash_find(internal->directory_cache, path);
3529 if (dir) return dir;
3532 if (internal->parent)
3533 return efreet_menu_directory_get(internal->parent, path);
3540 * @param a: The first desktop
3541 * @param b: The second desktop
3542 * @return Returns the comparison of the desktop files
3543 * @brief Compares the desktop files.
3546 efreet_menu_cb_md_compare(Efreet_Menu_Desktop *a, Efreet_Menu_Desktop *b)
3549 return strcmp(ecore_file_file_get(a->desktop->orig_path), ecore_file_file_get(b->desktop->orig_path));
3551 return strcasecmp(a->desktop->name, b->desktop->name);
3556 efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name)
3558 return strcmp(internal->name.internal, name);
3562 efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name)
3564 return strcmp(md->id, name);
3567 static Efreet_Menu *
3568 efreet_menu_layout_menu(Efreet_Menu_Internal *internal)
3571 Eina_List *layout = NULL;
3574 if (internal->parent)
3576 /* Copy default layout rules */
3577 if (internal->show_empty == -1) internal->show_empty = internal->parent->show_empty;
3578 if (internal->in_line == -1) internal->in_line = internal->parent->in_line;
3579 if (internal->inline_limit == -1) internal->inline_limit = internal->parent->inline_limit;
3580 if (internal->inline_header == -1) internal->inline_header = internal->parent->inline_header;
3581 if (internal->inline_alias == -1) internal->inline_alias = internal->parent->inline_alias;
3584 if (internal->layout)
3585 layout = internal->layout;
3587 else if (internal->parent)
3589 Efreet_Menu_Internal *parent;
3590 parent = internal->parent;
3593 layout = parent->default_layout;
3594 parent = parent->parent;
3595 } while (!layout && parent);
3599 entry = efreet_menu_entry_new();
3600 entry->type = EFREET_MENU_ENTRY_MENU;
3601 entry->id = eina_stringshare_add(internal->name.internal);
3602 entry->name = eina_stringshare_add(internal->name.name);
3603 if (internal->directory)
3605 entry->icon = eina_stringshare_add(internal->directory->icon);
3606 efreet_desktop_ref(internal->directory);
3607 entry->desktop = internal->directory;
3609 entry->entries = NULL;
3612 if (internal->sub_menus)
3614 internal->sub_menus = eina_list_sort(internal->sub_menus,
3616 EINA_COMPARE_CB(efreet_menu_cb_menu_compare));
3622 Efreet_Menu_Layout *lay;
3624 EINA_LIST_FOREACH(layout, l, lay)
3625 efreet_menu_layout_entries_get(entry, internal, lay);
3629 /* Default layout, first menus, then desktop */
3630 if (internal->sub_menus)
3632 Efreet_Menu_Internal *sub;
3634 EINA_LIST_FOREACH(internal->sub_menus, l, sub)
3636 Efreet_Menu *sub_entry;
3637 if ((sub->directory && sub->directory->no_display) || sub->deleted) continue;
3638 sub_entry = efreet_menu_layout_menu(sub);
3639 /* Don't show empty menus */
3640 if (!sub_entry->entries)
3642 efreet_menu_free(sub_entry);
3645 entry->entries = eina_list_append(entry->entries, sub_entry);
3649 if (internal->applications)
3651 Efreet_Menu_Desktop *md;
3653 EINA_LIST_FOREACH(internal->applications, l, md)
3655 Efreet_Menu *sub_entry;
3656 sub_entry = efreet_menu_layout_desktop(md);
3657 entry->entries = eina_list_append(entry->entries, sub_entry);
3662 /* Don't keep this list around if it is empty */
3667 static Efreet_Menu *
3668 efreet_menu_layout_desktop(Efreet_Menu_Desktop *md)
3673 entry = efreet_menu_entry_new();
3674 entry->type = EFREET_MENU_ENTRY_DESKTOP;
3675 entry->id = eina_stringshare_add(md->id);
3676 entry->name = eina_stringshare_add(md->desktop->name);
3677 if (md->desktop->icon) entry->icon = eina_stringshare_add(md->desktop->icon);
3678 efreet_desktop_ref(md->desktop);
3679 entry->desktop = md->desktop;
3685 efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal,
3686 Efreet_Menu_Layout *layout)
3688 Efreet_Menu *sub_entry;
3690 if (internal->sub_menus && layout->type == EFREET_MENU_LAYOUT_MENUNAME)
3692 Efreet_Menu_Internal *sub;
3694 /* Efreet_Menu_Layout might be from DefaultLayout, so we need a local copy */
3695 int show_empty, in_line, inline_limit, inline_header, inline_alias;
3697 if (layout->show_empty == -1) show_empty = internal->show_empty;
3698 else show_empty = layout->show_empty;
3700 if (layout->in_line == -1) in_line = internal->in_line;
3701 else in_line = layout->in_line;
3703 if (layout->inline_limit == -1) inline_limit = internal->inline_limit;
3704 else inline_limit = layout->inline_limit;
3706 if (layout->inline_header == -1) inline_header = internal->inline_header;
3707 else inline_header = layout->inline_header;
3709 if (layout->inline_alias == -1) inline_alias = internal->inline_alias;
3710 else inline_alias = layout->inline_alias;
3712 sub = eina_list_search_unsorted(internal->sub_menus,
3713 EINA_COMPARE_CB(efreet_menu_cb_compare_names), layout->name);
3716 if (!(sub->directory && sub->directory->no_display) && !sub->deleted)
3718 sub_entry = efreet_menu_layout_menu(sub);
3719 if (!show_empty && efreet_menu_layout_is_empty(sub_entry))
3720 efreet_menu_free(sub_entry);
3722 ((inline_limit == 0) ||
3723 (!sub_entry->entries || (eina_list_count(sub_entry->entries) <= inline_limit))))
3726 if (!sub_entry->entries)
3728 /* Can't inline an empty submenu */
3729 entry->entries = eina_list_append(entry->entries, sub_entry);
3731 else if (inline_alias && (eina_list_count(sub_entry->entries) == 1))
3735 tmp = eina_list_data_get(sub_entry->entries);
3736 sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3737 IF_RELEASE(tmp->name);
3738 tmp->name = sub_entry->name;
3739 sub_entry->name = NULL;
3740 IF_RELEASE(tmp->icon);
3741 tmp->icon = sub_entry->icon;
3742 sub_entry->icon = NULL;
3743 entry->entries = eina_list_append(entry->entries, tmp);
3744 efreet_menu_free(sub_entry);
3752 tmp = efreet_menu_entry_new();
3753 tmp->type = EFREET_MENU_ENTRY_HEADER;
3754 tmp->name = sub_entry->name;
3755 sub_entry->name = NULL;
3756 tmp->icon = sub_entry->icon;
3757 sub_entry->icon = NULL;
3758 entry->entries = eina_list_append(entry->entries, tmp);
3760 while ((tmp = eina_list_data_get(sub_entry->entries)))
3762 sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3763 entry->entries = eina_list_append(entry->entries, tmp);
3765 efreet_menu_free(sub_entry);
3769 entry->entries = eina_list_append(entry->entries, sub_entry);
3771 internal->sub_menus = eina_list_remove(internal->sub_menus, sub);
3772 efreet_menu_internal_free(sub);
3775 else if (internal->applications && layout->type == EFREET_MENU_LAYOUT_FILENAME)
3777 Efreet_Menu_Desktop *md;
3778 md = eina_list_search_unsorted(internal->applications,
3779 EINA_COMPARE_CB(efreet_menu_cb_md_compare_ids), layout->name);
3782 sub_entry = efreet_menu_layout_desktop(md);
3783 entry->entries = eina_list_append(entry->entries, sub_entry);
3784 internal->applications = eina_list_remove(internal->applications, md);
3787 else if (layout->type == EFREET_MENU_LAYOUT_MERGE)
3789 if (internal->applications && !strcmp(layout->name, "files"))
3791 Efreet_Menu_Desktop *md;
3793 while ((md = eina_list_data_get(internal->applications)))
3795 internal->applications = eina_list_remove_list(internal->applications,
3796 internal->applications);
3797 sub_entry = eina_list_search_unsorted(entry->entries,
3798 EINA_COMPARE_CB(efreet_menu_cb_entry_compare_desktop),
3802 sub_entry = efreet_menu_layout_desktop(md);
3803 entry->entries = eina_list_append(entry->entries, sub_entry);
3806 internal->applications = eina_list_free(internal->applications);
3808 else if (internal->sub_menus && !strcmp(layout->name, "menus"))
3810 Efreet_Menu_Internal *sub;
3812 while ((sub = eina_list_data_get(internal->sub_menus)))
3814 internal->sub_menus = eina_list_remove_list(internal->sub_menus, internal->sub_menus);
3815 if ((sub->directory && sub->directory->no_display) || sub->deleted)
3817 efreet_menu_internal_free(sub);
3820 sub_entry = eina_list_search_unsorted(entry->entries,
3821 EINA_COMPARE_CB(efreet_menu_cb_entry_compare_menu),
3825 sub_entry = efreet_menu_layout_menu(sub);
3826 if (!internal->show_empty && efreet_menu_layout_is_empty(sub_entry))
3827 efreet_menu_free(sub_entry);
3828 else if (internal->in_line &&
3829 ((internal->inline_limit == 0) ||
3830 (!sub_entry->entries || (eina_list_count(sub_entry->entries) <= internal->inline_limit))))
3833 if (!sub_entry->entries)
3835 /* Can't inline an empty submenu */
3836 entry->entries = eina_list_append(entry->entries, sub_entry);
3838 else if (internal->inline_alias && (eina_list_count(sub_entry->entries) == 1))
3842 tmp = eina_list_data_get(sub_entry->entries);
3843 sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3844 eina_stringshare_del(tmp->name);
3845 tmp->name = sub_entry->name;
3846 sub_entry->name = NULL;
3847 IF_RELEASE(tmp->icon);
3848 if (sub_entry->icon)
3850 tmp->icon = sub_entry->icon;
3851 sub_entry->icon = NULL;
3853 entry->entries = eina_list_append(entry->entries, tmp);
3854 efreet_menu_free(sub_entry);
3860 if (internal->inline_header)
3862 tmp = efreet_menu_entry_new();
3863 tmp->type = EFREET_MENU_ENTRY_HEADER;
3864 tmp->name = sub_entry->name;
3865 sub_entry->name = NULL;
3866 if (sub_entry->icon) tmp->icon = sub_entry->icon;
3867 sub_entry->icon = NULL;
3868 entry->entries = eina_list_append(entry->entries, tmp);
3870 while ((tmp = eina_list_data_get(sub_entry->entries)))
3872 sub_entry->entries = eina_list_remove_list(sub_entry->entries,
3873 sub_entry->entries);
3874 entry->entries = eina_list_append(entry->entries, tmp);
3876 efreet_menu_free(sub_entry);
3880 entry->entries = eina_list_append(entry->entries, sub_entry);
3882 efreet_menu_internal_free(sub);
3884 IF_FREE_LIST(internal->sub_menus, efreet_menu_internal_free);
3886 else if (internal->sub_menus && !strcmp(layout->name, "all"))
3890 orig = layout->name;
3891 layout->name = "menus";
3892 efreet_menu_layout_entries_get(entry, internal, layout);
3893 layout->name = "files";
3894 efreet_menu_layout_entries_get(entry, internal, layout);
3895 layout->name = orig;
3898 else if (layout->type == EFREET_MENU_LAYOUT_SEPARATOR)
3900 sub_entry = efreet_menu_entry_new();
3901 sub_entry->type = EFREET_MENU_ENTRY_SEPARATOR;
3902 entry->entries = eina_list_append(entry->entries, sub_entry);
3907 efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal)
3909 if (entry->type != EFREET_MENU_ENTRY_MENU) return 1;
3910 return ecore_str_compare(entry->name, internal->name.name);
3914 efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop)
3916 if (entry->type != EFREET_MENU_ENTRY_DESKTOP) return -1;
3917 return ecore_str_compare(entry->name, desktop->name);
3921 efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old)
3923 return ecore_str_compare(move->old_name, old);
3927 efreet_menu_layout_is_empty(Efreet_Menu *entry)
3929 Efreet_Menu *sub_entry;
3932 if (!entry->entries) return 1;
3934 EINA_LIST_FOREACH(entry->entries, l, sub_entry)
3936 if (sub_entry->type == EFREET_MENU_ENTRY_MENU) return 0;
3937 if (sub_entry->type == EFREET_MENU_ENTRY_DESKTOP) return 0;