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 Ecore_DList *directories; /**< All the directories set in the menu file */
46 Efreet_Menu_Move *current_move; /**< The current move */
48 Ecore_List *app_dirs; /**< .desktop application directories */
50 Ecore_List *app_pool; /**< application pool */
51 Ecore_List *applications; /**< applications in this menu */
53 Ecore_DList *directory_dirs; /**< .directory file directories */
54 Ecore_Hash *directory_cache; /**< .directory dirs */
56 Ecore_List *moves; /**< List of moves to be handled by the menu */
57 Ecore_List *filters; /**< Include and Exclude filters */
59 Efreet_Menu_Internal *parent; /**< Our parent menu */
60 Ecore_List *sub_menus; /**< Our sub menus */
62 Ecore_List *layout; /**< This menus layout */
63 Ecore_List *default_layout; /**< Default layout */
64 char show_empty; /**< Whether to show empty menus */
65 char in_line; /**< Whether this meny can be inlined */
66 char inline_limit; /**< Number of elements which triggers inline */
67 char inline_header; /**< Whether we should use the header name when this menu is inlined */
68 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 Ecore_List *categories; /**< The categories this op applies too */
134 Ecore_List *filenames; /**< The filenames this op applies too */
136 Ecore_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 char show_empty; /**< Whether to show empty menus */
188 char in_line; /**< Whether this meny can be inlined */
189 char inline_limit; /**< Number of elements which triggers inline */
190 char inline_header; /**< Whether we should use the header name when this menu is inlined */
191 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 Ecore_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 Ecore_Hash *efreet_merged_menus = NULL;
216 static Ecore_Hash *efreet_merged_dirs = NULL;
218 static Ecore_Hash *efreet_menu_handle_cbs = NULL;
219 static Ecore_Hash *efreet_menu_filter_cbs = NULL;
220 static Ecore_Hash *efreet_menu_move_cbs = NULL;
221 static Ecore_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 void efreet_menu_process_app_pool(Ecore_List *pool, Ecore_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 = ecore_hash_new(NULL, NULL);
443 efreet_menu_filter_cbs = ecore_hash_new(NULL, NULL);
444 efreet_menu_move_cbs = ecore_hash_new(NULL, NULL);
445 efreet_menu_layout_cbs = ecore_hash_new(NULL, NULL);
446 if (!efreet_menu_handle_cbs || !efreet_menu_filter_cbs
447 || !efreet_menu_move_cbs || !efreet_menu_layout_cbs)
450 ecore_hash_free_key_cb_set(efreet_menu_handle_cbs,
451 ECORE_FREE_CB(eina_stringshare_del));
452 ecore_hash_free_key_cb_set(efreet_menu_filter_cbs,
453 ECORE_FREE_CB(eina_stringshare_del));
454 ecore_hash_free_key_cb_set(efreet_menu_move_cbs,
455 ECORE_FREE_CB(eina_stringshare_del));
456 ecore_hash_free_key_cb_set(efreet_menu_layout_cbs,
457 ECORE_FREE_CB(eina_stringshare_del));
459 /* set Menu into it's own so we can check the XML is valid before trying
461 efreet_tag_menu = eina_stringshare_add(menu_cbs[0].key);
463 for (i = 0; menu_cbs[i].key != NULL; i++)
464 ecore_hash_set(efreet_menu_handle_cbs,
465 (void *)eina_stringshare_add(menu_cbs[i].key),
468 for (i = 0; filter_cbs[i].key != NULL; i++)
469 ecore_hash_set(efreet_menu_filter_cbs,
470 (void *)eina_stringshare_add(filter_cbs[i].key),
473 for (i = 0; move_cbs[i].key != NULL; i++)
474 ecore_hash_set(efreet_menu_move_cbs,
475 (void *)eina_stringshare_add(move_cbs[i].key),
478 for (i = 0; layout_cbs[i].key != NULL; i++)
479 ecore_hash_set(efreet_menu_layout_cbs,
480 (void *)eina_stringshare_add(layout_cbs[i].key),
487 * @return Returns no value
488 * @brief Initialize legacy kde support. This function blocks while
489 * the kde-config script is run.
492 efreet_menu_kde_legacy_init(void)
498 IF_FREE_LIST(efreet_menu_kde_legacy_dirs);
500 f = popen("kde-config --path apps", "r");
503 efreet_menu_kde_legacy_dirs = ecore_list_new();
504 ecore_list_free_cb_set(efreet_menu_kde_legacy_dirs,
505 ECORE_FREE_CB(eina_stringshare_del));
507 /* XXX if the return from kde-config is a line longer than PATH_MAX,
508 * this won't be correct (increase buffer and get the rest...) */
509 if (!fgets(buf, PATH_MAX, f))
511 printf("Error initializing KDE legacy information\n");
520 ecore_list_append(efreet_menu_kde_legacy_dirs,
521 (void *)eina_stringshare_add(s));
527 ecore_list_append(efreet_menu_kde_legacy_dirs,
528 (void *)eina_stringshare_add(s));
535 * @return Returns no value
536 * @brief Shuts down the Efreet menu system
539 efreet_menu_shutdown(void)
541 IF_FREE(efreet_menu_prefix);
542 IF_FREE(efreet_menu_file);
544 IF_FREE_HASH(efreet_menu_handle_cbs);
545 IF_FREE_HASH(efreet_menu_filter_cbs);
546 IF_FREE_HASH(efreet_menu_move_cbs);
547 IF_FREE_HASH(efreet_menu_layout_cbs);
549 IF_FREE_LIST(efreet_menu_kde_legacy_dirs);
551 IF_FREE_HASH(efreet_merged_menus);
552 IF_FREE_HASH(efreet_merged_dirs);
554 IF_RELEASE(efreet_tag_menu);
556 efreet_xml_shutdown();
557 eina_stringshare_shutdown();
561 efreet_menu_new(void)
564 menu = efreet_menu_entry_new();
565 menu->type = EFREET_MENU_ENTRY_MENU;
570 efreet_menu_file_set(const char *file)
572 IF_FREE(efreet_menu_file);
573 efreet_menu_file = NULL;
574 if (file) efreet_menu_file = strdup(file);
578 * @return Returns the Efreet_Menu_Internal representation of the default menu or
580 * @brief Creates the default menu representation
583 efreet_menu_get(void)
587 Ecore_List *config_dirs;
589 /* check the users config directory first */
590 snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
591 efreet_config_home_get(), efreet_menu_prefix_get());
592 if (ecore_file_exists(menu))
593 return efreet_menu_parse(menu);
595 if (efreet_menu_file)
597 if (ecore_file_exists(efreet_menu_file))
598 return efreet_menu_parse(efreet_menu_file);
601 /* fallback to the XDG_CONFIG_DIRS */
602 config_dirs = efreet_config_dirs_get();
603 ecore_list_first_goto(config_dirs);
604 while ((dir = ecore_list_next(config_dirs)))
606 snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
607 dir, efreet_menu_prefix_get());
608 if (ecore_file_exists(menu))
609 return efreet_menu_parse(menu);
616 * @param path: The path of the menu to load
617 * @return Returns the Efreet_Menu_Internal representation on success or NULL on
619 * @brief Parses the given .menu file and creates the menu representation
622 efreet_menu_parse(const char *path)
625 Efreet_Menu_Internal *internal = NULL;
626 Efreet_Menu *entry = NULL;
627 Ecore_List *search_dirs;
629 xml = efreet_xml_new(path);
630 if (!xml) return NULL;
632 /* make sure we've got a <Menu> to start with */
633 if (xml->tag != efreet_tag_menu)
635 printf("Menu file didn't start with <Menu> tag.\n");
640 IF_FREE_HASH(efreet_merged_menus);
641 efreet_merged_menus = ecore_hash_new(ecore_str_hash, ecore_str_compare);
642 ecore_hash_free_key_cb_set(efreet_merged_menus, ECORE_FREE_CB(free));
644 IF_FREE_HASH(efreet_merged_dirs);
645 efreet_merged_dirs = ecore_hash_new(ecore_str_hash, ecore_str_compare);
646 ecore_hash_free_key_cb_set(efreet_merged_dirs, ECORE_FREE_CB(free));
648 /* split appart the filename and the path */
649 internal = efreet_menu_internal_new();
651 /* Set default values */
652 internal->show_empty = 0;
653 internal->in_line = 0;
654 internal->inline_limit = 4;
655 internal->inline_header = 1;
656 internal->inline_alias = 0;
658 search_dirs = efreet_config_dirs_get();
660 efreet_menu_path_set(internal, path);
661 if (!efreet_menu_handle_menu(internal, xml))
664 efreet_menu_internal_free(internal);
669 efreet_menu_resolve_moves(internal);
671 if (!efreet_menu_process_dirs(internal))
673 efreet_menu_internal_free(internal);
677 /* handle all .desktops */
678 if (!efreet_menu_process(internal, 0))
680 efreet_menu_internal_free(internal);
684 /* handle menus with only unallocated .desktops */
685 if (!efreet_menu_process(internal, 1))
687 efreet_menu_internal_free(internal);
692 entry = efreet_menu_layout_menu(internal);
693 efreet_menu_internal_free(internal);
698 * @param menu: The menu to work with
699 * @param path: The path where the menu should be saved
700 * @return Returns 1 on success, 0 on failure
701 * @brief Saves the menu to file
704 efreet_menu_save(Efreet_Menu *menu, const char *path)
709 f = fopen(path, "w");
711 fprintf(f, "<?xml version=\"1.0\"?>\n");
712 fprintf(f, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\" "
713 "\"http://standards.freedesktop.org/menu-spec/menu-1.0.dtd\">\n");
714 ret = efreet_menu_save_menu(menu, f, 0);
720 efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent)
722 efreet_menu_save_indent(f, indent);
723 fprintf(f, "<Menu>\n");
724 efreet_menu_save_indent(f, indent + 1);
725 fprintf(f, "<Name>%s</Name>\n", menu->name);
729 /* Only save these for the root element */
730 efreet_menu_save_indent(f, indent + 1);
731 fprintf(f, "<DefaultAppDirs/>\n");
732 efreet_menu_save_indent(f, indent + 1);
733 fprintf(f, "<DefaultDirectoryDirs/>\n");
738 efreet_menu_save_indent(f, indent + 1);
739 fprintf(f, "<Directory>%s</Directory>\n", menu->desktop->orig_path);
745 int has_desktop = 0, has_menu = 0;
747 efreet_menu_save_indent(f, indent + 1);
748 fprintf(f, "<Layout>\n");
749 ecore_list_first_goto(menu->entries);
750 while ((entry = ecore_list_next(menu->entries)))
752 if (entry->type == EFREET_MENU_ENTRY_MENU)
754 efreet_menu_save_indent(f, indent + 2);
755 fprintf(f, "<Menuname>%s</Menuname>\n", entry->id);
758 else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
760 efreet_menu_save_indent(f, indent + 2);
761 fprintf(f, "<Filename>%s</Filename>\n", entry->id);
764 else if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
766 efreet_menu_save_indent(f, indent + 2);
767 fprintf(f, "<Separator/>\n");
770 efreet_menu_save_indent(f, indent + 1);
771 fprintf(f, "</Layout>\n");
775 efreet_menu_save_indent(f, indent + 1);
776 fprintf(f, "<Include>\n");
777 ecore_list_first_goto(menu->entries);
778 while ((entry = ecore_list_next(menu->entries)))
780 if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
782 efreet_menu_save_indent(f, indent + 2);
783 fprintf(f, "<Filename>%s</Filename>\n", entry->id);
786 efreet_menu_save_indent(f, indent + 1);
787 fprintf(f, "</Include>\n");
792 ecore_list_first_goto(menu->entries);
793 while ((entry = ecore_list_next(menu->entries)))
795 if (entry->type == EFREET_MENU_ENTRY_MENU)
796 efreet_menu_save_menu(entry, f, indent + 1);
800 efreet_menu_save_indent(f, indent);
801 fprintf(f, "</Menu>\n");
806 efreet_menu_save_indent(FILE *f, int indent)
810 for (i = 0; i < indent; i++)
816 * @param menu: The menu to work with
817 * @param desktop: The desktop to insert
818 * @param pos: The position to place the new desktop
819 * @return Returns 1 on success, 0 on failure
820 * @brief Insert a desktop element in a menu structure. Only accepts desktop files
821 * in default directories.
824 efreet_menu_desktop_insert(Efreet_Menu *menu, Efreet_Desktop *desktop, int pos)
829 if (!desktop || !menu) return 0;
830 id = efreet_util_path_to_file_id(desktop->orig_path);
833 entry = efreet_menu_entry_new();
834 entry->type = EFREET_MENU_ENTRY_DESKTOP;
835 entry->id = eina_stringshare_add(id);
836 entry->name = eina_stringshare_add(desktop->name);
837 if (desktop->icon) entry->icon = eina_stringshare_add(desktop->icon);
838 efreet_desktop_ref(desktop);
839 entry->desktop = desktop;
843 menu->entries = ecore_list_new();
844 ecore_list_free_cb_set(menu->entries, ECORE_FREE_CB(efreet_menu_free));
847 if (pos < 0 || pos >= ecore_list_count(menu->entries))
848 ecore_list_append(menu->entries, entry);
851 ecore_list_index_goto(menu->entries, pos);
852 ecore_list_insert(menu->entries, entry);
858 * @param menu: The menu to work with
859 * @param desktop: The desktop to remove
860 * @return Returns 1 on success, 0 on failure
861 * @brief Remove a desktop element in a menu structure. Only accepts desktop files
862 * in default directories.
865 efreet_menu_desktop_remove(Efreet_Menu *menu, Efreet_Desktop *desktop)
869 if (!desktop || !menu) return 0;
871 entry = ecore_list_find(menu->entries,
872 ECORE_COMPARE_CB(efreet_menu_cb_entry_compare_desktop),
876 ecore_list_remove(menu->entries);
877 efreet_menu_free(entry);
884 * @param menu: The menu to work with
885 * @param menu: The menu to work with
886 * @param indent: The indent level to print the menu at
887 * @return Returns no value
888 * @brief Dumps the contents of the menu to the command line
891 efreet_menu_dump(Efreet_Menu *menu, const char *indent)
893 printf("%s%s: ", indent, menu->name);
894 printf("%s\n", (menu->icon ? menu->icon : "No icon"));
896 /* XXX dump the rest of the menu info */
904 len = strlen(indent) + 3;
905 new_indent = malloc(sizeof(char *) * len);
906 snprintf(new_indent, len, "%s ", indent);
908 ecore_list_first_goto(menu->entries);
909 while ((entry = ecore_list_next(menu->entries)))
911 if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
912 printf("%s|---\n", new_indent);
913 else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
914 printf("%s|-%s\n", new_indent, entry->name);
915 else if (entry->type == EFREET_MENU_ENTRY_MENU)
916 efreet_menu_dump(entry, new_indent);
917 else if (entry->type == EFREET_MENU_ENTRY_HEADER)
918 printf("%s|---%s\n", new_indent, entry->name);
926 * @param user_dir: The user directory to work with
927 * @param system_dirs: The system directories to work with
928 * @param suffix: The path suffix to add
929 * @return Returns the list of directories
930 * @brief Creates the list of directories based on the user
931 * dir, system dirs and given suffix.
934 efreet_default_dirs_get(const char *user_dir, Ecore_List *system_dirs,
941 list = ecore_list_new();
942 ecore_list_free_cb_set(list, ECORE_FREE_CB(free));
944 snprintf(dir, sizeof(dir), "%s/%s", user_dir, suffix);
945 ecore_list_append(list, strdup(dir));
947 ecore_list_first_goto(system_dirs);
948 while ((xdg_dir = ecore_list_next(system_dirs)))
950 snprintf(dir, sizeof(dir), "%s/%s", xdg_dir, suffix);
951 ecore_list_append(list, strdup(dir));
959 * @return Returns a new Efreet_Menu_Internal struct
960 * @brief Allocates and initializes a new Efreet_Menu_Internal structure
962 static Efreet_Menu_Internal *
963 efreet_menu_internal_new(void)
965 Efreet_Menu_Internal *internal;
967 internal = NEW(Efreet_Menu_Internal, 1);
968 internal->show_empty = -1;
969 internal->in_line = -1;
970 internal->inline_limit = -1;
971 internal->inline_header = -1;
972 internal->inline_alias = -1;
978 * @param menu: The menu to free
979 * @return Returns no value
980 * @brief Frees up the given menu structure
983 efreet_menu_internal_free(Efreet_Menu_Internal *internal)
985 if (!internal) return;
987 IF_FREE(internal->file.path);
988 IF_FREE(internal->file.name);
990 IF_RELEASE(internal->name.internal);
991 internal->name.name = NULL;
993 IF_FREE_LIST(internal->applications);
995 IF_FREE_DLIST(internal->directories);
996 IF_FREE_LIST(internal->app_dirs);
997 IF_FREE_LIST(internal->app_pool);
998 IF_FREE_DLIST(internal->directory_dirs);
999 IF_FREE_HASH(internal->directory_cache);
1001 IF_FREE_LIST(internal->moves);
1002 IF_FREE_LIST(internal->filters);
1004 IF_FREE_LIST(internal->sub_menus);
1006 IF_FREE_LIST(internal->layout);
1007 IF_FREE_LIST(internal->default_layout);
1014 * @return Returns the XDG_MENU_PREFIX env variable or "" if none set
1015 * @brief Retrieves the XDG_MENU_PREFIX or "" if not set.
1018 efreet_menu_prefix_get(void)
1022 if (efreet_menu_prefix) return efreet_menu_prefix;
1024 prefix = getenv("XDG_MENU_PREFIX");
1025 if (prefix) efreet_menu_prefix = strdup(prefix);
1026 else efreet_menu_prefix = strdup("");
1028 return efreet_menu_prefix;
1033 * @param menu: The menu to populate
1034 * @param xml: The xml dom tree to populate from
1035 * @return Returns 1 if this XML tree is valid, 0 otherwise
1036 * @brief Populates the given menu from the given xml structure
1038 * We walk the Menu children backwards. The reason for this is so that we
1039 * can deal with all the things that make us select the 'last' element
1040 * (MergeFile, Directory, etc). We'll see the last one first and can deal
1041 * with it right away.
1044 efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml)
1047 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
1049 ecore_list_last_goto(xml->children);
1050 while ((child = ecore_dlist_previous(xml->children)))
1052 cb = ecore_hash_get(efreet_menu_handle_cbs, child->tag);
1055 if (!cb(internal, child))
1060 printf("Unknown XML tag: %s\n", child->tag);
1069 * @param parent: The parent Menu
1070 * @param xml: The xml that defines the menu
1071 * @return Returns 1 on success or 0 on failure
1072 * @brief Handles the sub-menu nodes of the XML file
1075 efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1077 Efreet_Menu_Internal *internal, *match;
1079 efreet_menu_create_sub_menu_list(parent);
1081 internal = efreet_menu_internal_new();
1082 internal->file.path = strdup(parent->file.path);
1083 if (!efreet_menu_handle_menu(internal, xml))
1085 efreet_menu_internal_free(internal);
1089 /* if this menu already exists we just take this one and stick it on the
1090 * start of the existing one */
1091 if ((match = ecore_list_find(parent->sub_menus,
1092 ECORE_COMPARE_CB(efreet_menu_cb_menu_compare), internal)))
1095 efreet_menu_concatenate(match, internal);
1096 efreet_menu_internal_free(internal);
1099 ecore_list_prepend(parent->sub_menus, internal);
1106 * @param parent: The parent menu
1107 * @param xml: The xml tree
1108 * @return Returns 1 on success or 0 on failure
1109 * @brief Handles the AppDir tag
1112 efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1115 Efreet_Menu_App_Dir *app_dir;
1117 if (!parent || !xml) return 0;
1119 efreet_menu_create_app_dirs_list(parent);
1120 path = efreet_menu_path_get(parent, xml->text);
1121 if (!path) return 0;
1123 /* we've already got this guy in our list we can skip it */
1124 if (ecore_list_find(parent->app_dirs,
1125 ECORE_COMPARE_CB(efreet_menu_cb_app_dirs_compare), path))
1131 app_dir = efreet_menu_app_dir_new();
1132 app_dir->path = path;
1134 ecore_list_prepend(parent->app_dirs, app_dir);
1141 * @param parent: The parent menu
1142 * @param xml: UNUSED
1143 * @return Returns 1 on success or 0 on failure
1144 * @brief Handles the DefaultAppDirs
1147 efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1149 Ecore_List *dirs, *prepend;
1152 if (!parent) return 0;
1154 efreet_menu_create_app_dirs_list(parent);
1155 dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
1157 prepend = ecore_list_new();
1158 ecore_list_first_goto(dirs);
1159 while ((dir = ecore_list_next(dirs)))
1161 Efreet_Menu_App_Dir *app_dir;
1163 if (ecore_list_find(parent->app_dirs,
1164 ECORE_COMPARE_CB(efreet_menu_cb_app_dirs_compare), dir))
1167 app_dir = efreet_menu_app_dir_new();
1168 app_dir->path = strdup(dir);
1170 ecore_list_append(prepend, app_dir);
1172 ecore_list_destroy(dirs);
1173 ecore_list_prepend_list(parent->app_dirs, prepend);
1174 ecore_list_destroy(prepend);
1181 * @param parent: The parent menu
1182 * @param xml: The xml tree
1183 * @return Returns 1 on success or 0 on failure
1184 * @brief Handles the DirectoryDir tag
1187 efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1191 if (!parent || !xml) return 0;
1193 efreet_menu_create_directory_dirs_list(parent);
1194 path = efreet_menu_path_get(parent, xml->text);
1195 if (!path) return 0;
1197 /* we've already got this guy in our list we can skip it */
1198 if (ecore_list_find(parent->directory_dirs, ECORE_COMPARE_CB(strcmp), path))
1204 ecore_dlist_prepend(parent->directory_dirs, path);
1211 * @param parent: The parent menu
1212 * @param xml: UNUSED
1213 * @return Returns 1 on success or 0 on failure
1214 * @brief Handles the DefaultDirectoryDirs tag
1217 efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1222 if (!parent) return 0;
1224 efreet_menu_create_directory_dirs_list(parent);
1225 dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
1226 "desktop-directories");
1227 ecore_list_first_goto(dirs);
1228 while ((dir = ecore_list_next(dirs)))
1230 if (ecore_list_find(parent->directory_dirs, ECORE_COMPARE_CB(strcmp), dir))
1233 ecore_dlist_prepend(parent->directory_dirs, strdup(dir));
1236 ecore_list_destroy(dirs);
1243 * @param parent: The parent Menu
1244 * @param xml: The xml to work with
1245 * @return Returns 1 on success or 0 on failure
1246 * @brief Sets the menu name from the given XML fragment.
1249 efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1251 /* not allowed to have two Name settings in a menu */
1252 if (parent->name.internal)
1254 printf("efreet_menu_handle_name() setting second name into menu\n");
1258 /* ignore the name if it contains a / */
1259 if (strchr(xml->text, '/')) return 1;
1261 parent->name.internal = eina_stringshare_add(xml->text);
1268 * @param parent: The parent menu
1269 * @param xml: The xml tree
1270 * @return Returns 1 on success or 0 on failure
1271 * @brief Handles the Directory tag
1273 * This just adds the given directory path to a list which we'll walk once
1274 * we've traversed the entire menu into memory.
1277 efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1279 if (!parent || !xml) return 0;
1281 efreet_menu_create_directories_list(parent);
1282 ecore_dlist_prepend(parent->directories, strdup(xml->text));
1289 * @param parent: The parent menu
1290 * @param xml: The xml tree
1291 * @return Returns 1 on success or 0 on failure
1292 * @brief Handles the OnlyUnallocated tag
1295 efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1297 if (!parent || !xml) return 0;
1299 /* a later instance has been seen so we can ignore this one */
1300 if (parent->seen_allocated) return 1;
1302 parent->seen_allocated = 1;
1303 parent->only_unallocated = 1;
1310 * @param parent: The parent menu
1311 * @param xml: The xml tree
1312 * @return Returns 1 on success or 0 on failure
1313 * @brief Handles the NotOnlyUnallocated tag
1316 efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1318 if (!parent || !xml) return 0;
1320 /* a later instance has been seen so we can ignore this one */
1321 if (parent->seen_allocated) return 1;
1323 parent->seen_allocated = 1;
1324 parent->only_unallocated = 0;
1331 * @param parent: The parent menu
1332 * @param xml: The xml tree
1333 * @return Returns 1 on success or 0 on failure
1334 * @brief Handles the Deleted tag
1337 efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1339 if (!parent || !xml) return 0;
1341 /* a later instance has been seen so we can ignore this one */
1342 if (parent->seen_deleted) return 1;
1344 parent->seen_deleted = 1;
1345 parent->deleted = 1;
1352 * @param parent: The parent menu
1353 * @param xml: The xml tree
1354 * @return Returns 1 on success or 0 on failure
1355 * @brief Handles the NotDeleted tag
1358 efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1360 if (!parent || !xml) return 0;
1362 /* a later instance has been seen so we can ignore this one */
1363 if (parent->seen_deleted) return 1;
1365 parent->seen_deleted = 1;
1366 parent->deleted = 0;
1373 * @param parent: The parent menu
1374 * @param xml: The XML tree to work with
1375 * @return Returns 1 on success or 0 on failure
1376 * @brief Handles parsing the Include tag and all subtags
1379 efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1381 return efreet_menu_handle_filter(parent, xml,
1382 EFREET_MENU_FILTER_INCLUDE);
1387 * @param parent: The parent menu
1388 * @param xml: The xml tree
1389 * @return Returns 1 on success or 0 on failure
1390 * @brief Handles the Exclude tag and all subtags
1393 efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1395 return efreet_menu_handle_filter(parent, xml,
1396 EFREET_MENU_FILTER_EXCLUDE);
1401 * @param op: The filter operation
1402 * @param xml: The xml tree
1403 * @return Returns 1 on success or 0 on failure
1404 * @brief Handles the Filename tag
1407 efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1409 if (!op || !xml) return 0;
1413 op->filenames = ecore_list_new();
1414 ecore_list_free_cb_set(op->filenames, free);
1417 ecore_list_append(op->filenames, strdup(xml->text));
1424 * @param op: The filter operation
1425 * @param xml: The xml tree
1426 * @return Returns 1 on success or 0 on failure
1427 * @brief Handles the Category tag
1430 efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1432 if (!op || !xml) return 0;
1434 if (!op->categories)
1436 op->categories = ecore_list_new();
1437 ecore_list_free_cb_set(op->categories, free);
1440 ecore_list_append(op->categories, strdup(xml->text));
1447 * @param op: The filter operation
1448 * @param xml: The xml tree
1449 * @return Returns 1 on success or 0 on failure
1450 * @brief Handles the All tag and all subtags
1453 efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1455 if (!op || !xml) return 0;
1464 * @param op: The filter operation
1465 * @param xml: The xml tree
1466 * @return Returns 1 on success or 0 on failure
1467 * @brief Handles the And tag and all subtags
1470 efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1472 if (!op || !xml) return 0;
1474 return efreet_menu_handle_filter_child_op(op, xml,
1475 EFREET_MENU_FILTER_OP_AND);
1480 * @param op: The filter operation
1481 * @param xml: The xml tree
1482 * @return Returns 1 on success or 0 on failure
1483 * @brief Handles the Or tag and all subtags
1486 efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1488 if (!op || !xml) return 0;
1490 return efreet_menu_handle_filter_child_op(op, xml,
1491 EFREET_MENU_FILTER_OP_OR);
1496 * @param op: The filter operation
1497 * @param xml: The xml tree
1498 * @return Returns 1 on success or 0 on failure
1499 * @brief Handles the Not tag and all subtags
1502 efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1504 if (!op || !xml) return 0;
1506 return efreet_menu_handle_filter_child_op(op, xml,
1507 EFREET_MENU_FILTER_OP_NOT);
1512 * @param parent: The parent menu
1513 * @param xml: The xml tree
1514 * @return Returns 1 on success or 0 on failure
1515 * @brief Handles the MergeFile tag
1518 efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1521 const char *attr = NULL;
1525 if (!parent || !xml) return 0;
1527 /* check to see if this is a path or parent type */
1528 attr = efreet_xml_attribute_get(xml, "type");
1529 if (attr && !strcmp(attr, "parent"))
1532 /* we're given a path */
1534 path = efreet_menu_path_get(parent, xml->text);
1536 /* need to find the next menu with the same name as ours in the config
1537 * dir after ours (if we're in a config dir) */
1540 Ecore_List *search_dirs;
1541 const char *dir, *p;
1544 if (!parent->file.path)
1546 printf("efreet_menu_handle_merge_file() missing menu path ...\n");
1550 search_dirs = efreet_config_dirs_get();
1552 /* we need to find the next menu with the same name in the directory
1553 * after the on the the menu was found in. to do that we first check
1554 * if it's in the config_home_directory() if so we need to search
1555 * all of the dirs. If it isn't in the config home directory then we
1556 * scan the search dirs and look for it. The search_dirs list will
1557 * be left at the next pointer so we can start looking for the menu
1558 * from that point */
1560 ecore_list_first_goto(search_dirs);
1561 dir = efreet_config_home_get();
1563 if (strncmp(dir, parent->file.path, len))
1565 while ((dir = ecore_list_next(search_dirs)))
1567 if (!strncmp(dir, parent->file.path, len))
1574 printf("efreet_menu_handle_merge_file() failed to find "
1575 "menu parent directory\n");
1579 /* the parent file path may have more path then just the base
1580 * directory so we need to append that as well */
1581 p = parent->file.path + len;
1583 /* whatever dirs are left in the search dir we need to look for the
1584 * menu with the same relative filename */
1585 while ((dir = ecore_list_next(search_dirs)))
1587 char file[PATH_MAX];
1589 snprintf(file, sizeof(file), "%s/%s/%s", dir, (p ? p : ""),
1591 if (ecore_file_exists(file))
1593 path = strdup(file);
1599 /* nothing to do if no file found */
1600 if (!path) return 1;
1602 if (!efreet_menu_merge(parent, xml, path))
1612 * @param parent: The parent menu to merge into
1613 * @param xml: The XML to be merged
1614 * @param path: The path to the .menu file to merge
1617 efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
1619 Efreet_Xml *merge_xml;
1620 Efreet_Menu_Internal *internal;
1623 if (!parent || !xml || !path) return 0;
1625 /* do nothing if the file doesn't exist */
1626 if (!ecore_file_exists(path)) return 1;
1628 realpath = ecore_file_realpath(path);
1629 if (realpath[0] == '\0')
1631 printf("efreet_menu_merge() unable to get real path for %s\n", path);
1635 /* don't merge the same path twice */
1636 if (ecore_hash_get(efreet_merged_menus, realpath))
1639 ecore_hash_set(efreet_merged_menus, strdup(realpath), (void *)1);
1641 merge_xml = efreet_xml_new(realpath);
1646 printf("efreet_menu_merge() failed to read in the "
1647 "merge file (%s)\n", realpath);
1651 internal = efreet_menu_internal_new();
1652 efreet_menu_path_set(internal, path);
1653 efreet_menu_handle_menu(internal, merge_xml);
1654 efreet_menu_concatenate(parent, internal);
1655 efreet_menu_internal_free(internal);
1657 efreet_xml_del(merge_xml);
1664 * @param parent: The parent menu
1665 * @param xml: The xml tree
1666 * @return Returns 1 on success or 0 on failure
1667 * @brief Handles the MergeDir tag
1670 efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1675 if (!parent || !xml || !xml->text) return 0;
1677 path = efreet_menu_path_get(parent, xml->text);
1678 if (!path) return 1;
1679 if (!ecore_file_exists(path))
1685 ret = efreet_menu_merge_dir(parent, xml, path);
1693 * @param parent: the parent menu of the merge
1694 * @param xml: The xml tree
1695 * @param path: The path to the merge directory
1696 * @return Returns 1 on success or 0 on failure
1697 * @brief Find all of the .menu files in the given directory and merge them
1698 * into the @a parent menu.
1701 efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
1703 char dir_path[PATH_MAX];
1705 struct dirent *file;
1707 if (!parent || !xml || !path) return 0;
1709 /* check to see if we've merged this directory already */
1710 if (ecore_hash_get(efreet_merged_dirs, path)) return 1;
1711 ecore_hash_set(efreet_merged_dirs, strdup(path), (void *)1);
1713 files = opendir(path);
1714 if (!files) return 1;
1716 while ((file = readdir(files)))
1720 if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
1721 p = strrchr(file->d_name, '.');
1723 if (strcmp(p, ".menu")) continue;
1725 snprintf(dir_path, PATH_MAX, "%s/%s", path, file->d_name);
1726 if (!efreet_menu_merge(parent, xml, dir_path))
1736 * @param parent: The parent menu
1737 * @param xml: The xml tree
1738 * @return Returns 1 on success or 0 on failure
1739 * @brief Handles the DefaultMergeDirs tag
1742 efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1745 char path[PATH_MAX], *p;
1748 if (!parent || !xml) return 0;
1750 prefix = efreet_menu_prefix_get();
1751 if (!strcmp(prefix, "gnome-") &&
1752 (!strcmp(parent->file.name, "gnome-applications.menu")))
1753 p = strdup("applications");
1755 else if ((!strcmp(prefix, "kde-") &&
1756 (!strcmp(parent->file.name, "kde-applications.menu"))))
1757 p = strdup("applications");
1763 p = strdup(parent->file.name);
1764 s = strrchr(p, '.');
1767 snprintf(path, sizeof(path), "menus/%s-merged", p);
1770 dirs = efreet_default_dirs_get(efreet_config_home_get(),
1771 efreet_config_dirs_get(), path);
1772 ecore_list_first_goto(dirs);
1773 while ((p = ecore_list_first_remove(dirs)))
1775 efreet_menu_merge_dir(parent, xml, p);
1778 ecore_list_destroy(dirs);
1785 * @param parent: The parent menu
1786 * @param xml: The xml tree
1787 * @return Returns 1 on success or 0 on failure
1788 * @brief Handles the LegacyDir tag
1791 efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1793 Efreet_Menu_Internal *legacy;
1795 if (!parent || !xml) return 0;
1797 legacy = efreet_menu_handle_legacy_dir_helper(NULL, parent, xml->text,
1798 efreet_xml_attribute_get(xml, "prefix"));
1799 efreet_menu_concatenate(parent, legacy);
1800 efreet_menu_internal_free(legacy);
1808 * @param parent: The parent menu
1809 * @param legacy_dir: The legacy directory path
1810 * @param prefix: The legacy directory prefix if one set
1811 * @return Returns the Efreet_Menu_Internal representing the legacy hierarchy
1812 * @brief Handles the process of merging @a legacy_dir into @a parent menu
1814 static Efreet_Menu_Internal *
1815 efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root,
1816 Efreet_Menu_Internal *parent,
1817 const char *legacy_dir,
1820 char *path, file_path[PATH_MAX];
1821 Efreet_Menu_Internal *legacy_internal;
1822 Efreet_Menu_Filter *filter;
1823 Efreet_Menu_App_Dir *app_dir;
1824 int path_len, count = 0;
1826 struct dirent *file;
1828 if (!parent || !legacy_dir) return 0;
1830 path = efreet_menu_path_get(parent, legacy_dir);
1832 /* nothing to do if the legacy path doesn't exist */
1833 if (!ecore_file_exists(path))
1839 legacy_internal = efreet_menu_internal_new();
1840 legacy_internal->name.internal = eina_stringshare_add(ecore_file_file_get(path));
1842 /* add the legacy dir as an app dir */
1843 app_dir = efreet_menu_app_dir_new();
1844 app_dir->path = strdup(path);
1845 app_dir->legacy = 1;
1846 if (prefix && !strchr(prefix, '/')) app_dir->prefix = strdup(prefix);
1848 efreet_menu_create_app_dirs_list(legacy_internal);
1849 ecore_list_append(legacy_internal->app_dirs, app_dir);
1853 /* XXX This seems wrong, but it makes efreet pass the fdo tests */
1854 app_dir = efreet_menu_app_dir_new();
1855 app_dir->path = strdup(path);
1856 app_dir->legacy = 1;
1857 if (prefix && !strchr(prefix, '/')) app_dir->prefix = strdup(prefix);
1858 ecore_list_append(root->app_dirs, app_dir);
1862 /* add the legacy dir as a directory dir */
1863 efreet_menu_create_directory_dirs_list(legacy_internal);
1864 ecore_dlist_append(legacy_internal->directory_dirs, strdup(path));
1866 /* setup a filter for all the conforming .desktop files in the legacy
1868 filter = efreet_menu_filter_new();
1869 filter->type = EFREET_MENU_FILTER_INCLUDE;
1871 filter->op->type = EFREET_MENU_FILTER_OP_OR;
1872 filter->op->filenames = ecore_list_new();
1873 ecore_list_free_cb_set(filter->op->filenames, free);
1875 efreet_menu_create_filter_list(legacy_internal);
1876 ecore_list_append(legacy_internal->filters, filter);
1878 path_len = strlen(path);
1879 files = opendir(path);
1880 while ((file = readdir(files)))
1882 Efreet_Desktop *desktop = NULL;
1886 if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
1887 file_path[0] = '\0';
1888 ecore_strlcpy(file_path, path, PATH_MAX);
1889 ecore_strlcpy(file_path + path_len, "/", PATH_MAX - path_len);
1890 ecore_strlcpy(file_path + path_len + 1, file->d_name, PATH_MAX - path_len - 1);
1892 /* recurse into sub directories */
1893 if (ecore_file_is_dir(file_path))
1895 Efreet_Menu_Internal *ret;
1897 ret = efreet_menu_handle_legacy_dir_helper(root ? root : legacy_internal,
1898 legacy_internal, file_path, prefix);
1901 efreet_menu_internal_free(legacy_internal);
1907 efreet_menu_create_sub_menu_list(legacy_internal);
1908 ecore_list_prepend(legacy_internal->sub_menus, ret);
1913 if (!strcmp(file->d_name, ".directory"))
1915 legacy_internal->directory = efreet_desktop_get(file_path);
1916 if (legacy_internal->directory
1917 && legacy_internal->directory->type != EFREET_DESKTOP_TYPE_DIRECTORY)
1919 efreet_desktop_free(legacy_internal->directory);
1920 legacy_internal->directory = NULL;
1925 exten = strrchr(file->d_name, '.');
1927 if (exten && !strcmp(exten, ".desktop"))
1928 desktop = efreet_desktop_get(file_path);
1930 if (!desktop) continue;
1932 /* if the .desktop has categories it isn't legacy */
1933 if (efreet_desktop_category_count_get(desktop) != 0)
1936 /* XXX: This will disappear when the .desktop is free'd */
1937 efreet_desktop_category_add(desktop, "Legacy");
1941 snprintf(buf, PATH_MAX, "%s%s", prefix, file->d_name);
1942 ecore_list_append(filter->op->filenames, strdup(buf));
1945 ecore_list_append(filter->op->filenames, strdup(file->d_name));
1948 efreet_desktop_free(desktop);
1953 return legacy_internal;
1958 * @param parent: The parent menu
1959 * @param xml: UNUSED
1960 * @return Returns 1 on success or 0 on failure
1961 * @brief Handles the KDELegacyDirs tag
1964 efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1968 if (!parent) return 0;
1970 if (!efreet_menu_kde_legacy_dirs) return 1;
1972 /* XXX if one _helper() call succeeds, we return success. should this be flipped?
1973 * (return fail if on of them failed) */
1974 ecore_list_first_goto(efreet_menu_kde_legacy_dirs);
1975 while ((dir = ecore_list_next(efreet_menu_kde_legacy_dirs)))
1977 Efreet_Menu_Internal *kde;
1979 kde = efreet_menu_handle_legacy_dir_helper(NULL, parent, dir, "kde");
1982 efreet_menu_concatenate(parent, kde);
1983 efreet_menu_internal_free(kde);
1993 * @param parent: The parent menu
1994 * @param xml: The xml tree
1995 * @return Returns 1 on success or 0 on failure
1996 * @brief Handles the Move tag and all subtags
1999 efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2003 if (!parent || !xml) return 0;
2005 efreet_menu_create_move_list(parent);
2007 ecore_list_first_goto(xml->children);
2008 while ((child = ecore_list_next(xml->children)))
2010 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
2012 cb = ecore_hash_get(efreet_menu_move_cbs, child->tag);
2015 if (!cb(parent, child))
2020 printf("efreet_menu_handle_move() unknown tag found "
2021 "in Move (%s)\n", child->tag);
2026 parent->current_move = NULL;
2033 * @param parent: The parent menu
2034 * @param xml: The xml tree
2035 * @return Returns 1 on success or 0 on failure
2036 * @brief Handles the Old tag
2039 efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2041 Efreet_Menu_Move *move;
2043 if (!parent || !xml || !xml->text) return 0;
2045 if (parent->current_move)
2047 printf("efreet_menu_handle_old() saw second <Old> "
2048 "before seeing <New>\n");
2052 /* If we already moved this menu, remove the old move */
2053 /* XXX This seems wrong, but it makes efreet pass the fdo tests */
2055 move = ecore_list_find(parent->moves,
2056 ECORE_COMPARE_CB(efreet_menu_cb_move_compare), xml->text);
2057 if (move) ecore_list_remove_destroy(parent->moves);
2060 move = efreet_menu_move_new();
2061 move->old_name = strdup(xml->text);
2063 parent->current_move = move;
2064 ecore_list_append(parent->moves, move);
2071 * @param parent: The parent menu
2072 * @param xml: The xml tree
2073 * @return Returns 1 on success or 0 on failure
2074 * @brief Handles the New tag
2077 efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2079 if (!parent || !xml || !xml->text) return 0;
2081 if (!parent->current_move)
2083 printf("efreet_menu_handle_new() saw New before seeing Old\n");
2087 parent->current_move->new_name = strdup(xml->text);
2088 parent->current_move = NULL;
2095 * @param parent: The parent menu
2096 * @param xml: The xml tree
2097 * @return Returns 1 on success or 0 on failure
2098 * @brief Handles the Layout tag and all subtags
2101 efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2105 if (!parent || !xml) return 0;
2107 /* We use the last existing layout */
2108 if (parent->layout) return 1;
2110 efreet_menu_create_layout_list(parent);
2112 ecore_list_first_goto(xml->children);
2113 while ((child = ecore_list_next(xml->children)))
2115 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
2117 cb = ecore_hash_get(efreet_menu_layout_cbs, child->tag);
2120 if (!cb(parent, child, 0))
2125 printf("efreet_menu_handle_move() unknown tag found "
2126 "in Layout (%s)\n", child->tag);
2136 * @param parent: The parent menu
2137 * @param xml: The xml tree
2138 * @return Returns 1 on success or 0 on failure
2139 * @brief Handles the DefaultLayout tag
2142 efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2147 if (!parent || !xml) return 0;
2149 /* We use the last existing layout */
2150 if (parent->default_layout) return 1;
2152 val = efreet_xml_attribute_get(xml, "show_empty");
2153 if (val) parent->show_empty = !strcmp(val, "true");
2155 val = efreet_xml_attribute_get(xml, "inline");
2156 if (val) parent->in_line = !strcmp(val, "true");
2158 val = efreet_xml_attribute_get(xml, "inline_limit");
2159 if (val) parent->inline_limit = atoi(val);
2161 val = efreet_xml_attribute_get(xml, "inline_header");
2162 if (val) parent->inline_header = !strcmp(val, "true");
2164 val = efreet_xml_attribute_get(xml, "inline_alias");
2165 if (val) parent->inline_alias = !strcmp(val, "true");
2167 efreet_menu_create_default_layout_list(parent);
2169 ecore_list_first_goto(xml->children);
2170 while ((child = ecore_list_next(xml->children)))
2172 int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
2174 cb = ecore_hash_get(efreet_menu_layout_cbs, child->tag);
2177 if (!cb(parent, child, 1))
2182 printf("efreet_menu_handle_move() unknown tag found in "
2183 "DefaultLayout (%s)\n", child->tag);
2192 efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2194 Efreet_Menu_Layout *layout;
2197 if (!parent || !xml) return 0;
2201 printf("efreet_menu_handle_layout_menuname() The Menuname tag in "
2202 "layout needs a filename.\n");
2206 layout = efreet_menu_layout_new();
2207 layout->type = EFREET_MENU_LAYOUT_MENUNAME;
2208 layout->name = strdup(xml->text);
2210 val = efreet_xml_attribute_get(xml, "show_empty");
2211 if (val) layout->show_empty = !strcmp(val, "true");
2213 val = efreet_xml_attribute_get(xml, "inline");
2214 if (val) layout->in_line = !strcmp(val, "true");
2216 val = efreet_xml_attribute_get(xml, "inline_limit");
2217 if (val) layout->inline_limit = atoi(val);
2219 val = efreet_xml_attribute_get(xml, "inline_header");
2220 if (val) layout->inline_header = !strcmp(val, "true");
2222 val = efreet_xml_attribute_get(xml, "inline_alias");
2223 if (val) layout->inline_alias = !strcmp(val, "true");
2225 if (def) ecore_list_append(parent->default_layout, layout);
2226 else ecore_list_append(parent->layout, layout);
2232 efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2234 Efreet_Menu_Layout *layout;
2236 if (!parent || !xml) return 0;
2240 printf("efreet_menu_handle_layout_filename() The Filename tag in "
2241 "layout needs a filename.\n");
2245 layout = efreet_menu_layout_new();
2246 layout->type = EFREET_MENU_LAYOUT_FILENAME;
2247 layout->name = strdup(xml->text);
2249 if (def) ecore_list_append(parent->default_layout, layout);
2250 else ecore_list_append(parent->layout, layout);
2256 efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2258 Efreet_Menu_Layout *layout;
2260 if (!parent || !xml) return 0;
2262 layout = efreet_menu_layout_new();
2263 layout->type = EFREET_MENU_LAYOUT_SEPARATOR;
2265 ecore_list_append(parent->default_layout, layout);
2267 ecore_list_append(parent->layout, layout);
2272 efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2274 Efreet_Menu_Layout *layout;
2277 if (!parent || !xml) return 0;
2279 attr = efreet_xml_attribute_get(xml, "type");
2282 printf("efreet_menu_handle_layout_merge() The Merge tag in layout "
2283 "needs a type attribute.\n");
2287 if (strcmp(attr, "files") && strcmp(attr, "menus") && strcmp(attr, "all"))
2289 printf("efreet_menu_handle_layout_merge() The type attribute for "
2290 "the Merge tag contains an unknown value (%s).\n", attr);
2294 layout = efreet_menu_layout_new();
2295 layout->type = EFREET_MENU_LAYOUT_MERGE;
2296 layout->name = strdup(attr);
2298 if (def) ecore_list_append(parent->default_layout, layout);
2299 else ecore_list_append(parent->layout, layout);
2306 * @param parent: The parent menu
2307 * @param xml: The XML tree to parse
2308 * @param type: The type of filter
2309 * @return Returns 1 on success or 0 on failure
2310 * @brief Parses the given XML tree and adds the filter to the parent menu
2313 efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml,
2314 Efreet_Menu_Filter_Type type)
2316 Efreet_Menu_Filter *filter;
2318 efreet_menu_create_filter_list(parent);
2320 /* filters have a default or relationship */
2321 filter = efreet_menu_filter_new();
2322 filter->type = type;
2323 filter->op->type = EFREET_MENU_FILTER_OP_OR;
2325 if (!efreet_menu_handle_filter_op(filter->op, xml))
2327 efreet_menu_filter_free(filter);
2331 ecore_list_prepend(parent->filters, filter);
2338 * @param op: The operation to work with
2339 * @param xml: The XML tree representing this operation
2340 * @return Returns 1 on success or 0 on failure
2341 * @brief Parses the given XML tree and populates the operation
2344 efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
2348 ecore_list_first_goto(xml->children);
2349 while ((child = ecore_list_next(xml->children)))
2351 int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
2353 cb = ecore_hash_get(efreet_menu_filter_cbs, child->tag);
2361 printf("efreet_menu_handle_filter_op() unknown tag in filter (%s)\n", child->tag);
2370 * @return Returns a new Efreet_Menu_Filter on success or NULL on failure
2371 * @brief Creates and initializes an Efreet_Menu_Filter object
2373 static Efreet_Menu_Filter *
2374 efreet_menu_filter_new(void)
2376 Efreet_Menu_Filter *filter;
2378 filter = NEW(Efreet_Menu_Filter, 1);
2379 filter->op = efreet_menu_filter_op_new();
2391 * @param filter: The filter to work with
2392 * @return Returns no data
2393 * @brief Frees the given filter and all data
2396 efreet_menu_filter_free(Efreet_Menu_Filter *filter)
2398 if (!filter) return;
2400 if (filter->op) efreet_menu_filter_op_free(filter->op);
2408 * @return Returns a new Efreet_Menu_Layout on success or NULL on failure
2409 * @brief Creates and initializes an Efreet_Menu_Layout object
2411 static Efreet_Menu_Layout *
2412 efreet_menu_layout_new(void)
2414 Efreet_Menu_Layout *layout;
2416 layout = NEW(Efreet_Menu_Layout, 1);
2417 layout->show_empty = -1;
2418 layout->in_line = -1;
2419 layout->inline_limit = -1;
2420 layout->inline_header = -1;
2421 layout->inline_alias = -1;
2428 * @param layout: The layout to work with
2429 * @return Returns no data
2430 * @brief Frees the given layout and all data
2433 efreet_menu_layout_free(Efreet_Menu_Layout *layout)
2435 if (!layout) return;
2437 IF_FREE(layout->name);
2444 * @return Returns a new Efreet_Menu_Filter_Op on success or NULL on failure
2445 * @brief Creates and initializes an Efreet_Menu_Filter_Op structure
2447 static Efreet_Menu_Filter_Op *
2448 efreet_menu_filter_op_new(void)
2450 Efreet_Menu_Filter_Op *op;
2452 op = NEW(Efreet_Menu_Filter_Op, 1);
2459 * @param op: The operation to work with
2460 * @return Returns no value.
2461 * @brief Frees the given operation and all sub data
2464 efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op)
2468 IF_FREE_LIST(op->categories);
2469 IF_FREE_LIST(op->filenames);
2470 IF_FREE_LIST(op->filters);
2477 * @return Returns a new Efreet_Menu_Desktop on success or NULL on failure
2478 * @brief Creates and returns an Efreet_Menu_Desktop
2480 static Efreet_Menu_Desktop *
2481 efreet_menu_desktop_new(void)
2483 Efreet_Menu_Desktop *md;
2485 md = NEW(Efreet_Menu_Desktop, 1);
2492 * @param md: The Efreet_Menu_Desktop to free
2493 * @return Returns no value
2494 * @brief Frees the given structure
2497 efreet_menu_desktop_free(Efreet_Menu_Desktop *md)
2500 if (md->desktop) efreet_desktop_free(md->desktop);
2506 * @return Returns a new Efreet_Menu on success or NULL on failure
2507 * @brief Creates and returns an Efreet_Menu
2509 static Efreet_Menu *
2510 efreet_menu_entry_new(void)
2514 entry = NEW(Efreet_Menu, 1);
2521 * @param entry: The Efreet_Menu to free
2522 * @return Returns no value
2523 * @brief Frees the given structure
2526 efreet_menu_free(Efreet_Menu *entry)
2528 IF_RELEASE(entry->name);
2529 IF_RELEASE(entry->icon);
2530 IF_FREE_LIST(entry->entries);
2531 IF_RELEASE(entry->id);
2532 if (entry->desktop) efreet_desktop_free(entry->desktop);
2538 * @param op: The op to add a child too
2539 * @param xml: The XML tree of the child
2540 * @param type: The type of child to add
2541 * @return Returns 1 on success or 0 on failure
2542 * @brief Parses the given XML tree and populates a new child operation.
2545 efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
2546 Efreet_Menu_Filter_Op_Type type)
2548 Efreet_Menu_Filter_Op *child_op;
2550 child_op = efreet_menu_filter_op_new();
2551 child_op->type = type;
2553 if (!efreet_menu_handle_filter_op(child_op, xml))
2555 efreet_menu_filter_op_free(child_op);
2561 op->filters = ecore_list_new();
2562 ecore_list_free_cb_set(op->filters,
2563 ECORE_FREE_CB(efreet_menu_filter_op_free));
2566 ecore_list_append(op->filters, child_op);
2573 * @param menu: The menu to work with
2574 * @param only_unallocated: Do we only look for unallocated items?
2575 * @return Returns 1 if we've successfully processed the menu, 0 otherwise
2576 * @brief Handles the processing of the menu data to retrieve the .desktop
2577 * files for the menu
2580 efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2582 /* a menu _MUST_ have a name */
2583 if (!internal->name.internal || (internal->name.internal[0] == '\0'))
2586 /* handle filtering out .desktop files as needed. This deals with all
2588 efreet_menu_process_filters(internal, only_unallocated);
2590 if (internal->sub_menus)
2592 Efreet_Menu_Internal *sub_internal;
2594 ecore_list_first_goto(internal->sub_menus);
2595 while ((sub_internal = ecore_list_next(internal->sub_menus)))
2597 sub_internal->parent = internal;
2598 efreet_menu_process(sub_internal, only_unallocated);
2605 /* This will walk through all of the app dirs and load all the .desktop
2606 * files into the cache for the menu. The .desktop files will have their
2607 * allocated flag set to 0 */
2609 efreet_menu_process_dirs(Efreet_Menu_Internal *internal)
2611 /* Scan application directories for .desktop files */
2612 if (!efreet_menu_app_dirs_process(internal))
2615 /* Scan directory directories for .directory file */
2616 if (!efreet_menu_directory_dirs_process(internal))
2619 if (internal->sub_menus)
2621 Efreet_Menu_Internal *sub_internal;
2623 ecore_list_first_goto(internal->sub_menus);
2624 while ((sub_internal = ecore_list_next(internal->sub_menus)))
2626 sub_internal->parent = internal;
2627 efreet_menu_process_dirs(sub_internal);
2636 * @param menu: the menu to process
2637 * @param only_unallocated: Only handle menus taht deal with unallocated items
2638 * @return Returns no value
2639 * @brief Handles the processing of the filters attached to the given menu.
2641 * For each include filter we'll add the items to our applications array. Each
2642 * exclude filter will remove items from the applications array
2645 efreet_menu_process_filters(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2647 Efreet_Menu_Filter *filter;
2650 /* nothing to do if we're checking the other option */
2651 if (only_unallocated != internal->only_unallocated) return;
2653 if (!internal->applications) internal->applications = ecore_list_new();
2654 else ecore_list_clear(internal->applications);
2656 if (!internal->filters) return;
2658 ecore_list_first_goto(internal->filters);
2659 while ((filter = ecore_list_next(internal->filters)))
2661 Efreet_Menu_Desktop *md;
2663 /* skip excludes until we get an include */
2664 if (!included && (filter->type == EFREET_MENU_FILTER_EXCLUDE))
2668 if (filter->type == EFREET_MENU_FILTER_INCLUDE)
2670 Ecore_Hash *matches;
2672 matches = ecore_hash_new(ecore_str_hash, ecore_str_compare);
2673 efreet_menu_process_app_pool(internal->app_pool, internal->applications,
2674 matches, filter, internal->only_unallocated);
2675 if (internal->parent)
2677 Efreet_Menu_Internal *parent;
2679 parent = internal->parent;
2681 efreet_menu_process_app_pool(parent->app_pool,
2682 internal->applications, matches, filter,
2683 internal->only_unallocated);
2684 } while ((parent = parent->parent));
2686 ecore_hash_destroy(matches);
2690 /* check each item in our menu so far and see if it's excluded */
2691 ecore_list_first_goto(internal->applications);
2692 while ((md = ecore_list_current(internal->applications)))
2694 if (efreet_menu_filter_matches(filter->op, md))
2695 ecore_list_remove(internal->applications);
2697 ecore_list_next(internal->applications);
2702 /* sort the menu applications. we do this in process filters so it will only
2703 * be done once per menu.*/
2704 if (internal->applications)
2708 count = ecore_list_count(internal->applications);
2712 Efreet_Menu_Desktop *md;
2714 sheap = ecore_sheap_new(
2715 ECORE_COMPARE_CB(efreet_menu_cb_md_compare), count);
2716 while ((md = ecore_list_first_remove(internal->applications)))
2717 ecore_sheap_insert(sheap, md);
2719 while ((md = ecore_sheap_extract(sheap)))
2721 if (md->desktop->no_display) continue;
2722 ecore_list_append(internal->applications, md);
2725 ecore_sheap_destroy(sheap);
2728 /* Don't keep the list if it is empty */
2729 if (ecore_list_empty_is(internal->applications))
2730 IF_FREE_LIST(internal->applications);
2736 * @param pool: The app pool to iterate
2737 * @param applications: The list of applications to append too
2738 * @param matches: The hash of previously matched ids
2739 * @param filter: The menu filter to run on the pool items
2740 * @param only_unallocated: Do we check only unallocated pool items?
2741 * @return Returns no value.
2742 * @brief This will iterate the items in @a pool and append them to @a
2743 * applications if they match the @a filter given and aren't previoulsy entered
2744 * in @a matches. If @a only_unallocated is set we'll only only at the
2745 * .desktop files that haven't been previoulsy matched
2748 void efreet_menu_process_app_pool(Ecore_List *pool, Ecore_List *applications,
2749 Ecore_Hash *matches,
2750 Efreet_Menu_Filter *filter,
2751 unsigned int only_unallocated)
2753 Efreet_Menu_Desktop *md;
2757 ecore_list_first_goto(pool);
2758 while ((md = ecore_list_next(pool)))
2760 if (ecore_hash_get(matches, md->id)) continue;
2761 if (only_unallocated && md->allocated) continue;
2762 if (efreet_menu_filter_matches(filter->op, md))
2764 ecore_list_append(applications, md);
2765 ecore_hash_set(matches, (void *)md->id, md);
2773 * @param op: The filter operation to execute
2774 * @param md: The desktop to run the filter on
2775 * @return Returns 1 if this desktop matches the given filter, 0 otherwise
2776 * @brief This will execute the given @a filter on the given desktop
2779 efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2781 if (op->type == EFREET_MENU_FILTER_OP_OR)
2782 return efreet_menu_filter_or_matches(op, md);
2784 if (op->type == EFREET_MENU_FILTER_OP_AND)
2785 return efreet_menu_filter_and_matches(op, md);
2787 if (op->type == EFREET_MENU_FILTER_OP_NOT)
2788 return efreet_menu_filter_not_matches(op, md);
2795 * @param op: The filter operation to execute
2796 * @param md: The desktop to execute on
2797 * @return Returns 1 if the desktop matches, 0 otherwise
2798 * @brief Executes the OR operation, @a op, on the desktop, @a md.
2801 efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2803 Efreet_Menu_Filter_Op *child;
2806 if (op->all) return 1;
2808 if (op->categories && md->desktop->categories)
2810 ecore_list_first_goto(op->categories);
2811 while ((t = ecore_list_next(op->categories)))
2813 if (ecore_list_find(md->desktop->categories, ECORE_COMPARE_CB(strcmp), t))
2820 ecore_list_first_goto(op->filenames);
2821 while ((t = ecore_list_next(op->filenames)))
2822 if (!strcmp(t, md->id)) return 1;
2827 ecore_list_first_goto(op->filters);
2828 while ((child = ecore_list_next(op->filters)))
2830 if (efreet_menu_filter_matches(child, md))
2840 * @param op: The filter operation to execute
2841 * @param md: The desktop to execute on
2842 * @return Returns 1 if the desktop matches, 0 otherwise
2843 * @brief Executes the AND operation, @a op, on the desktop, @a md.
2846 efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2848 Efreet_Menu_Filter_Op *child;
2853 if ((ecore_list_count(op->categories) > 0) && !md->desktop->categories)
2856 ecore_list_first_goto(op->categories);
2857 while ((t = ecore_list_next(op->categories)))
2859 if (!ecore_list_find(md->desktop->categories, ECORE_COMPARE_CB(strcmp), t))
2866 ecore_list_first_goto(op->filenames);
2867 while ((t = ecore_list_next(op->filenames)))
2869 if (strcmp(t, md->id)) return 0;
2875 ecore_list_first_goto(op->filters);
2876 while ((child = ecore_list_next(op->filters)))
2878 if (!efreet_menu_filter_matches(child, md))
2888 * @param op: The filter operation to execute
2889 * @param md: The desktop to execute on
2890 * @return Returns 1 if the desktop matches, 0 otherwise
2891 * @brief Executes the NOT operation, @a op, on the desktop, @a md.
2894 efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2896 Efreet_Menu_Filter_Op *child;
2899 /* !all means no desktops match */
2900 if (op->all) return 0;
2904 if ((ecore_list_count(op->categories) > 0) && !md->desktop->categories)
2907 ecore_list_first_goto(op->categories);
2908 while ((t = ecore_list_next(op->categories)))
2910 if (ecore_list_find(md->desktop->categories, ECORE_COMPARE_CB(strcmp), t))
2917 ecore_list_first_goto(op->filenames);
2918 while ((t = ecore_list_next(op->filenames)))
2920 if (!strcmp(t, md->id)) return 0;
2926 ecore_list_first_goto(op->filters);
2927 while ((child = ecore_list_next(op->filters)))
2929 if (efreet_menu_filter_matches(child, md))
2939 * @param dest: The destination menu
2940 * @param src: The source menu
2941 * @return Returns no value
2942 * @brief Takes the child elements of the menu @a src and puts then on the
2943 * _start_ of the menu @a dest.
2946 efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src)
2948 Efreet_Menu_Internal *submenu;
2950 if (!dest || !src) return;
2952 if (!dest->directory && src->directory)
2954 dest->directory = src->directory;
2955 src->directory = NULL;
2958 if (!dest->seen_allocated && src->seen_allocated)
2960 dest->only_unallocated = src->only_unallocated;
2961 dest->seen_allocated = 1;
2964 if (!dest->seen_deleted && src->seen_deleted)
2966 dest->deleted = src->deleted;
2967 dest->seen_deleted = 1;
2970 if (src->directories)
2972 efreet_menu_create_directories_list(dest);
2973 ecore_dlist_prepend_list(dest->directories, src->directories);
2978 efreet_menu_create_app_dirs_list(dest);
2979 ecore_list_prepend_list(dest->app_dirs, src->app_dirs);
2982 if (src->directory_dirs)
2984 efreet_menu_create_directory_dirs_list(dest);
2985 ecore_dlist_prepend_list(dest->directory_dirs, src->directory_dirs);
2990 efreet_menu_create_move_list(dest);
2991 ecore_list_prepend_list(dest->moves, src->moves);
2996 efreet_menu_create_filter_list(dest);
2997 ecore_list_prepend_list(dest->filters, src->filters);
3002 efreet_menu_create_sub_menu_list(dest);
3004 while ((submenu = ecore_list_last_remove(src->sub_menus)))
3006 Efreet_Menu_Internal *match;
3008 /* if this menu is in the list already we just add to that */
3009 if ((match = ecore_list_find(dest->sub_menus,
3010 ECORE_COMPARE_CB(efreet_menu_cb_menu_compare), submenu)))
3012 efreet_menu_concatenate(match, submenu);
3013 efreet_menu_internal_free(submenu);
3016 ecore_list_prepend(dest->sub_menus, submenu);
3023 * @param menu: The menu to work with
3024 * @return Returns no value
3025 * @brief Handles any \<Move\> commands in the menus
3028 efreet_menu_resolve_moves(Efreet_Menu_Internal *internal)
3030 Efreet_Menu_Internal *child;
3031 Efreet_Menu_Move *move;
3033 /* child moves are handled before parent moves */
3034 if (internal->sub_menus)
3036 ecore_list_first_goto(internal->sub_menus);
3037 while ((child = ecore_list_next(internal->sub_menus)))
3038 efreet_menu_resolve_moves(child);
3041 /* nothing to do if this menu has no moves */
3042 if (!internal->moves) return;
3044 ecore_list_first_goto(internal->moves);
3045 while ((move = ecore_list_next(internal->moves)))
3047 Efreet_Menu_Internal *origin, *dest, *parent;
3049 /* if the origin path doesn't exist we do nothing */
3050 origin = efreet_menu_by_name_find(internal, move->old_name, &parent);
3051 if (!origin) continue;
3053 /* remove the origin menu from the parent */
3054 ecore_list_remove(parent->sub_menus);
3056 /* if the destination path doesn't exist we just rename the origin
3057 * menu and append to the parents list of children */
3058 dest = efreet_menu_by_name_find(internal, move->new_name, &parent);
3061 char *path, *tmp, *t;
3063 /* if the dest path has /'s in it then we need to add menus to
3064 * fill out the paths */
3065 t = strdup(move->new_name);
3067 path = strchr(tmp, '/');
3070 Efreet_Menu_Internal *ancestor;
3074 ancestor = efreet_menu_internal_new();
3075 ancestor->name.internal = eina_stringshare_add(tmp);
3077 efreet_menu_create_sub_menu_list(parent);
3078 ecore_list_append(parent->sub_menus, ancestor);
3082 path = strchr(tmp, '/');
3085 IF_RELEASE(origin->name.internal);
3086 origin->name.internal = eina_stringshare_add(tmp);
3088 efreet_menu_create_sub_menu_list(parent);
3089 ecore_list_append(parent->sub_menus, origin);
3095 efreet_menu_concatenate(dest, origin);
3096 efreet_menu_internal_free(origin);
3099 IF_FREE_LIST(internal->moves);
3104 * @param menu: The menu to start searching from
3105 * @param name: The menu name to find
3106 * @param parent: The parent of the found menu
3107 * @return Returns the menu with the given @a name or NULL if none found
3108 * @brief Searches the menu tree starting at @a menu looking for a menu with
3111 static Efreet_Menu_Internal *
3112 efreet_menu_by_name_find(Efreet_Menu_Internal *internal, const char *name, Efreet_Menu_Internal **parent)
3114 char *part, *tmp, *ptr;
3116 if (parent) *parent = internal;
3118 /* find the correct parent menu */
3121 part = strchr(ptr, '/');
3126 if (!ecore_list_find(internal->sub_menus,
3127 ECORE_COMPARE_CB(efreet_menu_cb_compare_names), ptr))
3133 internal = ecore_list_current(internal->sub_menus);
3135 part = strchr(ptr, '/');
3138 if (parent) *parent = internal;
3140 /* find the menu in the parent list */
3141 if (!ecore_list_find(internal->sub_menus,
3142 ECORE_COMPARE_CB(efreet_menu_cb_compare_names), ptr))
3149 return ecore_list_current(internal->sub_menus);
3153 efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path)
3158 p = strrchr(tmp, '/');
3164 internal->file.path = strdup(tmp);
3165 internal->file.name = strdup(p);
3172 * @return Returns a new Efreet_Menu_Move struct on success or NULL on failure
3173 * @brief Creates an returns a new Efreet_Menu_Move struct or NULL on failure
3175 static Efreet_Menu_Move *
3176 efreet_menu_move_new(void)
3178 Efreet_Menu_Move *move;
3180 move = NEW(Efreet_Menu_Move, 1);
3187 * @param move: The Efreet_Menu_Move to free
3188 * @return Returns no value.
3189 * @brief Frees the given move structure
3192 efreet_menu_move_free(Efreet_Menu_Move *move)
3196 IF_FREE(move->old_name);
3197 IF_FREE(move->new_name);
3204 * @return Returns a new Efreet_Menu_App_Dir on success or NULL on failure
3205 * @brief Creates and initializes a new Efreet_Menu_App_Dir structure
3207 static Efreet_Menu_App_Dir *
3208 efreet_menu_app_dir_new(void)
3210 Efreet_Menu_App_Dir *dir;
3212 dir = NEW(Efreet_Menu_App_Dir, 1);
3219 * @param dir: The app dir to free
3220 * @return Returns no value
3221 * @brief Frees @a dir
3224 efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir)
3229 IF_FREE(dir->prefix);
3236 * @param a: The app dir to compare too
3237 * @param b: The path to compare too
3238 * @return Returns 1 if the strings are equals, 0 otherwise
3239 * @brief Compares the too strings
3242 efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b)
3244 return ecore_str_compare(a->path, b);
3248 efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal)
3250 if (!internal || internal->sub_menus) return;
3252 internal->sub_menus = ecore_list_new();
3253 ecore_list_free_cb_set(internal->sub_menus,
3254 ECORE_FREE_CB(efreet_menu_internal_free));
3258 efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal)
3260 if (!internal || internal->app_dirs) return;
3262 internal->app_dirs = ecore_list_new();
3263 ecore_list_free_cb_set(internal->app_dirs,
3264 ECORE_FREE_CB(efreet_menu_app_dir_free));
3268 efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal)
3270 if (!internal || internal->directory_dirs) return;
3272 internal->directory_dirs = ecore_dlist_new();
3273 ecore_list_free_cb_set(internal->directory_dirs, ECORE_FREE_CB(free));
3277 efreet_menu_create_move_list(Efreet_Menu_Internal *internal)
3279 if (!internal || internal->moves) return;
3281 internal->moves = ecore_list_new();
3282 ecore_list_free_cb_set(internal->moves, ECORE_FREE_CB(efreet_menu_move_free));
3286 efreet_menu_create_filter_list(Efreet_Menu_Internal *internal)
3288 if (!internal || internal->filters) return;
3290 internal->filters = ecore_list_new();
3291 ecore_list_free_cb_set(internal->filters,
3292 ECORE_FREE_CB(efreet_menu_filter_free));
3296 efreet_menu_create_layout_list(Efreet_Menu_Internal *internal)
3298 if (!internal || internal->layout) return;
3300 internal->layout = ecore_list_new();
3301 ecore_list_free_cb_set(internal->layout,
3302 ECORE_FREE_CB(efreet_menu_layout_free));
3306 efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal)
3308 if (!internal || internal->default_layout) return;
3310 internal->default_layout = ecore_list_new();
3311 ecore_list_free_cb_set(internal->default_layout,
3312 ECORE_FREE_CB(efreet_menu_layout_free));
3316 efreet_menu_create_directories_list(Efreet_Menu_Internal *internal)
3318 if (!internal || internal->directories) return;
3320 internal->directories = ecore_dlist_new();
3321 ecore_list_free_cb_set(internal->directories, free);
3325 efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix)
3327 char path[PATH_MAX];
3330 /* see if we've got an absolute or relative path */
3331 if (suffix[0] == '/')
3332 snprintf(path, sizeof(path), "%s", suffix);
3336 if (!internal->file.path)
3338 printf("efreet_menu_handle_app_dir() missing menu path ...\n");
3341 snprintf(path, sizeof(path), "%s/%s", internal->file.path, suffix);
3345 while (path[len] == '/') path[len--] = '\0';
3347 return strdup(path);
3351 efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b)
3353 return ecore_str_compare(a->name.internal, b->name.internal);
3357 efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal)
3359 Efreet_Menu_App_Dir *app_dir;
3361 if (internal->app_pool)
3363 ecore_list_destroy(internal->app_pool);
3364 internal->app_pool = NULL;
3367 if (internal->app_dirs)
3369 internal->app_pool = ecore_list_new();
3370 ecore_list_free_cb_set(internal->app_pool, ECORE_FREE_CB(efreet_menu_desktop_free));
3372 ecore_list_first_goto(internal->app_dirs);
3373 while ((app_dir = ecore_list_next(internal->app_dirs)))
3374 efreet_menu_app_dir_scan(internal, app_dir->path, app_dir->prefix, app_dir->legacy);
3380 efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal, const char *path, const char *id, int legacy)
3382 Efreet_Desktop *desktop;
3383 Efreet_Menu_Desktop *menu_desktop;
3385 char buf[PATH_MAX], buf2[PATH_MAX];
3386 struct dirent *file;
3389 files = opendir(path);
3390 if (!files) return 1;
3392 while ((file = readdir(files)))
3394 if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
3395 snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name);
3397 snprintf(buf2, PATH_MAX, "%s-%s", id, file->d_name);
3399 strcpy(buf2, file->d_name);
3401 if (ecore_file_is_dir(buf))
3404 efreet_menu_app_dir_scan(internal, buf, buf2, legacy);
3408 ext = strrchr(buf, '.');
3410 if (!ext || strcmp(ext, ".desktop")) continue;
3411 desktop = efreet_desktop_get(buf);
3413 if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_APPLICATION)
3415 if (desktop) efreet_desktop_free(desktop);
3418 /* Don't add two files with the same id in the app pool */
3419 if (ecore_list_find(internal->app_pool,
3420 ECORE_COMPARE_CB(efreet_menu_cb_md_compare_ids), buf2)) continue;
3422 menu_desktop = efreet_menu_desktop_new();
3423 menu_desktop->desktop = desktop;
3424 menu_desktop->id = eina_stringshare_add(buf2);
3425 ecore_list_prepend(internal->app_pool, menu_desktop);
3435 * @param menu: The menu to work with
3436 * @return Returns 1 on success or 0 on failure
3437 * @brief Process the directory dirs in @a menu
3440 efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal)
3444 if (internal->directory_dirs)
3446 internal->directory_cache = ecore_hash_new(ecore_str_hash, ecore_str_compare);
3447 ecore_hash_free_key_cb_set(internal->directory_cache, ECORE_FREE_CB(free));
3448 ecore_hash_free_value_cb_set(internal->directory_cache, ECORE_FREE_CB(efreet_desktop_free));
3450 ecore_dlist_last_goto(internal->directory_dirs);
3451 while ((path = ecore_dlist_previous(internal->directory_dirs)))
3452 efreet_menu_directory_dir_scan(path, NULL, internal->directory_cache);
3455 if (internal->directories)
3457 ecore_dlist_last_goto(internal->directories);
3458 while ((path = ecore_dlist_previous(internal->directories)))
3460 internal->directory = efreet_menu_directory_get(internal, path);
3461 if (internal->directory) break;
3464 if (!internal->directory)
3465 internal->name.name = internal->name.internal;
3467 internal->name.name = internal->directory->name;
3474 * @param path: The path to scan
3475 * @param relative_path: The relative portion of the path
3476 * @param cache: The cache to populate
3477 * @return Returns 1 on success or 0 on failure
3478 * @brief Scans the given directory dir for .directory files and adds the
3479 * applications to the cache
3482 efreet_menu_directory_dir_scan(const char *path, const char *relative_path,
3485 Efreet_Desktop *desktop;
3487 char buf[PATH_MAX], buf2[PATH_MAX];
3488 struct dirent *file;
3491 files = opendir(path);
3492 if (!files) return 1;
3494 while ((file = readdir(files)))
3496 if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
3497 snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name);
3499 snprintf(buf2, PATH_MAX, "%s/%s", relative_path, file->d_name);
3501 strcpy(buf2, file->d_name);
3503 if (ecore_file_is_dir(buf))
3504 efreet_menu_directory_dir_scan(buf, buf2, cache);
3508 ext = strrchr(buf, '.');
3509 if (!ext || strcmp(ext, ".directory")) continue;
3511 desktop = efreet_desktop_get(buf);
3512 if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_DIRECTORY)
3514 efreet_desktop_free(desktop);
3518 ecore_hash_set(cache, (void *)strdup(buf2), desktop);
3528 * @param menu: The menu to work with
3529 * @param path: The path to work with
3530 * @return Returns the desktop file for this path or NULL if none exists
3531 * @brief Finds the desktop file for the given path.
3533 static Efreet_Desktop *
3534 efreet_menu_directory_get(Efreet_Menu_Internal *internal, const char *path)
3536 Efreet_Desktop *dir;
3538 if (internal->directory_cache)
3540 dir = ecore_hash_get(internal->directory_cache, path);
3541 if (dir) return dir;
3544 if (internal->parent)
3545 return efreet_menu_directory_get(internal->parent, path);
3552 * @param a: The first desktop
3553 * @param b: The second desktop
3554 * @return Returns the comparison of the desktop files
3555 * @brief Compares the desktop files.
3558 efreet_menu_cb_md_compare(Efreet_Menu_Desktop *a, Efreet_Menu_Desktop *b)
3561 return strcmp(ecore_file_file_get(a->desktop->orig_path), ecore_file_file_get(b->desktop->orig_path));
3563 return strcasecmp(a->desktop->name, b->desktop->name);
3568 efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name)
3570 return strcmp(internal->name.internal, name);
3574 efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name)
3576 return strcmp(md->id, name);
3579 static Efreet_Menu *
3580 efreet_menu_layout_menu(Efreet_Menu_Internal *internal)
3583 Ecore_List *layout = NULL;
3585 if (internal->parent)
3587 /* Copy default layout rules */
3588 if (internal->show_empty == -1) internal->show_empty = internal->parent->show_empty;
3589 if (internal->in_line == -1) internal->in_line = internal->parent->in_line;
3590 if (internal->inline_limit == -1) internal->inline_limit = internal->parent->inline_limit;
3591 if (internal->inline_header == -1) internal->inline_header = internal->parent->inline_header;
3592 if (internal->inline_alias == -1) internal->inline_alias = internal->parent->inline_alias;
3595 if (internal->layout)
3596 layout = internal->layout;
3598 else if (internal->parent)
3600 Efreet_Menu_Internal *parent;
3601 parent = internal->parent;
3604 layout = parent->default_layout;
3605 parent = parent->parent;
3606 } while (!layout && parent);
3610 entry = efreet_menu_entry_new();
3611 entry->type = EFREET_MENU_ENTRY_MENU;
3612 entry->id = eina_stringshare_add(internal->name.internal);
3613 entry->name = eina_stringshare_add(internal->name.name);
3614 if (internal->directory)
3616 entry->icon = eina_stringshare_add(internal->directory->icon);
3617 efreet_desktop_ref(internal->directory);
3618 entry->desktop = internal->directory;
3620 entry->entries = ecore_list_new();
3621 ecore_list_free_cb_set(entry->entries, ECORE_FREE_CB(efreet_menu_free));
3624 if (internal->sub_menus)
3626 ecore_list_sort(internal->sub_menus,
3627 ECORE_COMPARE_CB(efreet_menu_cb_menu_compare), ECORE_SORT_MIN);
3633 Efreet_Menu_Layout *lay;
3635 ecore_list_first_goto(layout);
3636 while ((lay = ecore_list_next(layout)))
3637 efreet_menu_layout_entries_get(entry, internal, lay);
3641 /* Default layout, first menus, then desktop */
3642 if (internal->sub_menus)
3644 Efreet_Menu_Internal *sub;
3646 ecore_list_first_goto(internal->sub_menus);
3647 while ((sub = ecore_list_next(internal->sub_menus)))
3649 Efreet_Menu *sub_entry;
3650 if ((sub->directory && sub->directory->no_display) || sub->deleted) continue;
3651 sub_entry = efreet_menu_layout_menu(sub);
3652 /* Don't show empty menus */
3653 if (!sub_entry->entries)
3655 efreet_menu_free(sub_entry);
3658 ecore_list_append(entry->entries, sub_entry);
3662 if (internal->applications)
3664 Efreet_Menu_Desktop *md;
3666 ecore_list_first_goto(internal->applications);
3667 while ((md = ecore_list_next(internal->applications)))
3669 Efreet_Menu *sub_entry;
3670 sub_entry = efreet_menu_layout_desktop(md);
3671 ecore_list_append(entry->entries, sub_entry);
3676 /* Don't keep this list around if it is empty */
3677 if (ecore_list_empty_is(entry->entries)) IF_FREE_LIST(entry->entries);
3682 static Efreet_Menu *
3683 efreet_menu_layout_desktop(Efreet_Menu_Desktop *md)
3688 entry = efreet_menu_entry_new();
3689 entry->type = EFREET_MENU_ENTRY_DESKTOP;
3690 entry->id = eina_stringshare_add(md->id);
3691 entry->name = eina_stringshare_add(md->desktop->name);
3692 if (md->desktop->icon) entry->icon = eina_stringshare_add(md->desktop->icon);
3693 efreet_desktop_ref(md->desktop);
3694 entry->desktop = md->desktop;
3700 efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal,
3701 Efreet_Menu_Layout *layout)
3703 Efreet_Menu *sub_entry;
3705 if (internal->sub_menus && layout->type == EFREET_MENU_LAYOUT_MENUNAME)
3707 Efreet_Menu_Internal *sub;
3709 /* Efreet_Menu_Layout might be from DefaultLayout, so we need a local copy */
3710 int show_empty, in_line, inline_limit, inline_header, inline_alias;
3712 if (layout->show_empty == -1) show_empty = internal->show_empty;
3713 else show_empty = layout->show_empty;
3715 if (layout->in_line == -1) in_line = internal->in_line;
3716 else in_line = layout->in_line;
3718 if (layout->inline_limit == -1) inline_limit = internal->inline_limit;
3719 else inline_limit = layout->inline_limit;
3721 if (layout->inline_header == -1) inline_header = internal->inline_header;
3722 else inline_header = layout->inline_header;
3724 if (layout->inline_alias == -1) inline_alias = internal->inline_alias;
3725 else inline_alias = layout->inline_alias;
3727 sub = ecore_list_find(internal->sub_menus,
3728 ECORE_COMPARE_CB(efreet_menu_cb_compare_names), layout->name);
3731 if (!(sub->directory && sub->directory->no_display) && !sub->deleted)
3733 sub_entry = efreet_menu_layout_menu(sub);
3734 if (!show_empty && efreet_menu_layout_is_empty(sub_entry))
3735 efreet_menu_free(sub_entry);
3737 ((inline_limit == 0) ||
3738 (!sub_entry->entries || (ecore_list_count(sub_entry->entries) <= inline_limit))))
3741 if (!sub_entry->entries)
3743 /* Can't inline an empty submenu */
3744 ecore_list_append(entry->entries, sub_entry);
3746 else if (inline_alias && (ecore_list_count(sub_entry->entries) == 1))
3750 tmp = ecore_list_first_remove(sub_entry->entries);
3751 IF_RELEASE(tmp->name);
3752 tmp->name = sub_entry->name;
3753 sub_entry->name = NULL;
3754 IF_RELEASE(tmp->icon);
3755 tmp->icon = sub_entry->icon;
3756 sub_entry->icon = NULL;
3757 ecore_list_append(entry->entries, tmp);
3758 efreet_menu_free(sub_entry);
3766 tmp = efreet_menu_entry_new();
3767 tmp->type = EFREET_MENU_ENTRY_HEADER;
3768 tmp->name = sub_entry->name;
3769 sub_entry->name = NULL;
3770 tmp->icon = sub_entry->icon;
3771 sub_entry->icon = NULL;
3772 ecore_list_append(entry->entries, tmp);
3774 ecore_list_first_goto(sub_entry->entries);
3775 while ((tmp = ecore_list_first_remove(sub_entry->entries)))
3776 ecore_list_append(entry->entries, tmp);
3777 efreet_menu_free(sub_entry);
3781 ecore_list_append(entry->entries, sub_entry);
3783 ecore_list_remove(internal->sub_menus);
3784 efreet_menu_internal_free(sub);
3786 if (ecore_list_empty_is(internal->sub_menus)) IF_FREE_LIST(internal->sub_menus);
3788 else if (internal->applications && layout->type == EFREET_MENU_LAYOUT_FILENAME)
3790 Efreet_Menu_Desktop *md;
3791 md = ecore_list_find(internal->applications,
3792 ECORE_COMPARE_CB(efreet_menu_cb_md_compare_ids), layout->name);
3795 sub_entry = efreet_menu_layout_desktop(md);
3796 ecore_list_append(entry->entries, sub_entry);
3797 ecore_list_remove(internal->applications);
3799 if (ecore_list_empty_is(internal->applications)) IF_FREE_LIST(internal->applications);
3801 else if (layout->type == EFREET_MENU_LAYOUT_MERGE)
3803 if (internal->applications && !strcmp(layout->name, "files"))
3805 Efreet_Menu_Desktop *md;
3807 ecore_list_first_goto(internal->applications);
3808 while ((md = ecore_list_first_remove(internal->applications)))
3810 sub_entry = ecore_list_find(entry->entries,
3811 ECORE_COMPARE_CB(efreet_menu_cb_entry_compare_desktop), md->desktop);
3814 sub_entry = efreet_menu_layout_desktop(md);
3815 ecore_list_append(entry->entries, sub_entry);
3818 IF_FREE_LIST(internal->applications);
3820 else if (internal->sub_menus && !strcmp(layout->name, "menus"))
3822 Efreet_Menu_Internal *sub;
3824 ecore_list_first_goto(internal->sub_menus);
3825 while ((sub = ecore_list_first_remove(internal->sub_menus)))
3827 if ((sub->directory && sub->directory->no_display) || sub->deleted)
3829 efreet_menu_internal_free(sub);
3832 sub_entry = ecore_list_find(entry->entries,
3833 ECORE_COMPARE_CB(efreet_menu_cb_entry_compare_menu), sub);
3836 sub_entry = efreet_menu_layout_menu(sub);
3837 if (!internal->show_empty && efreet_menu_layout_is_empty(sub_entry))
3838 efreet_menu_free(sub_entry);
3839 else if (internal->in_line &&
3840 ((internal->inline_limit == 0) ||
3841 (!sub_entry->entries || (ecore_list_count(sub_entry->entries) <= internal->inline_limit))))
3844 if (!sub_entry->entries)
3846 /* Can't inline an empty submenu */
3847 ecore_list_append(entry->entries, sub_entry);
3849 else if (internal->inline_alias && (ecore_list_count(sub_entry->entries) == 1))
3853 tmp = ecore_list_first_remove(sub_entry->entries);
3854 eina_stringshare_del(tmp->name);
3855 tmp->name = sub_entry->name;
3856 sub_entry->name = NULL;
3857 IF_RELEASE(tmp->icon);
3858 if (sub_entry->icon)
3860 tmp->icon = sub_entry->icon;
3861 sub_entry->icon = NULL;
3863 ecore_list_append(entry->entries, tmp);
3864 efreet_menu_free(sub_entry);
3870 if (internal->inline_header)
3872 tmp = efreet_menu_entry_new();
3873 tmp->type = EFREET_MENU_ENTRY_HEADER;
3874 tmp->name = sub_entry->name;
3875 sub_entry->name = NULL;
3876 if (sub_entry->icon) tmp->icon = sub_entry->icon;
3877 sub_entry->icon = NULL;
3878 ecore_list_append(entry->entries, tmp);
3880 ecore_list_first_goto(sub_entry->entries);
3881 while ((tmp = ecore_list_first_remove(sub_entry->entries)))
3882 ecore_list_append(entry->entries, tmp);
3883 efreet_menu_free(sub_entry);
3887 ecore_list_append(entry->entries, sub_entry);
3889 efreet_menu_internal_free(sub);
3891 IF_FREE_LIST(internal->sub_menus);
3893 else if (internal->sub_menus && !strcmp(layout->name, "all"))
3897 orig = layout->name;
3898 layout->name = "menus";
3899 efreet_menu_layout_entries_get(entry, internal, layout);
3900 layout->name = "files";
3901 efreet_menu_layout_entries_get(entry, internal, layout);
3902 layout->name = orig;
3905 else if (layout->type == EFREET_MENU_LAYOUT_SEPARATOR)
3907 sub_entry = efreet_menu_entry_new();
3908 sub_entry->type = EFREET_MENU_ENTRY_SEPARATOR;
3909 ecore_list_append(entry->entries, sub_entry);
3914 efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal)
3916 if (entry->type != EFREET_MENU_ENTRY_MENU) return 1;
3917 return ecore_str_compare(entry->name, internal->name.name);
3921 efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop)
3923 if (entry->type != EFREET_MENU_ENTRY_DESKTOP) return -1;
3924 return ecore_str_compare(entry->name, desktop->name);
3928 efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old)
3930 return ecore_str_compare(move->old_name, old);
3934 efreet_menu_layout_is_empty(Efreet_Menu *entry)
3936 Efreet_Menu *sub_entry;
3938 if (!entry->entries) return 1;
3940 ecore_list_first_goto(entry->entries);
3941 while ((sub_entry = ecore_list_next(entry->entries)))
3943 if (sub_entry->type == EFREET_MENU_ENTRY_MENU) return 0;
3944 if (sub_entry->type == EFREET_MENU_ENTRY_DESKTOP) return 0;