more leak fixes
[framework/uifw/efreet.git] / src / lib / efreet_menu.c
1 /* vim: set sw=4 ts=4 sts=4 et: */
2 #include "Efreet.h"
3 #include "efreet_private.h"
4
5 /**
6  * Efreet_Menu_Move
7  */
8 typedef struct Efreet_Menu_Move Efreet_Menu_Move;
9
10 /**
11  * Efreet_Menu_Move
12  * Info on a menu movement
13  */
14 struct Efreet_Menu_Move
15 {
16     char *old_name;     /**< The menu path to move from */
17     char *new_name;     /**< The menu path to move too */
18 };
19
20 /**
21  * Efreet_Menu_Internal
22  */
23 typedef struct Efreet_Menu_Internal Efreet_Menu_Internal;
24
25 /**
26  * Efreet_Menu_Internal
27  * Contains the information about a menu
28  */
29 struct Efreet_Menu_Internal
30 {
31     struct
32     {
33         char *path;         /**< The base file path */
34         char *name;         /**< The filename for this menu */
35     } file;                 /**< The menu file information */
36
37     struct
38     {
39         const char *internal;     /**< The menu name */
40         const char *name;         /**< Name to use in the menus */
41     } name;                       /**< The names for this menu */
42
43     Efreet_Desktop *directory; /**< The directory */
44     Ecore_DList *directories;  /**< All the directories set in the menu file */
45
46     Efreet_Menu_Move *current_move; /**< The current move */
47
48     Eina_List *app_dirs;           /**< .desktop application directories */
49
50     Eina_List *app_pool;           /**< application pool */
51     Eina_List *applications;       /**< applications in this menu */
52
53     Ecore_DList *directory_dirs;    /**< .directory file directories */
54     Eina_Hash *directory_cache;    /**< .directory dirs */
55
56     Eina_List *moves;              /**< List of moves to be handled by the menu */
57     Eina_List *filters;            /**< Include and Exclude filters */
58
59     Efreet_Menu_Internal *parent;   /**< Our parent menu */
60     Eina_List *sub_menus;          /**< Our sub menus */
61
62     Eina_List *layout;             /**< This menus layout */
63     Eina_List *default_layout;     /**< Default layout */
64     signed char show_empty;    /**< Whether to show empty menus */
65     signed char in_line;       /**< Whether this meny can be inlined */
66     signed char inline_limit;  /**< Number of elements which triggers inline */
67     signed char inline_header; /**< Whether we should use the header name when this menu is inlined */
68     signed char inline_alias;  /**< Whether we should use the menu name when inlining */
69
70     unsigned char seen_allocated:1;     /**< have we set the only_unallocated */
71     unsigned char only_unallocated:1;   /**< Show only unallocated .desktops */
72
73     unsigned char seen_deleted:1;       /**< Have we seen the deleted item yet */
74     unsigned char deleted:1;            /**< The menu is deleted */
75 };
76
77 /**
78  * Efreet_Menu_App_Dir
79  */
80 typedef struct Efreet_Menu_App_Dir Efreet_Menu_App_Dir;
81
82 /**
83  * Holds information on an app dir
84  */
85 struct Efreet_Menu_App_Dir
86 {
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 */
90 };
91
92 /**
93  * The type of operations we can perform with a filter
94  */
95 enum Efreet_Menu_Filter_Op_Type
96 {
97     EFREET_MENU_FILTER_OP_OR,
98     EFREET_MENU_FILTER_OP_AND,
99     EFREET_MENU_FILTER_OP_NOT
100 };
101
102 /**
103  * Efreet_Menu_Filter_Op_Type
104  */
105 typedef enum Efreet_Menu_Filter_Op_Type Efreet_Menu_Filter_Op_Type;
106
107 /**
108  * The type of filter
109  */
110 enum Efreet_Menu_Filter_Type
111 {
112     EFREET_MENU_FILTER_INCLUDE,
113     EFREET_MENU_FILTER_EXCLUDE
114 };
115
116 /**
117  * Efreet_Menu_Filter_Type
118  */
119 typedef enum Efreet_Menu_Filter_Type Efreet_Menu_Filter_Type;
120
121 /**
122  * Efreet_Menu_Filter_Op
123  */
124 typedef struct Efreet_Menu_Filter_Op Efreet_Menu_Filter_Op;
125
126 /**
127  * Efreet_Menu_Filter_Op
128  * Contains information on a filter operation
129  */
130 struct Efreet_Menu_Filter_Op
131 {
132     Efreet_Menu_Filter_Op_Type type; /**< The type of operation */
133     Eina_List *categories;          /**< The categories this op applies too */
134     Eina_List *filenames;           /**< The filenames this op applies too */
135
136     Eina_List *filters;             /**< Child filters */
137
138     unsigned char all:1;             /**< Applies to all .desktop files */
139 };
140
141 /**
142  * Efreet_Menu_Filter
143  */
144 typedef struct Efreet_Menu_Filter Efreet_Menu_Filter;
145
146 /**
147  * Efreet_Menu_Filter
148  * Stores information on a filter
149  */
150 struct Efreet_Menu_Filter
151 {
152     Efreet_Menu_Filter_Type type;   /**< The type of filter */
153     Efreet_Menu_Filter_Op *op;      /**< The filter operations */
154 };
155
156 /**
157  * The type of layout
158  */
159 enum Efreet_Menu_Layout_Type
160 {
161     EFREET_MENU_LAYOUT_MENUNAME,
162     EFREET_MENU_LAYOUT_FILENAME,
163     EFREET_MENU_LAYOUT_SEPARATOR,
164     EFREET_MENU_LAYOUT_MERGE
165 };
166
167 /**
168  * Efreet_Menu_Layout_Type
169  */
170 typedef enum Efreet_Menu_Layout_Type Efreet_Menu_Layout_Type;
171
172 /**
173  * Efreet_Menu_Layout
174  */
175 typedef struct Efreet_Menu_Layout Efreet_Menu_Layout;
176
177 /**
178  * Efreet_Menu_Layout
179  * Stores information on a layout
180  */
181 struct Efreet_Menu_Layout
182 {
183     Efreet_Menu_Layout_Type  type;   /**< The type of layout */
184     char *name;                      /**< The name of the element */
185
186     /* The items below are for Menuname Layout elements */
187     signed char show_empty;    /**< Whether to show empty menus */
188     signed char in_line;       /**< Whether this meny can be inlined */
189     signed char inline_limit;  /**< Number of elements which triggers inline */
190     signed char inline_header; /**< Whether we should use the header name when this menu is inlined */
191     signed char inline_alias;  /**< Whether we should use the menu name when inlining */
192 };
193
194 /**
195  * Efreet_Menu_Desktop
196  */
197 typedef struct Efreet_Menu_Desktop Efreet_Menu_Desktop;
198
199 /**
200  * Efreet_Menu_Desktop
201  * Stores information on a desktop for the menu
202  */
203 struct Efreet_Menu_Desktop
204 {
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 */
208 };
209
210 static char *efreet_menu_prefix = NULL; /**< The $XDG_MENU_PREFIX env var */
211 Eina_List *efreet_menu_kde_legacy_dirs = NULL; /**< The directories to use for KDELegacy entries */
212 static const char *efreet_tag_menu = NULL;
213 static char *efreet_menu_file = NULL; /**< A menu file set explicityl as default */
214
215 static Eina_Hash *efreet_merged_menus = NULL;
216 static Eina_Hash *efreet_merged_dirs = NULL;
217
218 static Eina_Hash *efreet_menu_handle_cbs = NULL;
219 static Eina_Hash *efreet_menu_filter_cbs = NULL;
220 static Eina_Hash *efreet_menu_move_cbs = NULL;
221 static Eina_Hash *efreet_menu_layout_cbs = NULL;
222
223 static const char *efreet_menu_prefix_get(void);
224
225 static Efreet_Menu_Internal *efreet_menu_by_name_find(Efreet_Menu_Internal *internal,
226                                                     const char *name,
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);
230
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);
233
234 static int efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old);
235
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,
240                                         const char *path,
241                                         const char *id,
242                                         int legacy);
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,
246                                           Eina_Hash *cache);
247 static Efreet_Desktop *efreet_menu_directory_get(Efreet_Menu_Internal *internal,
248                                                     const char *path);
249 static void efreet_menu_process_filters(Efreet_Menu_Internal *internal,
250                                             unsigned int only_unallocated);
251 static Eina_List * efreet_menu_process_app_pool(Eina_List *pool, Eina_List *applications,
252                                          Eina_Hash *matches,
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);
263
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);
269
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);
281
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);
284
285 static Efreet_Menu_Move *efreet_menu_move_new(void);
286 static void efreet_menu_move_free(Efreet_Menu_Move *move);
287
288 static Efreet_Menu_Filter *efreet_menu_filter_new(void);
289 static void efreet_menu_filter_free(Efreet_Menu_Filter *filter);
290
291 static Efreet_Menu_Layout *efreet_menu_layout_new(void);
292 static void efreet_menu_layout_free(Efreet_Menu_Layout *layout);
293
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);
296
297 static Efreet_Menu_Desktop *efreet_menu_desktop_new(void);
298 static void efreet_menu_desktop_free(Efreet_Menu_Desktop *md);
299
300 static Efreet_Menu *efreet_menu_entry_new(void);
301
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);
304
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,
330                                                 const char *prefix);
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);
337
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);
343
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);
348
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);
351
352 static int efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b);
353
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);
356
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);
359
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);
362
363 static void efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path);
364
365 /**
366  * @return Returns 1 on success, 0 on failure
367  * @brief Initializes the Efreet Menu system.
368  */
369 int
370 efreet_menu_init(void)
371 {
372     int i;
373
374     struct
375     {
376         char *key;
377         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
378     } menu_cbs[] = {
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},
400         {NULL, NULL}
401     };
402
403     struct
404     {
405         char *key;
406         int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
407     } filter_cbs[] = {
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},
414         {NULL, NULL}
415     };
416
417     struct
418     {
419         char *key;
420         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
421     } move_cbs[] = {
422         {"Old", efreet_menu_handle_old},
423         {"New", efreet_menu_handle_new},
424         {NULL, NULL}
425     };
426
427     struct
428     {
429         char *key;
430         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
431     } layout_cbs[] = {
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},
436         {NULL, NULL}
437     };
438
439     if (!eina_stringshare_init()) return 0;
440     if (!efreet_xml_init()) return 0;
441
442     efreet_menu_handle_cbs = eina_hash_string_superfast_new(NULL);
443     efreet_menu_filter_cbs = eina_hash_string_superfast_new(NULL);
444     efreet_menu_move_cbs = eina_hash_string_superfast_new(NULL);
445     efreet_menu_layout_cbs = eina_hash_string_superfast_new(NULL);
446     if (!efreet_menu_handle_cbs || !efreet_menu_filter_cbs
447             || !efreet_menu_move_cbs || !efreet_menu_layout_cbs)
448         return 0;
449
450     /* set Menu into it's own so we can check the XML is valid before trying
451      * to handle it */
452     efreet_tag_menu = eina_stringshare_add(menu_cbs[0].key);
453
454     for (i = 0; menu_cbs[i].key != NULL; i++)
455       {
456          eina_hash_del(efreet_menu_handle_cbs,
457                menu_cbs[i].key,
458                NULL);
459          eina_hash_add(efreet_menu_handle_cbs,
460                menu_cbs[i].key,
461                menu_cbs[i].cb);
462       }
463     for (i = 0; filter_cbs[i].key != NULL; i++)
464       {
465          eina_hash_del(efreet_menu_filter_cbs,
466                filter_cbs[i].key,
467                NULL);
468          eina_hash_add(efreet_menu_filter_cbs,
469                filter_cbs[i].key,
470                filter_cbs[i].cb);
471       }
472     for (i = 0; move_cbs[i].key != NULL; i++)
473       {
474          eina_hash_del(efreet_menu_move_cbs,
475                move_cbs[i].key,
476                NULL);
477          eina_hash_add(efreet_menu_move_cbs,
478                move_cbs[i].key,
479                move_cbs[i].cb);
480       }
481     for (i = 0; layout_cbs[i].key != NULL; i++)
482       {
483          eina_hash_del(efreet_menu_layout_cbs,
484                layout_cbs[i].key,
485                NULL);
486          eina_hash_add(efreet_menu_layout_cbs,
487                layout_cbs[i].key,
488                layout_cbs[i].cb);
489       }
490     return 1;
491 }
492
493 /**
494  * @return Returns no value
495  * @brief Initialize legacy kde support. This function blocks while
496  * the kde-config script is run.
497  */
498 EAPI int
499 efreet_menu_kde_legacy_init(void)
500 {
501     FILE *f;
502     char buf[PATH_MAX];
503     char *p, *s;
504
505     IF_FREE_LIST(efreet_menu_kde_legacy_dirs, eina_stringshare_del);
506
507     f = popen("kde-config --path apps", "r");
508     if (!f) return 0;
509
510     /* XXX if the return from kde-config is a line longer than PATH_MAX,
511      * this won't be correct (increase buffer and get the rest...) */
512     if (!fgets(buf, PATH_MAX, f))
513     {
514         printf("Error initializing KDE legacy information\n");
515         return 0;
516     }
517     s = buf;
518
519     p = strchr(s, ':');
520     while (p)
521     {
522         *p = '\0';
523         efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
524                             (void *)eina_stringshare_add(s));
525         s = p + 1;
526         p = strchr(s, ':');
527     }
528
529     if (*s)
530         efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
531                             (void *)eina_stringshare_add(s));
532
533     pclose(f);
534     return 1;
535 }
536
537 /**
538  * @return Returns no value
539  * @brief Shuts down the Efreet menu system
540  */
541 void
542 efreet_menu_shutdown(void)
543 {
544     IF_FREE(efreet_menu_prefix);
545     IF_FREE(efreet_menu_file);
546
547     IF_FREE_HASH(efreet_menu_handle_cbs);
548     IF_FREE_HASH(efreet_menu_filter_cbs);
549     IF_FREE_HASH(efreet_menu_move_cbs);
550     IF_FREE_HASH(efreet_menu_layout_cbs);
551
552     IF_FREE_LIST(efreet_menu_kde_legacy_dirs, eina_stringshare_del);
553
554     IF_FREE_HASH(efreet_merged_menus);
555     IF_FREE_HASH(efreet_merged_dirs);
556
557     IF_RELEASE(efreet_tag_menu);
558
559     efreet_xml_shutdown();
560     eina_stringshare_shutdown();
561 }
562
563 /**
564  * @param name The internal name of the menu
565  * @return Returns the Efreet_Menu on success or
566  * NULL on failure
567  * @brief Creates a new menu
568  */
569 EAPI Efreet_Menu *
570 efreet_menu_new(const char *name)
571 {
572     Efreet_Menu *menu;
573
574     if (!name)
575     {
576         printf("Error creating a new menu, name is missing\n");
577         return NULL;
578     }
579     menu = efreet_menu_entry_new();
580     menu->type = EFREET_MENU_ENTRY_MENU;
581     menu->name = eina_stringshare_add(name);
582     return menu;
583 }
584
585 EAPI void
586 efreet_menu_file_set(const char *file)
587 {
588     IF_FREE(efreet_menu_file);
589     efreet_menu_file = NULL;
590     if (file) efreet_menu_file = strdup(file);
591 }
592
593 /**
594  * @return Returns the Efreet_Menu_Internal representation of the default menu or
595  * NULL if none found
596  * @brief Creates the default menu representation
597  */
598 EAPI Efreet_Menu *
599 efreet_menu_get(void)
600 {
601     char menu[PATH_MAX];
602     const char *dir;
603     Eina_List *config_dirs, *l;
604
605     /* check the users config directory first */
606     snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
607                         efreet_config_home_get(), efreet_menu_prefix_get());
608     if (ecore_file_exists(menu))
609         return efreet_menu_parse(menu);
610
611     if (efreet_menu_file)
612     {
613         if (ecore_file_exists(efreet_menu_file))
614           return efreet_menu_parse(efreet_menu_file);
615     }
616
617     /* fallback to the XDG_CONFIG_DIRS */
618     config_dirs = efreet_config_dirs_get();
619     EINA_LIST_FOREACH(config_dirs, l, dir)
620     {
621         snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
622                                     dir, efreet_menu_prefix_get());
623         if (ecore_file_exists(menu))
624             return efreet_menu_parse(menu);
625     }
626
627     return NULL;
628 }
629
630 /**
631  * @param path: The path of the menu to load
632  * @return Returns the Efreet_Menu_Internal representation on success or NULL on
633  * failure
634  * @brief Parses the given .menu file and creates the menu representation
635  */
636 EAPI Efreet_Menu *
637 efreet_menu_parse(const char *path)
638 {
639     Efreet_Xml *xml;
640     Efreet_Menu_Internal *internal = NULL;
641     Efreet_Menu *entry = NULL;
642     Eina_List *search_dirs;
643
644     xml = efreet_xml_new(path);
645     if (!xml) return NULL;
646
647     /* make sure we've got a <Menu> to start with */
648     if (xml->tag != efreet_tag_menu)
649     {
650         printf("Menu file didn't start with <Menu> tag.\n");
651         efreet_xml_del(xml);
652         return NULL;
653     }
654
655     IF_FREE_HASH(efreet_merged_menus);
656     efreet_merged_menus = eina_hash_string_superfast_new(NULL);
657
658     IF_FREE_HASH(efreet_merged_dirs);
659     efreet_merged_dirs = eina_hash_string_superfast_new(NULL);
660
661     /* split appart the filename and the path */
662     internal = efreet_menu_internal_new();
663
664     /* Set default values */
665     internal->show_empty = 0;
666     internal->in_line = 0;
667     internal->inline_limit = 4;
668     internal->inline_header = 1;
669     internal->inline_alias = 0;
670
671     search_dirs = efreet_config_dirs_get();
672
673     efreet_menu_path_set(internal, path);
674     if (!efreet_menu_handle_menu(internal, xml))
675     {
676         efreet_xml_del(xml);
677         efreet_menu_internal_free(internal);
678         return NULL;
679     }
680     efreet_xml_del(xml);
681
682     efreet_menu_resolve_moves(internal);
683
684     if (!efreet_menu_process_dirs(internal))
685     {
686         efreet_menu_internal_free(internal);
687         return NULL;
688     }
689
690     /* handle all .desktops */
691     if (!efreet_menu_process(internal, 0))
692     {
693         efreet_menu_internal_free(internal);
694         return NULL;
695     }
696
697     /* handle menus with only unallocated .desktops */
698     if (!efreet_menu_process(internal, 1))
699     {
700         efreet_menu_internal_free(internal);
701         return NULL;
702     }
703
704     /* layout menu */
705     entry = efreet_menu_layout_menu(internal);
706     efreet_menu_internal_free(internal);
707     return entry;
708 }
709
710 /**
711  * @param menu: The menu to work with
712  * @param path: The path where the menu should be saved
713  * @return Returns 1 on success, 0 on failure
714  * @brief Saves the menu to file
715  */
716 EAPI int
717 efreet_menu_save(Efreet_Menu *menu, const char *path)
718 {
719     FILE *f;
720     int ret;
721
722     f = fopen(path, "w");
723     if (!f) return 0;
724     fprintf(f, "<?xml version=\"1.0\"?>\n");
725     fprintf(f, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\" "
726                 "\"http://standards.freedesktop.org/menu-spec/menu-1.0.dtd\">\n");
727     ret = efreet_menu_save_menu(menu, f, 0);
728     fclose(f);
729     return ret;
730 }
731
732 static int
733 efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent)
734 {
735     Eina_List *l;
736
737     efreet_menu_save_indent(f, indent);
738     fprintf(f, "<Menu>\n");
739     if (menu->name)
740     {
741         efreet_menu_save_indent(f, indent + 1);
742         fprintf(f, "<Name>%s</Name>\n", menu->name);
743     }
744
745     if (indent == 0)
746     {
747         /* Only save these for the root element */
748         efreet_menu_save_indent(f, indent + 1);
749         fprintf(f, "<DefaultAppDirs/>\n");
750         efreet_menu_save_indent(f, indent + 1);
751         fprintf(f, "<DefaultDirectoryDirs/>\n");
752     }
753
754     if (menu->desktop)
755     {
756         efreet_menu_save_indent(f, indent + 1);
757         fprintf(f, "<Directory>%s</Directory>\n", menu->desktop->orig_path);
758     }
759
760     if (menu->entries)
761     {
762         Efreet_Menu *entry;
763         int has_desktop = 0, has_menu = 0;
764
765         efreet_menu_save_indent(f, indent + 1);
766         fprintf(f, "<Layout>\n");
767         EINA_LIST_FOREACH(menu->entries, l, entry)
768         {
769             if (entry->type == EFREET_MENU_ENTRY_MENU)
770             {
771                 efreet_menu_save_indent(f, indent + 2);
772                 fprintf(f, "<Menuname>%s</Menuname>\n", entry->id);
773                 has_menu = 1;
774             }
775             else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
776             {
777                 efreet_menu_save_indent(f, indent + 2);
778                 fprintf(f, "<Filename>%s</Filename>\n", entry->id);
779                 has_desktop = 1;
780             }
781             else if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
782             {
783                 efreet_menu_save_indent(f, indent + 2);
784                 fprintf(f, "<Separator/>\n");
785             }
786         }
787         efreet_menu_save_indent(f, indent + 1);
788         fprintf(f, "</Layout>\n");
789
790         if (has_desktop)
791         {
792             efreet_menu_save_indent(f, indent + 1);
793             fprintf(f, "<Include>\n");
794             EINA_LIST_FOREACH(menu->entries, l, entry)
795             {
796                 if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
797                 {
798                     efreet_menu_save_indent(f, indent + 2);
799                     fprintf(f, "<Filename>%s</Filename>\n", entry->id);
800                 }
801             }
802             efreet_menu_save_indent(f, indent + 1);
803             fprintf(f, "</Include>\n");
804         }
805
806         if (has_menu)
807         {
808             EINA_LIST_FOREACH(menu->entries, l, entry)
809             {
810                 if (entry->type == EFREET_MENU_ENTRY_MENU)
811                     efreet_menu_save_menu(entry, f, indent + 1);
812             }
813         }
814     }
815     efreet_menu_save_indent(f, indent);
816     fprintf(f, "</Menu>\n");
817     return 1;
818 }
819
820 static int
821 efreet_menu_save_indent(FILE *f, int indent)
822 {
823     int i;
824
825     for (i = 0; i < indent; i++)
826         fprintf(f, "  ");
827     return 1;
828 }
829
830 /**
831  * @param menu: The menu to work with
832  * @param desktop: The desktop to insert
833  * @param pos: The position to place the new desktop
834  * @return Returns 1 on success, 0 on failure
835  * @brief Insert a desktop element in a menu structure. Only accepts desktop files
836  * in default directories.
837  */
838 EAPI int
839 efreet_menu_desktop_insert(Efreet_Menu *menu, Efreet_Desktop *desktop, int pos)
840 {
841     Efreet_Menu *entry;
842     const char *id;
843
844     if (!desktop || !menu) return 0;
845     id = efreet_util_path_to_file_id(desktop->orig_path);
846     if (!id) return 0;
847
848     entry = efreet_menu_entry_new();
849     entry->type = EFREET_MENU_ENTRY_DESKTOP;
850     entry->id = eina_stringshare_add(id);
851     entry->name = eina_stringshare_add(desktop->name);
852     if (desktop->icon) entry->icon = eina_stringshare_add(desktop->icon);
853     efreet_desktop_ref(desktop);
854     entry->desktop = desktop;
855
856     if (pos < 0 || pos >= eina_list_count(menu->entries))
857         menu->entries = eina_list_append(menu->entries, entry);
858     else
859     {
860         menu->entries = eina_list_append_relative(menu->entries, entry,
861                                                   eina_list_nth(menu->entries, pos));
862     }
863     return 1;
864 }
865
866 /**
867  * @param menu: The menu to work with
868  * @param desktop: The desktop to remove
869  * @return Returns 1 on success, 0 on failure
870  * @brief Remove a desktop element in a menu structure. Only accepts desktop files
871  * in default directories.
872  */
873 EAPI int
874 efreet_menu_desktop_remove(Efreet_Menu *menu, Efreet_Desktop *desktop)
875 {
876     Efreet_Menu *entry;
877
878     if (!desktop || !menu) return 0;
879
880     entry = eina_list_search_unsorted(menu->entries,
881                                       (Eina_Compare_Cb)efreet_menu_cb_entry_compare_desktop,
882                             desktop);
883     if (entry)
884     {
885         menu->entries = eina_list_remove(menu->entries, entry);
886         efreet_menu_free(entry);
887         return 1;
888     }
889     return 0;
890 }
891
892 /**
893  * @param menu: The menu to work with
894  * @param menu: The menu to work with
895  * @param indent: The indent level to print the menu at
896  * @return Returns no value
897  * @brief Dumps the contents of the menu to the command line
898  */
899 EAPI void
900 efreet_menu_dump(Efreet_Menu *menu, const char *indent)
901 {
902     Eina_List *l;
903
904     printf("%s%s: ", indent, menu->name);
905     printf("%s\n", (menu->icon ? menu->icon : "No icon"));
906
907     /* XXX dump the rest of the menu info */
908
909     if (menu->entries)
910     {
911         Efreet_Menu *entry;
912         char *new_indent;
913         int len;
914
915         len = strlen(indent) + 3;
916         new_indent = malloc(sizeof(char *) * len);
917         snprintf(new_indent, len, "%s  ", indent);
918
919         EINA_LIST_FOREACH(menu->entries, l, entry)
920         {
921             if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
922                 printf("%s|---\n", new_indent);
923             else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
924                 printf("%s|-%s\n", new_indent, entry->name);
925             else if (entry->type == EFREET_MENU_ENTRY_MENU)
926                 efreet_menu_dump(entry, new_indent);
927             else if (entry->type == EFREET_MENU_ENTRY_HEADER)
928                 printf("%s|---%s\n", new_indent, entry->name);
929         }
930
931         FREE(new_indent);
932     }
933 }
934
935 /**
936  * @param user_dir: The user directory to work with
937  * @param system_dirs: The system directories to work with
938  * @param suffix: The path suffix to add
939  * @return Returns the list of directories
940  * @brief Creates the list of directories based on the user
941  * dir, system dirs and given suffix.
942  */
943 Eina_List *
944 efreet_default_dirs_get(const char *user_dir, Eina_List *system_dirs,
945                                                     const char *suffix)
946 {
947     const char *xdg_dir;
948     char dir[PATH_MAX];
949     Eina_List *list = NULL;
950     Eina_List *l;
951
952     snprintf(dir, sizeof(dir), "%s/%s", user_dir, suffix);
953     list = eina_list_append(list, strdup(dir));
954
955     EINA_LIST_FOREACH(system_dirs, l, xdg_dir)
956     {
957         snprintf(dir, sizeof(dir), "%s/%s", xdg_dir, suffix);
958         list = eina_list_append(list, strdup(dir));
959     }
960
961     return list;
962 }
963
964 /**
965  * @internal
966  * @return Returns a new Efreet_Menu_Internal struct
967  * @brief Allocates and initializes a new Efreet_Menu_Internal structure
968  */
969 static Efreet_Menu_Internal *
970 efreet_menu_internal_new(void)
971 {
972     Efreet_Menu_Internal *internal;
973
974     internal = NEW(Efreet_Menu_Internal, 1);
975     internal->show_empty = -1;
976     internal->in_line = -1;
977     internal->inline_limit = -1;
978     internal->inline_header = -1;
979     internal->inline_alias = -1;
980
981     return internal;
982 }
983
984 /**
985  * @param menu: The menu to free
986  * @return Returns no value
987  * @brief Frees up the given menu structure
988  */
989 void
990 efreet_menu_internal_free(Efreet_Menu_Internal *internal)
991 {
992     void *d;
993
994     if (!internal) return;
995
996     IF_FREE(internal->file.path);
997     IF_FREE(internal->file.name);
998
999     IF_RELEASE(internal->name.internal);
1000     internal->name.name = NULL;
1001
1002     internal->applications = eina_list_free(internal->applications);
1003
1004     IF_FREE_DLIST(internal->directories);
1005     IF_FREE_LIST(internal->app_dirs, efreet_menu_app_dir_free);
1006     IF_FREE_LIST(internal->app_pool, efreet_menu_desktop_free);
1007     IF_FREE_DLIST(internal->directory_dirs);
1008     IF_FREE_HASH(internal->directory_cache);
1009
1010     IF_FREE_LIST(internal->moves, efreet_menu_move_free);
1011     IF_FREE_LIST(internal->filters, efreet_menu_filter_free);
1012
1013     IF_FREE_LIST(internal->sub_menus, efreet_menu_internal_free);
1014
1015     IF_FREE_LIST(internal->layout, efreet_menu_layout_free);
1016     IF_FREE_LIST(internal->default_layout, efreet_menu_layout_free);
1017
1018     FREE(internal);
1019 }
1020
1021 /**
1022  * @internal
1023  * @return Returns the XDG_MENU_PREFIX env variable or "" if none set
1024  * @brief Retrieves the XDG_MENU_PREFIX or "" if not set.
1025  */
1026 static const char *
1027 efreet_menu_prefix_get(void)
1028 {
1029     char *prefix;
1030
1031     if (efreet_menu_prefix) return efreet_menu_prefix;
1032
1033     prefix = getenv("XDG_MENU_PREFIX");
1034     if (prefix) efreet_menu_prefix = strdup(prefix);
1035     else efreet_menu_prefix = strdup("");
1036
1037     return efreet_menu_prefix;
1038 }
1039
1040 /**
1041  * @internal
1042  * @param menu: The menu to populate
1043  * @param xml: The xml dom tree to populate from
1044  * @return Returns 1 if this XML tree is valid, 0 otherwise
1045  * @brief Populates the given menu from the given xml structure
1046  *
1047  * We walk the Menu children backwards. The reason for this is so that we
1048  * can deal with all the things that make us select the 'last' element
1049  * (MergeFile, Directory, etc). We'll see the last one first and can deal
1050  * with it right away.
1051  */
1052 static int
1053 efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml)
1054 {
1055     Efreet_Xml *child;
1056     int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
1057
1058     ecore_list_last_goto(xml->children);
1059     while ((child = ecore_dlist_previous(xml->children)))
1060     {
1061         cb = eina_hash_find(efreet_menu_handle_cbs, child->tag);
1062         if (cb)
1063         {
1064             if (!cb(internal, child))
1065                 return 0;
1066         }
1067         else
1068         {
1069             printf("Unknown XML tag: %s\n", child->tag);
1070             return 0;
1071         }
1072     }
1073     return 1;
1074 }
1075
1076 /**
1077  * @internal
1078  * @param parent: The parent Menu
1079  * @param xml: The xml that defines the menu
1080  * @return Returns 1 on success or 0 on failure
1081  * @brief Handles the sub-menu nodes of the XML file
1082  */
1083 static int
1084 efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1085 {
1086     Efreet_Menu_Internal *internal, *match;
1087
1088     efreet_menu_create_sub_menu_list(parent);
1089
1090     internal = efreet_menu_internal_new();
1091     internal->file.path = strdup(parent->file.path);
1092     if (!efreet_menu_handle_menu(internal, xml))
1093     {
1094         efreet_menu_internal_free(internal);
1095         return 0;
1096     }
1097
1098     /* if this menu already exists we just take this one and stick it on the
1099      * start of the existing one */
1100     if ((match = eina_list_search_unsorted(parent->sub_menus,
1101                                            (Eina_Compare_Cb)efreet_menu_cb_menu_compare,
1102                                            internal)))
1103     {
1104
1105         efreet_menu_concatenate(match, internal);
1106         efreet_menu_internal_free(internal);
1107     }
1108     else
1109         parent->sub_menus = eina_list_prepend(parent->sub_menus, internal);
1110
1111     return 1;
1112 }
1113
1114 /**
1115  * @internal
1116  * @param parent: The parent menu
1117  * @param xml: The xml tree
1118  * @return Returns 1 on success or 0 on failure
1119  * @brief Handles the AppDir tag
1120  */
1121 static int
1122 efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1123 {
1124     char *path;
1125     Efreet_Menu_App_Dir *app_dir;
1126
1127     if (!parent || !xml) return 0;
1128
1129     efreet_menu_create_app_dirs_list(parent);
1130     path = efreet_menu_path_get(parent, xml->text);
1131     if (!path) return 0;
1132
1133     /* we've already got this guy in our list we can skip it */
1134     if (eina_list_search_unsorted(parent->app_dirs,
1135                                   (Eina_Compare_Cb)efreet_menu_cb_app_dirs_compare,
1136                                   path))
1137     {
1138         FREE(path);
1139         return 1;
1140     }
1141
1142     app_dir = efreet_menu_app_dir_new();
1143     app_dir->path = path;
1144
1145     parent->app_dirs = eina_list_prepend(parent->app_dirs, app_dir);
1146
1147     return 1;
1148 }
1149
1150 /**
1151  * @internal
1152  * @param parent: The parent menu
1153  * @param xml: UNUSED
1154  * @return Returns 1 on success or 0 on failure
1155  * @brief Handles the DefaultAppDirs
1156  */
1157 static int
1158 efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1159 {
1160     Eina_List *prepend = NULL;
1161     Eina_List *dirs;
1162     char *dir;
1163
1164     if (!parent) return 0;
1165
1166     efreet_menu_create_app_dirs_list(parent);
1167     dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
1168                                                                     "applications");
1169     EINA_LIST_FREE(dirs, dir)
1170     {
1171         Efreet_Menu_App_Dir *app_dir;
1172
1173         if (eina_list_search_unsorted(parent->app_dirs,
1174                                       (Eina_Compare_Cb)efreet_menu_cb_app_dirs_compare,
1175                                       dir))
1176             continue;
1177
1178         app_dir = efreet_menu_app_dir_new();
1179         app_dir->path = strdup(dir);
1180
1181         prepend = eina_list_append(prepend, app_dir);
1182
1183         free(dir);
1184     }
1185     parent->app_dirs = eina_list_merge(prepend, parent->app_dirs);
1186
1187     return 1;
1188 }
1189
1190 /**
1191  * @internal
1192  * @param parent: The parent menu
1193  * @param xml: The xml tree
1194  * @return Returns 1 on success or 0 on failure
1195  * @brief Handles the DirectoryDir tag
1196  */
1197 static int
1198 efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1199 {
1200     char *path;
1201
1202     if (!parent || !xml) return 0;
1203
1204     efreet_menu_create_directory_dirs_list(parent);
1205     path = efreet_menu_path_get(parent, xml->text);
1206     if (!path) return 0;
1207
1208     /* we've already got this guy in our list we can skip it */
1209     if (ecore_list_find(parent->directory_dirs, ECORE_COMPARE_CB(strcmp), path))
1210     {
1211         FREE(path);
1212         return 1;
1213     }
1214
1215     ecore_dlist_prepend(parent->directory_dirs, path);
1216
1217     return 1;
1218 }
1219
1220 /**
1221  * @internal
1222  * @param parent: The parent menu
1223  * @param xml: UNUSED
1224  * @return Returns 1 on success or 0 on failure
1225  * @brief Handles the DefaultDirectoryDirs tag
1226  */
1227 static int
1228 efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1229 {
1230     Eina_List *dirs, *l;
1231     char *dir;
1232
1233     if (!parent) return 0;
1234
1235     efreet_menu_create_directory_dirs_list(parent);
1236     dirs = efreet_default_dirs_get(efreet_data_home_get(), efreet_data_dirs_get(),
1237                                                             "desktop-directories");
1238     EINA_LIST_FOREACH(dirs, l, dir)
1239     {
1240         if (ecore_list_find(parent->directory_dirs, ECORE_COMPARE_CB(strcmp), dir))
1241             continue;
1242
1243         ecore_dlist_prepend(parent->directory_dirs, strdup(dir));
1244     }
1245
1246     while (dirs)
1247     {
1248         free(eina_list_data_get(dirs));
1249         dirs = eina_list_remove_list(dirs, dirs);
1250     }
1251
1252     return 1;
1253 }
1254
1255 /**
1256  * @internal
1257  * @param parent: The parent Menu
1258  * @param xml: The xml to work with
1259  * @return Returns 1 on success or 0 on failure
1260  * @brief Sets the menu name from the given XML fragment.
1261  */
1262 static int
1263 efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1264 {
1265     /* not allowed to have two Name settings in a menu */
1266     if (parent->name.internal)
1267     {
1268         printf("efreet_menu_handle_name() setting second name into menu\n");
1269         return 0;
1270     }
1271
1272     /* ignore the name if it contains a / */
1273     if (strchr(xml->text, '/')) return 1;
1274
1275     parent->name.internal = eina_stringshare_add(xml->text);
1276
1277     return 1;
1278 }
1279
1280 /**
1281  * @internal
1282  * @param parent: The parent menu
1283  * @param xml: The xml tree
1284  * @return Returns 1 on success or 0 on failure
1285  * @brief Handles the Directory tag
1286  *
1287  * This just adds the given directory path to a list which we'll walk once
1288  * we've traversed the entire menu into memory.
1289  */
1290 static int
1291 efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1292 {
1293     if (!parent || !xml) return 0;
1294
1295     efreet_menu_create_directories_list(parent);
1296     ecore_dlist_prepend(parent->directories, strdup(xml->text));
1297
1298     return 1;
1299 }
1300
1301 /**
1302  * @internal
1303  * @param parent: The parent menu
1304  * @param xml: The xml tree
1305  * @return Returns 1 on success or 0 on failure
1306  * @brief Handles the OnlyUnallocated tag
1307  */
1308 static int
1309 efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1310 {
1311     if (!parent || !xml) return 0;
1312
1313     /* a later instance has been seen so we can ignore this one */
1314     if (parent->seen_allocated) return 1;
1315
1316     parent->seen_allocated = 1;
1317     parent->only_unallocated = 1;
1318
1319     return 1;
1320 }
1321
1322 /**
1323  * @internal
1324  * @param parent: The parent menu
1325  * @param xml: The xml tree
1326  * @return Returns 1 on success or 0 on failure
1327  * @brief Handles the NotOnlyUnallocated tag
1328  */
1329 static int
1330 efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1331 {
1332     if (!parent || !xml) return 0;
1333
1334     /* a later instance has been seen so we can ignore this one */
1335     if (parent->seen_allocated) return 1;
1336
1337     parent->seen_allocated = 1;
1338     parent->only_unallocated = 0;
1339
1340     return 1;
1341 }
1342
1343 /**
1344  * @internal
1345  * @param parent: The parent menu
1346  * @param xml: The xml tree
1347  * @return Returns 1 on success or 0 on failure
1348  * @brief Handles the Deleted tag
1349  */
1350 static int
1351 efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1352 {
1353     if (!parent || !xml) return 0;
1354
1355     /* a later instance has been seen so we can ignore this one */
1356     if (parent->seen_deleted) return 1;
1357
1358     parent->seen_deleted = 1;
1359     parent->deleted = 1;
1360
1361     return 1;
1362 }
1363
1364 /**
1365  * @internal
1366  * @param parent: The parent menu
1367  * @param xml: The xml tree
1368  * @return Returns 1 on success or 0 on failure
1369  * @brief Handles the NotDeleted tag
1370  */
1371 static int
1372 efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1373 {
1374     if (!parent || !xml) return 0;
1375
1376     /* a later instance has been seen so we can ignore this one */
1377     if (parent->seen_deleted) return 1;
1378
1379     parent->seen_deleted = 1;
1380     parent->deleted = 0;
1381
1382     return 1;
1383 }
1384
1385 /**
1386  * @internal
1387  * @param parent: The parent menu
1388  * @param xml: The XML tree to work with
1389  * @return Returns 1 on success or 0 on failure
1390  * @brief Handles parsing the Include tag and all subtags
1391  */
1392 static int
1393 efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1394 {
1395     return efreet_menu_handle_filter(parent, xml,
1396                                 EFREET_MENU_FILTER_INCLUDE);
1397 }
1398
1399 /**
1400  * @internal
1401  * @param parent: The parent menu
1402  * @param xml: The xml tree
1403  * @return Returns 1 on success or 0 on failure
1404  * @brief Handles the Exclude tag and all subtags
1405  */
1406 static int
1407 efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1408 {
1409     return efreet_menu_handle_filter(parent, xml,
1410                                 EFREET_MENU_FILTER_EXCLUDE);
1411 }
1412
1413 /**
1414  * @internal
1415  * @param op: The filter operation
1416  * @param xml: The xml tree
1417  * @return Returns 1 on success or 0 on failure
1418  * @brief Handles the Filename tag
1419  */
1420 static int
1421 efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1422 {
1423     if (!op || !xml) return 0;
1424
1425     op->filenames = eina_list_append(op->filenames, strdup(xml->text));
1426
1427     return 1;
1428 }
1429
1430 /**
1431  * @internal
1432  * @param op: The filter operation
1433  * @param xml: The xml tree
1434  * @return Returns 1 on success or 0 on failure
1435  * @brief Handles the Category tag
1436  */
1437 static int
1438 efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1439 {
1440     if (!op || !xml) return 0;
1441
1442
1443     op->categories = eina_list_append(op->categories, strdup(xml->text));
1444
1445     return 1;
1446 }
1447
1448 /**
1449  * @internal
1450  * @param op: The filter operation
1451  * @param xml: The xml tree
1452  * @return Returns 1 on success or 0 on failure
1453  * @brief Handles the All tag and all subtags
1454  */
1455 static int
1456 efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1457 {
1458     if (!op || !xml) return 0;
1459
1460     op->all = 1;
1461
1462     return 1;
1463 }
1464
1465 /**
1466  * @internal
1467  * @param op: The filter operation
1468  * @param xml: The xml tree
1469  * @return Returns 1 on success or 0 on failure
1470  * @brief Handles the And tag and all subtags
1471  */
1472 static int
1473 efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1474 {
1475     if (!op || !xml) return 0;
1476
1477     return efreet_menu_handle_filter_child_op(op, xml,
1478                             EFREET_MENU_FILTER_OP_AND);
1479 }
1480
1481 /**
1482  * @internal
1483  * @param op: The filter operation
1484  * @param xml: The xml tree
1485  * @return Returns 1 on success or 0 on failure
1486  * @brief Handles the Or tag and all subtags
1487  */
1488 static int
1489 efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1490 {
1491     if (!op || !xml) return 0;
1492
1493     return efreet_menu_handle_filter_child_op(op, xml,
1494                             EFREET_MENU_FILTER_OP_OR);
1495 }
1496
1497 /**
1498  * @internal
1499  * @param op: The filter operation
1500  * @param xml: The xml tree
1501  * @return Returns 1 on success or 0 on failure
1502  * @brief Handles the Not tag and all subtags
1503  */
1504 static int
1505 efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
1506 {
1507     if (!op || !xml) return 0;
1508
1509     return efreet_menu_handle_filter_child_op(op, xml,
1510                             EFREET_MENU_FILTER_OP_NOT);
1511 }
1512
1513 /**
1514  * @internal
1515  * @param parent: The parent menu
1516  * @param xml: The xml tree
1517  * @return Returns 1 on success or 0 on failure
1518  * @brief Handles the MergeFile tag
1519  */
1520 static int
1521 efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1522 {
1523     Eina_List *l;
1524     char *path = NULL;
1525     const char *attr = NULL;
1526     int is_path = 1;
1527     int ret = 1;
1528
1529     if (!parent || !xml) return 0;
1530
1531     /* check to see if this is a path or parent type */
1532     attr = efreet_xml_attribute_get(xml, "type");
1533     if (attr && !strcmp(attr, "parent"))
1534         is_path = 0;
1535
1536     /* we're given a path */
1537     if (is_path)
1538         path = efreet_menu_path_get(parent, xml->text);
1539
1540     /* need to find the next menu with the same name as ours in the config
1541      * dir after ours (if we're in a config dir) */
1542     else
1543     {
1544         Eina_List *search_dirs;
1545         const char *dir, *p;
1546         int len = 0;
1547
1548         if (!parent->file.path)
1549         {
1550             printf("efreet_menu_handle_merge_file() missing menu path ...\n");
1551             return 0;
1552         }
1553
1554         search_dirs = efreet_config_dirs_get();
1555
1556         /* we need to find the next menu with the same name in the directory
1557          * after the on the the menu was found in. to do that we first check
1558          * if it's in the config_home_directory() if so we need to search
1559          * all of the dirs. If it isn't in the config home directory then we
1560          * scan the search dirs and look for it. The search_dirs list will
1561          * be left at the next pointer so we can start looking for the menu
1562          * from that point */
1563
1564         dir = efreet_config_home_get();
1565         len = strlen(dir);
1566         if (strncmp(dir, parent->file.path, len))
1567         {
1568             EINA_LIST_FOREACH(search_dirs, l, dir)
1569             {
1570                 if (!strncmp(dir, parent->file.path, len))
1571                     break;
1572             }
1573         }
1574
1575         if (!dir)
1576         {
1577             printf("efreet_menu_handle_merge_file() failed to find "
1578                     "menu parent directory\n");
1579             return 0;
1580         }
1581
1582         /* the parent file path may have more path then just the base
1583          * directory so we need to append that as well */
1584         p = parent->file.path + len;
1585
1586         /* whatever dirs are left in the search dir we need to look for the
1587          * menu with the same relative filename */
1588         EINA_LIST_FOREACH(search_dirs, l, dir)
1589         {
1590             char file[PATH_MAX];
1591
1592             snprintf(file, sizeof(file), "%s/%s/%s", dir, (p ? p : ""),
1593                                                         parent->file.name);
1594             if (ecore_file_exists(file))
1595             {
1596                 path = strdup(file);
1597                 break;
1598             }
1599         }
1600     }
1601
1602     /* nothing to do if no file found */
1603     if (!path) return 1;
1604
1605     if (!efreet_menu_merge(parent, xml, path))
1606         ret = 0;
1607
1608     FREE(path);
1609
1610     return ret;
1611 }
1612
1613 /**
1614  * @internal
1615  * @param parent: The parent menu to merge into
1616  * @param xml: The XML to be merged
1617  * @param path: The path to the .menu file to merge
1618  */
1619 static int
1620 efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
1621 {
1622     Efreet_Xml *merge_xml;
1623     Efreet_Menu_Internal *internal;
1624     char *realpath;
1625
1626     if (!parent || !xml || !path) return 0;
1627
1628     /* do nothing if the file doesn't exist */
1629     if (!ecore_file_exists(path)) return 1;
1630
1631     realpath = ecore_file_realpath(path);
1632     if (realpath[0] == '\0')
1633     {
1634         printf("efreet_menu_merge() unable to get real path for %s\n", path);
1635         return 0;
1636     }
1637
1638     /* don't merge the same path twice */
1639     if (eina_hash_find(efreet_merged_menus, realpath))
1640         return 1;
1641
1642     eina_hash_add(efreet_merged_menus, realpath, (void *)1);
1643
1644     merge_xml = efreet_xml_new(realpath);
1645     FREE(realpath);
1646
1647     if (!merge_xml)
1648     {
1649         printf("efreet_menu_merge() failed to read in the "
1650                 "merge file (%s)\n", realpath);
1651         return 0;
1652     }
1653
1654     internal = efreet_menu_internal_new();
1655     efreet_menu_path_set(internal, path);
1656     efreet_menu_handle_menu(internal, merge_xml);
1657     efreet_menu_concatenate(parent, internal);
1658     efreet_menu_internal_free(internal);
1659
1660     efreet_xml_del(merge_xml);
1661
1662     return 1;
1663 }
1664
1665 /**
1666  * @internal
1667  * @param parent: The parent menu
1668  * @param xml: The xml tree
1669  * @return Returns 1 on success or 0 on failure
1670  * @brief Handles the MergeDir tag
1671  */
1672 static int
1673 efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1674 {
1675     char *path;
1676     int ret;
1677
1678     if (!parent || !xml || !xml->text) return 0;
1679
1680     path = efreet_menu_path_get(parent, xml->text);
1681     if (!path) return 1;
1682     if (!ecore_file_exists(path))
1683     {
1684         FREE(path);
1685         return 1;
1686     }
1687
1688     ret = efreet_menu_merge_dir(parent, xml, path);
1689     FREE(path);
1690
1691     return ret;
1692 }
1693
1694 /**
1695  * @internal
1696  * @param parent: the parent menu of the merge
1697  * @param xml: The xml tree
1698  * @param path: The path to the merge directory
1699  * @return Returns 1 on success or 0 on failure
1700  * @brief Find all of the .menu files in the given directory and merge them
1701  * into the @a parent menu.
1702  */
1703 static int
1704 efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path)
1705 {
1706     char dir_path[PATH_MAX];
1707     DIR *files;
1708     struct dirent *file;
1709
1710     if (!parent || !xml || !path) return 0;
1711
1712     /* check to see if we've merged this directory already */
1713     if (eina_hash_find(efreet_merged_dirs, path)) return 1;
1714     eina_hash_add(efreet_merged_dirs, path, (void *)1);
1715
1716     files = opendir(path);
1717     if (!files) return 1;
1718
1719     while ((file = readdir(files)))
1720     {
1721         char *p;
1722
1723         if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
1724         p = strrchr(file->d_name, '.');
1725         if (!p) continue;
1726         if (strcmp(p, ".menu")) continue;
1727
1728         snprintf(dir_path, PATH_MAX, "%s/%s", path, file->d_name);
1729         if (!efreet_menu_merge(parent, xml, dir_path))
1730         {
1731             closedir(files);
1732             return 0;
1733         }
1734     }
1735     closedir(files);
1736
1737     return 1;
1738 }
1739
1740 /**
1741  * @internal
1742  * @param parent: The parent menu
1743  * @param xml: The xml tree
1744  * @return Returns 1 on success or 0 on failure
1745  * @brief Handles the DefaultMergeDirs tag
1746  */
1747 static int
1748 efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1749 {
1750     Eina_List *dirs;
1751     char path[PATH_MAX], *p;
1752     const char *prefix;
1753
1754     if (!parent || !xml) return 0;
1755
1756     prefix = efreet_menu_prefix_get();
1757     if (!strcmp(prefix, "gnome-") &&
1758             (!strcmp(parent->file.name, "gnome-applications.menu")))
1759         p = strdup("applications");
1760
1761     else if ((!strcmp(prefix, "kde-") &&
1762             (!strcmp(parent->file.name, "kde-applications.menu"))))
1763         p = strdup("applications");
1764
1765     else
1766     {
1767         char *s;
1768
1769         p = strdup(parent->file.name);
1770         s = strrchr(p, '.');
1771         if (s) *s = '\0';
1772     }
1773     snprintf(path, sizeof(path), "menus/%s-merged", p);
1774     FREE(p);
1775
1776     dirs = efreet_default_dirs_get(efreet_config_home_get(),
1777                                     efreet_config_dirs_get(), path);
1778
1779     while ((p = eina_list_data_get(dirs)))
1780     {
1781         dirs = eina_list_remove_list(dirs, dirs);
1782         efreet_menu_merge_dir(parent, xml, p);
1783         FREE(p);
1784     }
1785
1786     return 1;
1787 }
1788
1789 /**
1790  * @internal
1791  * @param parent: The parent menu
1792  * @param xml: The xml tree
1793  * @return Returns 1 on success or 0 on failure
1794  * @brief Handles the LegacyDir tag
1795  */
1796 static int
1797 efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
1798 {
1799     Efreet_Menu_Internal *legacy;
1800
1801     if (!parent || !xml) return 0;
1802
1803     legacy = efreet_menu_handle_legacy_dir_helper(NULL, parent, xml->text,
1804                                 efreet_xml_attribute_get(xml, "prefix"));
1805     efreet_menu_concatenate(parent, legacy);
1806     efreet_menu_internal_free(legacy);
1807
1808     return 1;
1809
1810 }
1811
1812 /**
1813  * @internal
1814  * @param parent: The parent menu
1815  * @param legacy_dir: The legacy directory path
1816  * @param prefix: The legacy directory prefix if one set
1817  * @return Returns the Efreet_Menu_Internal representing the legacy hierarchy
1818  * @brief Handles the process of merging @a legacy_dir into @a parent menu
1819  */
1820 static Efreet_Menu_Internal *
1821 efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root,
1822                                         Efreet_Menu_Internal *parent,
1823                                         const char *legacy_dir,
1824                                         const char *prefix)
1825 {
1826     char *path, file_path[PATH_MAX];
1827     Efreet_Menu_Internal *legacy_internal;
1828     Efreet_Menu_Filter *filter;
1829     Efreet_Menu_App_Dir *app_dir;
1830     int path_len, count = 0;
1831     DIR *files;
1832     struct dirent *file;
1833
1834     if (!parent || !legacy_dir) return 0;
1835
1836     path = efreet_menu_path_get(parent, legacy_dir);
1837
1838     /* nothing to do if the legacy path doesn't exist */
1839     if (!ecore_file_exists(path))
1840     {
1841         FREE(path);
1842         return NULL;
1843     }
1844
1845     legacy_internal = efreet_menu_internal_new();
1846     legacy_internal->name.internal = eina_stringshare_add(ecore_file_file_get(path));
1847
1848     /* add the legacy dir as an app dir */
1849     app_dir = efreet_menu_app_dir_new();
1850     app_dir->path = strdup(path);
1851     app_dir->legacy = 1;
1852     if (prefix && !strchr(prefix, '/')) app_dir->prefix = strdup(prefix);
1853
1854     efreet_menu_create_app_dirs_list(legacy_internal);
1855     legacy_internal->app_dirs = eina_list_append(legacy_internal->app_dirs, app_dir);
1856 #ifndef STRICT_SPEC
1857     if (root)
1858     {
1859         /* XXX This seems wrong, but it makes efreet pass the fdo tests */
1860         app_dir = efreet_menu_app_dir_new();
1861         app_dir->path = strdup(path);
1862         app_dir->legacy = 1;
1863         if (prefix && !strchr(prefix, '/')) app_dir->prefix = strdup(prefix);
1864         root->app_dirs = eina_list_append(root->app_dirs, app_dir);
1865     }
1866 #endif
1867
1868     /* add the legacy dir as a directory dir */
1869     efreet_menu_create_directory_dirs_list(legacy_internal);
1870     ecore_dlist_append(legacy_internal->directory_dirs, strdup(path));
1871
1872     /* setup a filter for all the conforming .desktop files in the legacy
1873      * dir */
1874     filter = efreet_menu_filter_new();
1875     filter->type = EFREET_MENU_FILTER_INCLUDE;
1876
1877     filter->op->type = EFREET_MENU_FILTER_OP_OR;
1878
1879     efreet_menu_create_filter_list(legacy_internal);
1880     legacy_internal->filters = eina_list_append(legacy_internal->filters, filter);
1881
1882     path_len = strlen(path);
1883     files = opendir(path);
1884     while ((file = readdir(files)))
1885     {
1886         Efreet_Desktop *desktop = NULL;
1887         char buf[PATH_MAX];
1888         char *exten;
1889
1890         if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
1891         file_path[0] = '\0';
1892         ecore_strlcpy(file_path, path, PATH_MAX);
1893         ecore_strlcpy(file_path + path_len, "/", PATH_MAX - path_len);
1894         ecore_strlcpy(file_path + path_len + 1, file->d_name, PATH_MAX - path_len - 1);
1895
1896         /* recurse into sub directories */
1897         if (ecore_file_is_dir(file_path))
1898         {
1899             Efreet_Menu_Internal *ret;
1900
1901             ret = efreet_menu_handle_legacy_dir_helper(root ? root : legacy_internal,
1902                                                         legacy_internal, file_path, prefix);
1903             if (!ret)
1904             {
1905                 efreet_menu_filter_free(filter);
1906                 efreet_menu_internal_free(legacy_internal);
1907                 FREE(path);
1908                 closedir(files);
1909                 return NULL;
1910             }
1911
1912             efreet_menu_create_sub_menu_list(legacy_internal);
1913             legacy_internal->sub_menus = eina_list_prepend(legacy_internal->sub_menus, ret);
1914
1915             continue;
1916         }
1917
1918         if (!strcmp(file->d_name, ".directory"))
1919         {
1920             legacy_internal->directory = efreet_desktop_get(file_path);
1921             if (legacy_internal->directory
1922                     && legacy_internal->directory->type != EFREET_DESKTOP_TYPE_DIRECTORY)
1923             {
1924                 efreet_desktop_free(legacy_internal->directory);
1925                 legacy_internal->directory = NULL;
1926             }
1927             continue;
1928         }
1929
1930         exten = strrchr(file->d_name, '.');
1931
1932         if (exten && !strcmp(exten, ".desktop"))
1933             desktop = efreet_desktop_get(file_path);
1934
1935         if (!desktop) continue;
1936
1937         /* if the .desktop has categories it isn't legacy */
1938         if (efreet_desktop_category_count_get(desktop) != 0)
1939             continue;
1940
1941         /* XXX: This will disappear when the .desktop is free'd */
1942         efreet_desktop_category_add(desktop, "Legacy");
1943
1944         if (prefix)
1945         {
1946             snprintf(buf, PATH_MAX, "%s%s", prefix, file->d_name);
1947             filter->op->filenames = eina_list_append(filter->op->filenames, strdup(buf));
1948         }
1949         else
1950             filter->op->filenames = eina_list_append(filter->op->filenames, strdup(file->d_name));
1951
1952         count++;
1953         efreet_desktop_free(desktop);
1954     }
1955     closedir(files);
1956
1957     FREE(path);
1958     efreet_menu_filter_free(filter);
1959     return legacy_internal;
1960 }
1961
1962 /**
1963  * @internal
1964  * @param parent: The parent menu
1965  * @param xml: UNUSED
1966  * @return Returns 1 on success or 0 on failure
1967  * @brief Handles the KDELegacyDirs tag
1968  */
1969 static int
1970 efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml __UNUSED__)
1971 {
1972     Eina_List *l;
1973     const char *dir;
1974
1975     if (!parent) return 0;
1976
1977     if (!efreet_menu_kde_legacy_dirs) return 1;
1978
1979     /* XXX if one _helper() call succeeds, we return success. should this be flipped?
1980      * (return fail if on of them failed) */
1981     EINA_LIST_FOREACH(efreet_menu_kde_legacy_dirs, l, dir)
1982     {
1983         Efreet_Menu_Internal *kde;
1984
1985         kde = efreet_menu_handle_legacy_dir_helper(NULL, parent, dir, "kde");
1986         if (kde)
1987         {
1988             efreet_menu_concatenate(parent, kde);
1989             efreet_menu_internal_free(kde);
1990             return 1;
1991         }
1992     }
1993
1994     return 0;
1995 }
1996
1997 /**
1998  * @internal
1999  * @param parent: The parent menu
2000  * @param xml: The xml tree
2001  * @return Returns 1 on success or 0 on failure
2002  * @brief Handles the Move tag and all subtags
2003  */
2004 static int
2005 efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2006 {
2007     Efreet_Xml *child;
2008
2009     if (!parent || !xml) return 0;
2010
2011     efreet_menu_create_move_list(parent);
2012
2013     ecore_list_first_goto(xml->children);
2014     while ((child = ecore_list_next(xml->children)))
2015     {
2016         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
2017
2018         cb = eina_hash_find(efreet_menu_move_cbs, child->tag);
2019         if (cb)
2020         {
2021             if (!cb(parent, child))
2022                 return 0;
2023         }
2024         else
2025         {
2026             printf("efreet_menu_handle_move() unknown tag found "
2027                     "in Move (%s)\n", child->tag);
2028             return 0;
2029         }
2030     }
2031
2032     parent->current_move = NULL;
2033
2034     return 1;
2035 }
2036
2037 /**
2038  * @internal
2039  * @param parent: The parent menu
2040  * @param xml: The xml tree
2041  * @return Returns 1 on success or 0 on failure
2042  * @brief Handles the Old tag
2043  */
2044 static int
2045 efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2046 {
2047     Efreet_Menu_Move *move;
2048
2049     if (!parent || !xml || !xml->text) return 0;
2050
2051     if (parent->current_move)
2052     {
2053         printf("efreet_menu_handle_old() saw second <Old> "
2054                 "before seeing <New>\n");
2055         return 0;
2056     }
2057
2058     /* If we already moved this menu, remove the old move */
2059     /* XXX This seems wrong, but it makes efreet pass the fdo tests */
2060 #ifndef STRICT_SPEC
2061     move = eina_list_search_unsorted(parent->moves,
2062                                      (Eina_Compare_Cb)efreet_menu_cb_move_compare,
2063                                      xml->text);
2064     if (move)
2065     {
2066         efreet_menu_move_free(move);
2067         parent->moves = eina_list_remove(parent->moves, move);
2068     }
2069 #endif
2070
2071     move = efreet_menu_move_new();
2072     move->old_name = strdup(xml->text);
2073
2074     parent->current_move = move;
2075     parent->moves = eina_list_append(parent->moves, move);
2076
2077     return 1;
2078 }
2079
2080 /**
2081  * @internal
2082  * @param parent: The parent menu
2083  * @param xml: The xml tree
2084  * @return Returns 1 on success or 0 on failure
2085  * @brief Handles the New tag
2086  */
2087 static int
2088 efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2089 {
2090     if (!parent || !xml || !xml->text) return 0;
2091
2092     if (!parent->current_move)
2093     {
2094         printf("efreet_menu_handle_new() saw New before seeing Old\n");
2095         return 0;
2096     }
2097
2098     parent->current_move->new_name = strdup(xml->text);
2099     parent->current_move = NULL;
2100
2101     return 1;
2102 }
2103
2104 /**
2105  * @internal
2106  * @param parent: The parent menu
2107  * @param xml: The xml tree
2108  * @return Returns 1 on success or 0 on failure
2109  * @brief Handles the Layout tag and all subtags
2110  */
2111 static int
2112 efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2113 {
2114     Efreet_Xml *child;
2115
2116     if (!parent || !xml) return 0;
2117
2118     /* We use the last existing layout */
2119     if (parent->layout) return 1;
2120
2121     efreet_menu_create_layout_list(parent);
2122
2123     ecore_list_first_goto(xml->children);
2124     while ((child = ecore_list_next(xml->children)))
2125     {
2126         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
2127
2128         cb = eina_hash_find(efreet_menu_layout_cbs, child->tag);
2129         if (cb)
2130         {
2131             if (!cb(parent, child, 0))
2132                 return 0;
2133         }
2134         else
2135         {
2136             printf("efreet_menu_handle_move() unknown tag found "
2137                     "in Layout (%s)\n", child->tag);
2138             return 0;
2139         }
2140     }
2141
2142     return 1;
2143 }
2144
2145 /**
2146  * @internal
2147  * @param parent: The parent menu
2148  * @param xml: The xml tree
2149  * @return Returns 1 on success or 0 on failure
2150  * @brief Handles the DefaultLayout tag
2151  */
2152 static int
2153 efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml)
2154 {
2155     const char *val;
2156     Efreet_Xml *child;
2157
2158     if (!parent || !xml) return 0;
2159
2160     /* We use the last existing layout */
2161     if (parent->default_layout) return 1;
2162
2163     val = efreet_xml_attribute_get(xml, "show_empty");
2164     if (val) parent->show_empty = !strcmp(val, "true");
2165
2166     val = efreet_xml_attribute_get(xml, "inline");
2167     if (val) parent->in_line = !strcmp(val, "true");
2168
2169     val = efreet_xml_attribute_get(xml, "inline_limit");
2170     if (val) parent->inline_limit = atoi(val);
2171
2172     val = efreet_xml_attribute_get(xml, "inline_header");
2173     if (val) parent->inline_header = !strcmp(val, "true");
2174
2175     val = efreet_xml_attribute_get(xml, "inline_alias");
2176     if (val) parent->inline_alias = !strcmp(val, "true");
2177
2178     efreet_menu_create_default_layout_list(parent);
2179
2180     ecore_list_first_goto(xml->children);
2181     while ((child = ecore_list_next(xml->children)))
2182     {
2183         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
2184
2185         cb = eina_hash_find(efreet_menu_layout_cbs, child->tag);
2186         if (cb)
2187         {
2188             if (!cb(parent, child, 1))
2189                 return 0;
2190         }
2191         else
2192         {
2193             printf("efreet_menu_handle_move() unknown tag found in "
2194                     "DefaultLayout (%s)\n", child->tag);
2195             return 0;
2196         }
2197     }
2198
2199     return 1;
2200 }
2201
2202 static int
2203 efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2204 {
2205     Efreet_Menu_Layout *layout;
2206     const char *val;
2207
2208     if (!parent || !xml) return 0;
2209
2210     if (!xml->text)
2211     {
2212         printf("efreet_menu_handle_layout_menuname() The Menuname tag in "
2213                 "layout needs a filename.\n");
2214         return 0;
2215     }
2216
2217     layout = efreet_menu_layout_new();
2218     layout->type = EFREET_MENU_LAYOUT_MENUNAME;
2219     layout->name = strdup(xml->text);
2220
2221     val = efreet_xml_attribute_get(xml, "show_empty");
2222     if (val) layout->show_empty = !strcmp(val, "true");
2223
2224     val = efreet_xml_attribute_get(xml, "inline");
2225     if (val) layout->in_line = !strcmp(val, "true");
2226
2227     val = efreet_xml_attribute_get(xml, "inline_limit");
2228     if (val) layout->inline_limit = atoi(val);
2229
2230     val = efreet_xml_attribute_get(xml, "inline_header");
2231     if (val) layout->inline_header = !strcmp(val, "true");
2232
2233     val = efreet_xml_attribute_get(xml, "inline_alias");
2234     if (val) layout->inline_alias = !strcmp(val, "true");
2235
2236     if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2237     else parent->layout = eina_list_append(parent->layout, layout);
2238
2239     return 1;
2240 }
2241
2242 static int
2243 efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2244 {
2245     Efreet_Menu_Layout *layout;
2246
2247     if (!parent || !xml) return 0;
2248
2249     if (!xml->text)
2250     {
2251         printf("efreet_menu_handle_layout_filename() The Filename tag in "
2252                 "layout needs a filename.\n");
2253         return 0;
2254     }
2255
2256     layout = efreet_menu_layout_new();
2257     layout->type = EFREET_MENU_LAYOUT_FILENAME;
2258     layout->name = strdup(xml->text);
2259
2260     if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2261     else parent->layout = eina_list_append(parent->layout, layout);
2262
2263     return 1;
2264 }
2265
2266 static int
2267 efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2268 {
2269     Efreet_Menu_Layout *layout;
2270
2271     if (!parent || !xml) return 0;
2272
2273     layout = efreet_menu_layout_new();
2274     layout->type = EFREET_MENU_LAYOUT_SEPARATOR;
2275     if (def)
2276         parent->default_layout = eina_list_append(parent->default_layout, layout);
2277     else
2278         parent->layout = eina_list_append(parent->layout, layout);
2279     return 1;
2280 }
2281
2282 static int
2283 efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def)
2284 {
2285     Efreet_Menu_Layout *layout;
2286     const char *attr;
2287
2288     if (!parent || !xml) return 0;
2289
2290     attr = efreet_xml_attribute_get(xml, "type");
2291     if (!attr)
2292     {
2293         printf("efreet_menu_handle_layout_merge() The Merge tag in layout "
2294                 "needs a type attribute.\n");
2295         return 0;
2296     }
2297
2298     if (strcmp(attr, "files") && strcmp(attr, "menus") && strcmp(attr, "all"))
2299     {
2300         printf("efreet_menu_handle_layout_merge() The type attribute for "
2301                 "the Merge tag contains an unknown value (%s).\n", attr);
2302         return 0;
2303     }
2304
2305     layout = efreet_menu_layout_new();
2306     layout->type = EFREET_MENU_LAYOUT_MERGE;
2307     layout->name = strdup(attr);
2308
2309     if (def) parent->default_layout = eina_list_append(parent->default_layout, layout);
2310     else parent->layout = eina_list_append(parent->layout, layout);
2311
2312     return 1;
2313 }
2314
2315 /**
2316  * @internal
2317  * @param parent: The parent menu
2318  * @param xml: The XML tree to parse
2319  * @param type: The type of filter
2320  * @return Returns 1 on success or 0 on failure
2321  * @brief Parses the given XML tree and adds the filter to the parent menu
2322  */
2323 static int
2324 efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml,
2325                                         Efreet_Menu_Filter_Type type)
2326 {
2327     Efreet_Menu_Filter *filter;
2328
2329     efreet_menu_create_filter_list(parent);
2330
2331     /* filters have a default or relationship */
2332     filter = efreet_menu_filter_new();
2333     filter->type = type;
2334     filter->op->type = EFREET_MENU_FILTER_OP_OR;
2335
2336     if (!efreet_menu_handle_filter_op(filter->op, xml))
2337     {
2338         efreet_menu_filter_free(filter);
2339         return 0;
2340     }
2341
2342     parent->filters = eina_list_prepend(parent->filters, filter);
2343
2344     return 1;
2345 }
2346
2347 /**
2348  * @internal
2349  * @param op: The operation to work with
2350  * @param xml: The XML tree representing this operation
2351  * @return Returns 1 on success or 0 on failure
2352  * @brief Parses the given XML tree and populates the operation
2353  */
2354 static int
2355 efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml)
2356 {
2357     Efreet_Xml *child;
2358
2359     ecore_list_first_goto(xml->children);
2360     while ((child = ecore_list_next(xml->children)))
2361     {
2362         int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
2363
2364         cb = eina_hash_find(efreet_menu_filter_cbs, child->tag);
2365         if (cb)
2366         {
2367             if (!cb(op, child))
2368                 return 0;
2369         }
2370         else
2371         {
2372             printf("efreet_menu_handle_filter_op() unknown tag in filter (%s)\n", child->tag);
2373             return 0;
2374         }
2375     }
2376     return 1;
2377 }
2378
2379 /**
2380  * @internal
2381  * @return Returns a new Efreet_Menu_Filter on success or NULL on failure
2382  * @brief Creates and initializes an Efreet_Menu_Filter object
2383  */
2384 static Efreet_Menu_Filter *
2385 efreet_menu_filter_new(void)
2386 {
2387     Efreet_Menu_Filter *filter;
2388
2389     filter = NEW(Efreet_Menu_Filter, 1);
2390     filter->op = efreet_menu_filter_op_new();
2391     if (!filter->op)
2392     {
2393         FREE(filter);
2394         return NULL;
2395     }
2396
2397     return filter;
2398 }
2399
2400 /**
2401  * @internal
2402  * @param filter: The filter to work with
2403  * @return Returns no data
2404  * @brief Frees the given filter and all data
2405  */
2406 static void
2407 efreet_menu_filter_free(Efreet_Menu_Filter *filter)
2408 {
2409     if (!filter) return;
2410
2411     if (filter->op) efreet_menu_filter_op_free(filter->op);
2412     filter->op = NULL;
2413
2414     FREE(filter);
2415 }
2416
2417 /**
2418  * @internal
2419  * @return Returns a new Efreet_Menu_Layout on success or NULL on failure
2420  * @brief Creates and initializes an Efreet_Menu_Layout object
2421  */
2422 static Efreet_Menu_Layout *
2423 efreet_menu_layout_new(void)
2424 {
2425     Efreet_Menu_Layout *layout;
2426
2427     layout = NEW(Efreet_Menu_Layout, 1);
2428     layout->show_empty = -1;
2429     layout->in_line = -1;
2430     layout->inline_limit = -1;
2431     layout->inline_header = -1;
2432     layout->inline_alias = -1;
2433
2434     return layout;
2435 }
2436
2437 /**
2438  * @internal
2439  * @param filter: The filter to work with
2440  * @return Returns no data
2441  * @brief Frees the given filter and all data
2442  */
2443 static void
2444 efreet_menu_layout_free(Efreet_Menu_Layout *layout)
2445 {
2446     if (!layout) return;
2447
2448     IF_FREE(layout->name);
2449     FREE(layout);
2450 }
2451
2452 /**
2453  * @internal
2454  * @return Returns a new Efreet_Menu_Filter_Op on success or NULL on failure
2455  * @brief Creates and initializes an Efreet_Menu_Filter_Op structure
2456  */
2457 static Efreet_Menu_Filter_Op *
2458 efreet_menu_filter_op_new(void)
2459 {
2460     Efreet_Menu_Filter_Op *op;
2461
2462     op = NEW(Efreet_Menu_Filter_Op, 1);
2463
2464     return op;
2465 }
2466
2467 /**
2468  * @internal
2469  * @param op: The operation to work with
2470  * @return Returns no value.
2471  * @brief Frees the given operation and all sub data
2472  */
2473 static void
2474 efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op)
2475 {
2476     if (!op) return;
2477
2478     IF_FREE_LIST(op->categories, free);
2479     IF_FREE_LIST(op->filenames, free);
2480     IF_FREE_LIST(op->filters, efreet_menu_filter_op_free);
2481
2482     FREE(op);
2483 }
2484
2485 /**
2486  * @internal
2487  * @return Returns a new Efreet_Menu_Desktop on success or NULL on failure
2488  * @brief Creates and returns an Efreet_Menu_Desktop
2489  */
2490 static Efreet_Menu_Desktop *
2491 efreet_menu_desktop_new(void)
2492 {
2493     Efreet_Menu_Desktop *md;
2494
2495     md = NEW(Efreet_Menu_Desktop, 1);
2496
2497     return md;
2498 }
2499
2500 /**
2501  * @internal
2502  * @param md: The Efreet_Menu_Desktop to free
2503  * @return Returns no value
2504  * @brief Frees the given structure
2505  */
2506 static void
2507 efreet_menu_desktop_free(Efreet_Menu_Desktop *md)
2508 {
2509     IF_RELEASE(md->id);
2510     if (md->desktop) efreet_desktop_free(md->desktop);
2511     FREE(md);
2512 }
2513
2514 /**
2515  * @internal
2516  * @return Returns a new Efreet_Menu on success or NULL on failure
2517  * @brief Creates and returns an Efreet_Menu
2518  */
2519 static Efreet_Menu *
2520 efreet_menu_entry_new(void)
2521 {
2522     Efreet_Menu *entry;
2523
2524     entry = NEW(Efreet_Menu, 1);
2525
2526     return entry;
2527 }
2528
2529 /**
2530  * @internal
2531  * @param entry: The Efreet_Menu to free
2532  * @return Returns no value
2533  * @brief Frees the given structure
2534  */
2535 EAPI void
2536 efreet_menu_free(Efreet_Menu *entry)
2537 {
2538     IF_RELEASE(entry->name);
2539     IF_RELEASE(entry->icon);
2540     entry->entries = eina_list_free(entry->entries);
2541     IF_RELEASE(entry->id);
2542     if (entry->desktop) efreet_desktop_free(entry->desktop);
2543     FREE(entry);
2544 }
2545
2546 /**
2547  * @internal
2548  * @param op: The op to add a child too
2549  * @param xml: The XML tree of the child
2550  * @param type: The type of child to add
2551  * @return Returns 1 on success or 0 on failure
2552  * @brief Parses the given XML tree and populates a new child operation.
2553  */
2554 static int
2555 efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
2556                                                 Efreet_Menu_Filter_Op_Type type)
2557 {
2558     Efreet_Menu_Filter_Op *child_op;
2559
2560     child_op = efreet_menu_filter_op_new();
2561     child_op->type = type;
2562
2563     if (!efreet_menu_handle_filter_op(child_op, xml))
2564     {
2565         efreet_menu_filter_op_free(child_op);
2566         return 0;
2567     }
2568
2569     op->filters = eina_list_append(op->filters, child_op);
2570
2571     return 1;
2572 }
2573
2574 /**
2575  * @internal
2576  * @param menu: The menu to work with
2577  * @param only_unallocated: Do we only look for unallocated items?
2578  * @return Returns 1 if we've successfully processed the menu, 0 otherwise
2579  * @brief Handles the processing of the menu data to retrieve the .desktop
2580  * files for the menu
2581  */
2582 static int
2583 efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2584 {
2585     Eina_List *l;
2586
2587     /* a menu _MUST_ have a name */
2588     if (!internal->name.internal || (internal->name.internal[0] == '\0'))
2589         return 0;
2590
2591     /* handle filtering out .desktop files as needed. This deals with all
2592      * .desktop files */
2593     efreet_menu_process_filters(internal, only_unallocated);
2594
2595     if (internal->sub_menus)
2596     {
2597         Efreet_Menu_Internal *sub_internal;
2598
2599         EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
2600         {
2601             sub_internal->parent = internal;
2602             efreet_menu_process(sub_internal, only_unallocated);
2603         }
2604     }
2605
2606     return 1;
2607 }
2608
2609 /* This will walk through all of the app dirs and load all the .desktop
2610  * files into the cache for the menu. The .desktop files will have their
2611  * allocated flag set to 0 */
2612 static int
2613 efreet_menu_process_dirs(Efreet_Menu_Internal *internal)
2614 {
2615     Eina_List *l;
2616
2617     /* Scan application directories for .desktop files */
2618     if (!efreet_menu_app_dirs_process(internal))
2619         return 0;
2620
2621     /* Scan directory directories for .directory file */
2622     if (!efreet_menu_directory_dirs_process(internal))
2623         return 0;
2624
2625     if (internal->sub_menus)
2626     {
2627         Efreet_Menu_Internal *sub_internal;
2628
2629         EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
2630         {
2631             sub_internal->parent = internal;
2632             efreet_menu_process_dirs(sub_internal);
2633         }
2634     }
2635
2636     return 1;
2637 }
2638
2639 /**
2640  * @internal
2641  * @param menu: the menu to process
2642  * @param only_unallocated: Only handle menus taht deal with unallocated items
2643  * @return Returns no value
2644  * @brief Handles the processing of the filters attached to the given menu.
2645  *
2646  * For each include filter we'll add the items to our applications array. Each
2647  * exclude filter will remove items from the applications array
2648  */
2649 static void
2650 efreet_menu_process_filters(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2651 {
2652     Efreet_Menu_Filter *filter;
2653     Efreet_Menu_Desktop *md;
2654     Eina_List *l, *ll;
2655
2656     int included = 0;
2657
2658     /* nothing to do if we're checking the other option */
2659     if (only_unallocated != internal->only_unallocated) return;
2660
2661     while (internal->applications)
2662         internal->applications = eina_list_remove_list(internal->applications,
2663                                                        internal->applications);
2664
2665     if (!internal->filters) return;
2666
2667     EINA_LIST_FOREACH(internal->filters, l, filter)
2668     {
2669         /* skip excludes until we get an include */
2670         if (!included && (filter->type == EFREET_MENU_FILTER_EXCLUDE))
2671             continue;
2672         included = 1;
2673
2674         if (filter->type == EFREET_MENU_FILTER_INCLUDE)
2675         {
2676             Eina_Hash *matches;
2677
2678             matches = eina_hash_string_superfast_new(NULL);
2679             internal->applications = efreet_menu_process_app_pool(internal->app_pool, internal->applications,
2680                                         matches, filter, internal->only_unallocated);
2681             if (internal->parent)
2682             {
2683                 Efreet_Menu_Internal *parent;
2684
2685                 parent = internal->parent;
2686                 do {
2687                     internal->applications = efreet_menu_process_app_pool(parent->app_pool,
2688                                                 internal->applications, matches, filter,
2689                                                 internal->only_unallocated);
2690                 } while ((parent = parent->parent));
2691             }
2692             eina_hash_free(matches);
2693         }
2694         else
2695         {
2696             /* check each item in our menu so far and see if it's excluded */
2697             l = internal->applications;
2698             while ((md = eina_list_data_get(l)))
2699             {
2700                 ll = eina_list_next(l);
2701                 if (efreet_menu_filter_matches(filter->op, md))
2702                     internal->applications = eina_list_remove_list(internal->applications, l);
2703                 l = ll;
2704             }
2705         }
2706     }
2707
2708     /* sort the menu applications. we do this in process filters so it will only
2709      * be done once per menu.*/
2710     if (internal->applications)
2711     {
2712         int count;
2713
2714         count = eina_list_count(internal->applications);
2715         if (count)
2716         {
2717             Ecore_Sheap *sheap;
2718             Efreet_Menu_Desktop *md;
2719
2720             sheap = ecore_sheap_new(
2721                         ECORE_COMPARE_CB(efreet_menu_cb_md_compare), count);
2722             while ((md = eina_list_data_get(internal->applications)))
2723             {
2724                 internal->applications = eina_list_remove_list(internal->applications,
2725                                                                internal->applications);
2726                 ecore_sheap_insert(sheap, md);
2727             }
2728             while ((md = ecore_sheap_extract(sheap)))
2729             {
2730                 if (md->desktop->no_display) continue;
2731                 internal->applications = eina_list_append(internal->applications, md);
2732             }
2733
2734             ecore_sheap_destroy(sheap);
2735         }
2736     }
2737 }
2738
2739 /**
2740  * @internal
2741  * @param pool: The app pool to iterate
2742  * @param applications: The list of applications to append too
2743  * @param matches: The hash of previously matched ids
2744  * @param filter: The menu filter to run on the pool items
2745  * @param only_unallocated: Do we check only unallocated pool items?
2746  * @return Returns no value.
2747  * @brief This will iterate the items in @a pool and append them to @a
2748  * applications if they match the @a filter given and aren't previoulsy entered
2749  * in @a matches. If @a only_unallocated is set we'll only only at the
2750  * .desktop files that haven't been previoulsy matched
2751  */
2752 static Eina_List *
2753 efreet_menu_process_app_pool(Eina_List *pool, Eina_List *applications,
2754                                   Eina_Hash *matches,
2755                                   Efreet_Menu_Filter *filter,
2756                                   unsigned int only_unallocated)
2757 {
2758     Efreet_Menu_Desktop *md;
2759     Eina_List *l;
2760
2761     if (!pool) return NULL;
2762
2763     EINA_LIST_FOREACH(pool, l, md)
2764     {
2765         if (eina_hash_find(matches, md->id)) continue;
2766         if (only_unallocated && md->allocated) continue;
2767         if (efreet_menu_filter_matches(filter->op, md))
2768         {
2769             applications = eina_list_append(applications, md);
2770             eina_hash_add(matches, (void *)md->id, md);
2771             md->allocated = 1;
2772         }
2773     }
2774     return applications;
2775 }
2776
2777 /**
2778  * @internal
2779  * @param op: The filter operation to execute
2780  * @param md: The desktop to run the filter on
2781  * @return Returns 1 if this desktop matches the given filter, 0 otherwise
2782  * @brief This will execute the given @a filter on the given desktop
2783  */
2784 static int
2785 efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2786 {
2787     if (op->type == EFREET_MENU_FILTER_OP_OR)
2788         return efreet_menu_filter_or_matches(op, md);
2789
2790     if (op->type == EFREET_MENU_FILTER_OP_AND)
2791         return efreet_menu_filter_and_matches(op, md);
2792
2793     if (op->type == EFREET_MENU_FILTER_OP_NOT)
2794         return efreet_menu_filter_not_matches(op, md);
2795
2796     return 0;
2797 }
2798
2799 /**
2800  * @internal
2801  * @param op: The filter operation to execute
2802  * @param md: The desktop to execute on
2803  * @return Returns 1 if the desktop matches, 0 otherwise
2804  * @brief Executes the OR operation, @a op, on the desktop, @a md.
2805  */
2806 static int
2807 efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2808 {
2809     Efreet_Menu_Filter_Op *child;
2810     Eina_List *l;
2811     char *t;
2812
2813     if (op->all) return 1;
2814
2815     if (op->categories && md->desktop->categories)
2816     {
2817         EINA_LIST_FOREACH(op->categories, l, t)
2818         {
2819             if (eina_list_search_unsorted(md->desktop->categories,
2820                                           (Eina_Compare_Cb)strcmp, t))
2821                 return 1;
2822         }
2823     }
2824
2825     if (op->filenames)
2826     {
2827         EINA_LIST_FOREACH(op->filenames, l, t)
2828             if (!strcmp(t, md->id)) return 1;
2829     }
2830
2831     if (op->filters)
2832     {
2833         EINA_LIST_FOREACH(op->filters, l, child)
2834         {
2835             if (efreet_menu_filter_matches(child, md))
2836                 return 1;
2837         }
2838     }
2839
2840     return 0;
2841 }
2842
2843 /**
2844  * @internal
2845  * @param op: The filter operation to execute
2846  * @param md: The desktop to execute on
2847  * @return Returns 1 if the desktop matches, 0 otherwise
2848  * @brief Executes the AND operation, @a op, on the desktop, @a md.
2849  */
2850 static int
2851 efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2852 {
2853     Efreet_Menu_Filter_Op *child;
2854     Eina_List *l;
2855     char *t;
2856
2857     if (op->categories)
2858     {
2859         if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
2860             return 0;
2861
2862         EINA_LIST_FOREACH(op->categories, l, t)
2863         {
2864             if (!eina_list_search_unsorted(md->desktop->categories,
2865                                            (Eina_Compare_Cb)strcmp, t))
2866                 return 0;
2867         }
2868     }
2869
2870     if (op->filenames)
2871     {
2872         EINA_LIST_FOREACH(op->filenames, l, t)
2873         {
2874             if (strcmp(t, md->id)) return 0;
2875         }
2876     }
2877
2878     if (op->filters)
2879     {
2880         EINA_LIST_FOREACH(op->filters, l, child)
2881         {
2882             if (!efreet_menu_filter_matches(child, md))
2883                 return 0;
2884         }
2885     }
2886
2887     return 1;
2888 }
2889
2890 /**
2891  * @internal
2892  * @param op: The filter operation to execute
2893  * @param md: The desktop to execute on
2894  * @return Returns 1 if the desktop matches, 0 otherwise
2895  * @brief Executes the NOT operation, @a op, on the desktop, @a md.
2896  */
2897 static int
2898 efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2899 {
2900     Efreet_Menu_Filter_Op *child;
2901     Eina_List *l;
2902     char *t;
2903
2904     /* !all means no desktops match */
2905     if (op->all) return 0;
2906
2907     if (op->categories)
2908     {
2909         if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
2910             return 1;
2911
2912         EINA_LIST_FOREACH(op->categories, l, t) 
2913         {
2914             if (eina_list_search_unsorted(md->desktop->categories,
2915                                           (Eina_Compare_Cb)strcmp, t))
2916                 return 0;
2917         }
2918     }
2919
2920     if (op->filenames)
2921     {
2922         EINA_LIST_FOREACH(op->filenames, l, t)
2923         {
2924             if (!strcmp(t, md->id)) return 0;
2925         }
2926     }
2927
2928     if (op->filters)
2929     {
2930         EINA_LIST_FOREACH(op->filters, l, child)
2931         {
2932             if (efreet_menu_filter_matches(child, md))
2933                 return 0;
2934         }
2935     }
2936
2937     return 1;
2938 }
2939
2940 /**
2941  * @internal
2942  * @param dest: The destination menu
2943  * @param src: The source menu
2944  * @return Returns no value
2945  * @brief Takes the child elements of the menu @a src and puts then on the
2946  * _start_ of the menu @a dest.
2947  */
2948 static void
2949 efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src)
2950 {
2951     Efreet_Menu_Internal *submenu;
2952
2953     if (!dest || !src) return;
2954
2955     if (!dest->directory && src->directory)
2956     {
2957         dest->directory = src->directory;
2958         src->directory = NULL;
2959     }
2960
2961     if (!dest->seen_allocated && src->seen_allocated)
2962     {
2963         dest->only_unallocated = src->only_unallocated;
2964         dest->seen_allocated = 1;
2965     }
2966
2967     if (!dest->seen_deleted && src->seen_deleted)
2968     {
2969         dest->deleted = src->deleted;
2970         dest->seen_deleted = 1;
2971     }
2972
2973     if (src->directories)
2974     {
2975         efreet_menu_create_directories_list(dest);
2976         ecore_dlist_prepend_list(dest->directories, src->directories);
2977     }
2978
2979     if (src->app_dirs)
2980     {
2981         efreet_menu_create_app_dirs_list(dest);
2982         dest->app_dirs = eina_list_merge(src->app_dirs, dest->app_dirs);
2983         src->app_dirs = NULL;
2984     }
2985
2986     if (src->directory_dirs)
2987     {
2988         efreet_menu_create_directory_dirs_list(dest);
2989         ecore_dlist_prepend_list(dest->directory_dirs, src->directory_dirs);
2990     }
2991
2992     if (src->moves)
2993     {
2994         efreet_menu_create_move_list(dest);
2995         dest->moves = eina_list_merge(src->moves, dest->moves);
2996         src->moves = NULL;
2997     }
2998
2999     if (src->filters)
3000     {
3001         efreet_menu_create_filter_list(dest);
3002         dest->filters = eina_list_merge(src->filters, dest->filters);
3003         src->filters = NULL;
3004     }
3005
3006     if (src->sub_menus)
3007     {
3008         efreet_menu_create_sub_menu_list(dest);
3009
3010         while ((submenu = eina_list_data_get(eina_list_last(src->sub_menus))))
3011         {
3012             Efreet_Menu_Internal *match;
3013
3014             src->sub_menus = eina_list_remove(src->sub_menus, submenu);
3015             /* if this menu is in the list already we just add to that */
3016             if ((match = eina_list_search_unsorted(dest->sub_menus,
3017                                                    (Eina_Compare_Cb)efreet_menu_cb_menu_compare,
3018                                                    submenu)))
3019             {
3020                 efreet_menu_concatenate(match, submenu);
3021                 efreet_menu_internal_free(submenu);
3022             }
3023             else
3024                 dest->sub_menus = eina_list_prepend(dest->sub_menus, submenu);
3025         }
3026     }
3027 }
3028
3029 /**
3030  * @internal
3031  * @param menu: The menu to work with
3032  * @return Returns no value
3033  * @brief Handles any \<Move\> commands in the menus
3034  */
3035 static void
3036 efreet_menu_resolve_moves(Efreet_Menu_Internal *internal)
3037 {
3038     Efreet_Menu_Internal *child;
3039     Efreet_Menu_Move *move;
3040     Eina_List *l;
3041
3042     /* child moves are handled before parent moves */
3043     if (internal->sub_menus)
3044     {
3045         EINA_LIST_FOREACH(internal->sub_menus, l, child)
3046             efreet_menu_resolve_moves(child);
3047     }
3048
3049     /* nothing to do if this menu has no moves */
3050     if (!internal->moves) return;
3051
3052     EINA_LIST_FOREACH(internal->moves, l, move)
3053     {
3054         Efreet_Menu_Internal *origin, *dest, *parent;
3055
3056         /* if the origin path doesn't exist we do nothing */
3057         origin = efreet_menu_by_name_find(internal, move->old_name, &parent);
3058         if (!origin) continue;
3059
3060         /* remove the origin menu from the parent */
3061         parent->sub_menus = eina_list_remove(parent->sub_menus, origin);
3062
3063         /* if the destination path doesn't exist we just rename the origin
3064          * menu and append to the parents list of children */
3065         dest = efreet_menu_by_name_find(internal, move->new_name, &parent);
3066         if (!dest)
3067         {
3068             char *path, *tmp, *t;
3069
3070             /* if the dest path has /'s in it then we need to add menus to
3071              * fill out the paths */
3072             t = strdup(move->new_name);
3073             tmp = t;
3074             path = strchr(tmp, '/');
3075             while (path)
3076             {
3077                 Efreet_Menu_Internal *ancestor;
3078
3079                 *path = '\0';
3080
3081                 ancestor = efreet_menu_internal_new();
3082                 ancestor->name.internal = eina_stringshare_add(tmp);
3083
3084                 efreet_menu_create_sub_menu_list(parent);
3085                 parent->sub_menus = eina_list_append(parent->sub_menus, ancestor);
3086
3087                 parent = ancestor;
3088                 tmp = ++path;
3089                 path = strchr(tmp, '/');
3090             }
3091
3092             IF_RELEASE(origin->name.internal);
3093             origin->name.internal = eina_stringshare_add(tmp);
3094
3095             efreet_menu_create_sub_menu_list(parent);
3096             parent->sub_menus = eina_list_append(parent->sub_menus, origin);
3097
3098             FREE(t);
3099         }
3100         else
3101         {
3102             efreet_menu_concatenate(dest, origin);
3103             efreet_menu_internal_free(origin);
3104         }
3105     }
3106     IF_FREE_LIST(internal->moves, efreet_menu_move_free);
3107 }
3108
3109 /**
3110  * @internal
3111  * @param menu: The menu to start searching from
3112  * @param name: The menu name to find
3113  * @param parent: The parent of the found menu
3114  * @return Returns the menu with the given @a name or NULL if none found
3115  * @brief Searches the menu tree starting at @a menu looking for a menu with
3116  * @a name.
3117  */
3118 static Efreet_Menu_Internal *
3119 efreet_menu_by_name_find(Efreet_Menu_Internal *internal, const char *name, Efreet_Menu_Internal **parent)
3120 {
3121     char *part, *tmp, *ptr;
3122
3123     if (parent) *parent = internal;
3124
3125     /* find the correct parent menu */
3126     tmp = strdup(name);
3127     ptr = tmp;
3128     part = strchr(ptr, '/');
3129     while (part)
3130     {
3131         *part = '\0';
3132
3133         if (!(internal = eina_list_search_unsorted(internal->sub_menus,
3134                                                    (Eina_Compare_Cb)efreet_menu_cb_compare_names,
3135                                                    ptr)))
3136         {
3137             FREE(tmp);
3138             return NULL;
3139         }
3140
3141         ptr = ++part;
3142         part = strchr(ptr, '/');
3143     }
3144
3145     if (parent) *parent = internal;
3146
3147     /* find the menu in the parent list */
3148     if (!(internal = eina_list_search_unsorted(internal->sub_menus,
3149                                                (Eina_Compare_Cb)efreet_menu_cb_compare_names,
3150                                                ptr)))
3151     {
3152         FREE(tmp);
3153         return NULL;
3154     }
3155
3156     FREE(tmp);
3157     return internal;
3158 }
3159
3160 static void
3161 efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path)
3162 {
3163     char *tmp, *p;
3164
3165     tmp = strdup(path);
3166     p = strrchr(tmp, '/');
3167     if (p)
3168     {
3169         *p = '\0';
3170         p++;
3171
3172         internal->file.path = strdup(tmp);
3173         internal->file.name = strdup(p);
3174     }
3175     FREE(tmp);
3176 }
3177
3178 /**
3179  * @internal
3180  * @return Returns a new Efreet_Menu_Move struct on success or NULL on failure
3181  * @brief Creates an returns a new Efreet_Menu_Move struct or NULL on failure
3182  */
3183 static Efreet_Menu_Move *
3184 efreet_menu_move_new(void)
3185 {
3186     Efreet_Menu_Move *move;
3187
3188     move = NEW(Efreet_Menu_Move, 1);
3189
3190     return move;
3191 }
3192
3193 /**
3194  * @internal
3195  * @param move: The Efreet_Menu_Move to free
3196  * @return Returns no value.
3197  * @brief Frees the given move structure
3198  */
3199 static void
3200 efreet_menu_move_free(Efreet_Menu_Move *move)
3201 {
3202     if (!move) return;
3203
3204     IF_FREE(move->old_name);
3205     IF_FREE(move->new_name);
3206
3207     FREE(move);
3208 }
3209
3210 /**
3211  * @internal
3212  * @return Returns a new Efreet_Menu_App_Dir on success or NULL on failure
3213  * @brief Creates and initializes a new Efreet_Menu_App_Dir structure
3214  */
3215 static Efreet_Menu_App_Dir *
3216 efreet_menu_app_dir_new(void)
3217 {
3218     Efreet_Menu_App_Dir *dir;
3219
3220     dir = NEW(Efreet_Menu_App_Dir, 1);
3221
3222     return dir;
3223 }
3224
3225 /**
3226  * @internal
3227  * @param dir: The Efreet_Menu_App_Dir to free
3228  * @return Returns no value.
3229  * @brief Frees the given dir structure
3230  */
3231 static void
3232 efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir)
3233 {
3234     if (!dir) return;
3235
3236     IF_FREE(dir->path);
3237     IF_FREE(dir->prefix);
3238     FREE(dir);
3239 }
3240
3241 /**
3242  * @internal
3243  * @param a: The app dir to compare too
3244  * @param b: The path to compare too
3245  * @return Returns 1 if the strings are equals, 0 otherwise
3246  * @brief Compares the too strings
3247  */
3248 static int
3249 efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b)
3250 {
3251     return ecore_str_compare(a->path, b);
3252 }
3253
3254 static void
3255 efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal)
3256 {
3257     if (!internal || internal->sub_menus) return;
3258
3259     internal->sub_menus = NULL;
3260 }
3261
3262 static void
3263 efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal)
3264 {
3265     if (!internal || internal->app_dirs) return;
3266
3267     internal->app_dirs = NULL;
3268 }
3269
3270 static void
3271 efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal)
3272 {
3273     if (!internal || internal->directory_dirs) return;
3274
3275     internal->directory_dirs = ecore_dlist_new();
3276     ecore_dlist_free_cb_set(internal->directory_dirs, free);
3277 }
3278
3279 static void
3280 efreet_menu_create_move_list(Efreet_Menu_Internal *internal)
3281 {
3282     if (!internal || internal->moves) return;
3283
3284     internal->moves = NULL;
3285 }
3286
3287 static void
3288 efreet_menu_create_filter_list(Efreet_Menu_Internal *internal)
3289 {
3290     if (!internal || internal->filters) return;
3291
3292     internal->filters = NULL;
3293 }
3294
3295 static void
3296 efreet_menu_create_layout_list(Efreet_Menu_Internal *internal)
3297 {
3298     if (!internal || internal->layout) return;
3299
3300     internal->layout = NULL;
3301 }
3302
3303 static void
3304 efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal)
3305 {
3306     if (!internal || internal->default_layout) return;
3307
3308     internal->default_layout = NULL;
3309 }
3310
3311 static void
3312 efreet_menu_create_directories_list(Efreet_Menu_Internal *internal)
3313 {
3314     if (!internal || internal->directories) return;
3315
3316     internal->directories = ecore_dlist_new();
3317     ecore_dlist_free_cb_set(internal->directories, free);
3318 }
3319
3320 static char *
3321 efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix)
3322 {
3323     char path[PATH_MAX];
3324     size_t len;
3325
3326     /* see if we've got an absolute or relative path */
3327     if (suffix[0] == '/')
3328         snprintf(path, sizeof(path), "%s", suffix);
3329
3330     else
3331     {
3332         if (!internal->file.path)
3333         {
3334             printf("efreet_menu_handle_app_dir() missing menu path ...\n");
3335             return NULL;
3336         }
3337         snprintf(path, sizeof(path), "%s/%s", internal->file.path, suffix);
3338     }
3339
3340     len = strlen(path);
3341     while (path[len] == '/') path[len--] = '\0';
3342
3343     return strdup(path);
3344 }
3345
3346 static int
3347 efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b)
3348 {
3349     return ecore_str_compare(a->name.internal, b->name.internal);
3350 }
3351
3352 static int
3353 efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal)
3354 {
3355     Efreet_Menu_App_Dir *app_dir;
3356     Efreet_Menu_Desktop *md;
3357     Eina_List *l;
3358
3359     EINA_LIST_FREE(internal->app_pool, md)
3360       efreet_menu_desktop_free(md);
3361
3362     EINA_LIST_FOREACH(internal->app_dirs, l, app_dir)
3363             efreet_menu_app_dir_scan(internal, app_dir->path, app_dir->prefix, app_dir->legacy);
3364
3365     return 1;
3366 }
3367
3368 static int
3369 efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal, const char *path, const char *id, int legacy)
3370 {
3371     Efreet_Desktop *desktop;
3372     Efreet_Menu_Desktop *menu_desktop;
3373     DIR *files;
3374     char buf[PATH_MAX], buf2[PATH_MAX];
3375     struct dirent *file;
3376     char *ext;
3377
3378     files = opendir(path);
3379     if (!files) return 1;
3380
3381     while ((file = readdir(files)))
3382     {
3383         if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
3384         snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name);
3385         if (id)
3386             snprintf(buf2, PATH_MAX, "%s-%s", id, file->d_name);
3387         else
3388             strcpy(buf2, file->d_name);
3389
3390         if (ecore_file_is_dir(buf))
3391         {
3392             if (!legacy)
3393                 efreet_menu_app_dir_scan(internal, buf, buf2, legacy);
3394         }
3395         else
3396         {
3397             ext = strrchr(buf, '.');
3398
3399             if (!ext || strcmp(ext, ".desktop")) continue;
3400             desktop = efreet_desktop_get(buf);
3401
3402             if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_APPLICATION)
3403             {
3404                 if (desktop) efreet_desktop_free(desktop);
3405                 continue;
3406             }
3407             /* Don't add two files with the same id in the app pool */
3408             if (eina_list_search_unsorted(internal->app_pool,
3409                                           (Eina_Compare_Cb)efreet_menu_cb_md_compare_ids,
3410                                           buf2))
3411                 continue;
3412
3413             menu_desktop = efreet_menu_desktop_new();
3414             menu_desktop->desktop = desktop;
3415             menu_desktop->id = eina_stringshare_add(buf2);
3416             internal->app_pool = eina_list_prepend(internal->app_pool, menu_desktop);
3417         }
3418     }
3419     closedir(files);
3420
3421     return 1;
3422 }
3423
3424 /**
3425  * @internal
3426  * @param menu: The menu to work with
3427  * @return Returns 1 on success or 0 on failure
3428  * @brief Process the directory dirs in @a menu
3429  */
3430 static int
3431 efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal)
3432 {
3433     const char *path;
3434
3435     if (internal->directory_dirs)
3436     {
3437         internal->directory_cache =
3438           eina_hash_string_superfast_new(EINA_FREE_CB(efreet_desktop_free));
3439
3440         ecore_dlist_last_goto(internal->directory_dirs);
3441         while ((path = ecore_dlist_previous(internal->directory_dirs)))
3442             efreet_menu_directory_dir_scan(path, NULL, internal->directory_cache);
3443     }
3444
3445     if (internal->directories)
3446     {
3447         ecore_dlist_last_goto(internal->directories);
3448         while ((path = ecore_dlist_previous(internal->directories)))
3449         {
3450             internal->directory = efreet_menu_directory_get(internal, path);
3451             if (internal->directory) break;
3452         }
3453     }
3454     if (!internal->directory)
3455         internal->name.name = internal->name.internal;
3456     else
3457         internal->name.name = internal->directory->name;
3458
3459     return 1;
3460 }
3461
3462 /**
3463  * @internal
3464  * @param path: The path to scan
3465  * @param relative_path: The relative portion of the path
3466  * @param cache: The cache to populate
3467  * @return Returns 1 on success or 0 on failure
3468  * @brief Scans the given directory dir for .directory files and adds the
3469  * applications to the cache
3470  */
3471 static int
3472 efreet_menu_directory_dir_scan(const char *path, const char *relative_path,
3473                                Eina_Hash *cache)
3474 {
3475     Efreet_Desktop *desktop;
3476     DIR *files;
3477     char buf[PATH_MAX], buf2[PATH_MAX];
3478     struct dirent *file;
3479     char *ext;
3480
3481     files = opendir(path);
3482     if (!files) return 1;
3483
3484     while ((file = readdir(files)))
3485     {
3486         if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
3487         snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name);
3488         if (relative_path)
3489             snprintf(buf2, PATH_MAX, "%s/%s", relative_path, file->d_name);
3490         else
3491             strcpy(buf2, file->d_name);
3492
3493         if (ecore_file_is_dir(buf))
3494             efreet_menu_directory_dir_scan(buf, buf2, cache);
3495
3496         else
3497         {
3498             ext = strrchr(buf, '.');
3499             if (!ext || strcmp(ext, ".directory")) continue;
3500
3501             desktop = efreet_desktop_get(buf);
3502             if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_DIRECTORY)
3503             {
3504                 efreet_desktop_free(desktop);
3505                 continue;
3506             }
3507
3508             eina_hash_del(cache, buf2, NULL);
3509             eina_hash_add(cache, buf2, desktop);
3510         }
3511     }
3512     closedir(files);
3513
3514     return 1;
3515 }
3516
3517 /**
3518  * @internal
3519  * @param menu: The menu to work with
3520  * @param path: The path to work with
3521  * @return Returns the desktop file for this path or NULL if none exists
3522  * @brief Finds the desktop file for the given path.
3523  */
3524 static Efreet_Desktop *
3525 efreet_menu_directory_get(Efreet_Menu_Internal *internal, const char *path)
3526 {
3527     Efreet_Desktop *dir;
3528
3529     if (internal->directory_cache)
3530     {
3531         dir = eina_hash_find(internal->directory_cache, path);
3532         if (dir) return dir;
3533     }
3534
3535     if (internal->parent)
3536         return efreet_menu_directory_get(internal->parent, path);
3537
3538     return NULL;
3539 }
3540
3541 /**
3542  * @internal
3543  * @param a: The first desktop
3544  * @param b: The second desktop
3545  * @return Returns the comparison of the desktop files
3546  * @brief Compares the desktop files.
3547  */
3548 static int
3549 efreet_menu_cb_md_compare(Efreet_Menu_Desktop *a, Efreet_Menu_Desktop *b)
3550 {
3551 #ifdef STRICT_SPEC
3552     return strcmp(ecore_file_file_get(a->desktop->orig_path), ecore_file_file_get(b->desktop->orig_path));
3553 #else
3554     return strcasecmp(a->desktop->name, b->desktop->name);
3555 #endif
3556 }
3557
3558 static int
3559 efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name)
3560 {
3561     return strcmp(internal->name.internal, name);
3562 }
3563
3564 static int
3565 efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name)
3566 {
3567     return strcmp(md->id, name);
3568 }
3569
3570 static Efreet_Menu *
3571 efreet_menu_layout_menu(Efreet_Menu_Internal *internal)
3572 {
3573     Efreet_Menu *entry;
3574     Eina_List *layout = NULL;
3575     Eina_List *l;
3576
3577     if (internal->parent)
3578     {
3579         /* Copy default layout rules */
3580         if (internal->show_empty == -1)    internal->show_empty = internal->parent->show_empty;
3581         if (internal->in_line == -1)       internal->in_line = internal->parent->in_line;
3582         if (internal->inline_limit == -1)  internal->inline_limit = internal->parent->inline_limit;
3583         if (internal->inline_header == -1) internal->inline_header = internal->parent->inline_header;
3584         if (internal->inline_alias == -1)  internal->inline_alias = internal->parent->inline_alias;
3585     }
3586
3587     if (internal->layout)
3588         layout = internal->layout;
3589
3590     else if (internal->parent)
3591     {
3592         Efreet_Menu_Internal *parent;
3593         parent = internal->parent;
3594         do
3595         {
3596             layout = parent->default_layout;
3597             parent = parent->parent;
3598         } while (!layout && parent);
3599     }
3600
3601     /* init entry */
3602     entry = efreet_menu_entry_new();
3603     entry->type = EFREET_MENU_ENTRY_MENU;
3604     entry->id = eina_stringshare_add(internal->name.internal);
3605     entry->name = eina_stringshare_add(internal->name.name);
3606     if (internal->directory)
3607     {
3608         entry->icon = eina_stringshare_add(internal->directory->icon);
3609         efreet_desktop_ref(internal->directory);
3610         entry->desktop = internal->directory;
3611     }
3612     entry->entries = NULL;
3613
3614 #if 1 //STRICT_SPEC
3615     if (internal->sub_menus)
3616     {
3617         internal->sub_menus = eina_list_sort(internal->sub_menus,
3618                                              0,
3619                                              (Eina_Compare_Cb)efreet_menu_cb_menu_compare);
3620     }
3621 #endif
3622
3623     if (layout)
3624     {
3625         Efreet_Menu_Layout *lay;
3626
3627         EINA_LIST_FOREACH(layout, l, lay)
3628             efreet_menu_layout_entries_get(entry, internal, lay);
3629     }
3630     else
3631     {
3632         /* Default layout, first menus, then desktop */
3633         if (internal->sub_menus)
3634         {
3635             Efreet_Menu_Internal *sub;
3636
3637             EINA_LIST_FOREACH(internal->sub_menus, l, sub)
3638             {
3639                 Efreet_Menu *sub_entry;
3640                 if ((sub->directory && sub->directory->no_display) || sub->deleted) continue;
3641                 sub_entry = efreet_menu_layout_menu(sub);
3642                 /* Don't show empty menus */
3643                 if (!sub_entry->entries)
3644                 {
3645                     efreet_menu_free(sub_entry);
3646                     continue;
3647                 }
3648                 entry->entries = eina_list_append(entry->entries, sub_entry);
3649             }
3650         }
3651
3652         if (internal->applications)
3653         {
3654             Efreet_Menu_Desktop *md;
3655
3656             EINA_LIST_FOREACH(internal->applications, l, md)
3657             {
3658                 Efreet_Menu *sub_entry;
3659                 sub_entry = efreet_menu_layout_desktop(md);
3660                 entry->entries = eina_list_append(entry->entries, sub_entry);
3661             }
3662         }
3663     }
3664
3665     /* Don't keep this list around if it is empty */
3666
3667     return entry;
3668 }
3669
3670 static Efreet_Menu *
3671 efreet_menu_layout_desktop(Efreet_Menu_Desktop *md)
3672 {
3673     Efreet_Menu *entry;
3674
3675     /* init entry */
3676     entry = efreet_menu_entry_new();
3677     entry->type = EFREET_MENU_ENTRY_DESKTOP;
3678     entry->id = eina_stringshare_add(md->id);
3679     entry->name = eina_stringshare_add(md->desktop->name);
3680     if (md->desktop->icon) entry->icon = eina_stringshare_add(md->desktop->icon);
3681     efreet_desktop_ref(md->desktop);
3682     entry->desktop = md->desktop;
3683
3684     return entry;
3685 }
3686
3687 static void
3688 efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal,
3689         Efreet_Menu_Layout *layout)
3690 {
3691     Efreet_Menu *sub_entry;
3692
3693     if (internal->sub_menus && layout->type == EFREET_MENU_LAYOUT_MENUNAME)
3694     {
3695         Efreet_Menu_Internal *sub;
3696
3697         /* Efreet_Menu_Layout might be from DefaultLayout, so we need a local copy */
3698         int show_empty, in_line, inline_limit, inline_header, inline_alias;
3699
3700         if (layout->show_empty == -1) show_empty = internal->show_empty;
3701         else show_empty = layout->show_empty;
3702
3703         if (layout->in_line == -1) in_line = internal->in_line;
3704         else in_line = layout->in_line;
3705
3706         if (layout->inline_limit == -1) inline_limit = internal->inline_limit;
3707         else inline_limit = layout->inline_limit;
3708
3709         if (layout->inline_header == -1) inline_header = internal->inline_header;
3710         else inline_header = layout->inline_header;
3711
3712         if (layout->inline_alias == -1) inline_alias = internal->inline_alias;
3713         else inline_alias = layout->inline_alias;
3714
3715         sub = eina_list_search_unsorted(internal->sub_menus,
3716                                         (Eina_Compare_Cb)efreet_menu_cb_compare_names, layout->name);
3717         if (sub)
3718         {
3719             if (!(sub->directory && sub->directory->no_display) && !sub->deleted)
3720             {
3721                 sub_entry = efreet_menu_layout_menu(sub);
3722                 if (!show_empty && efreet_menu_layout_is_empty(sub_entry))
3723                     efreet_menu_free(sub_entry);
3724                 else if (in_line &&
3725                         ((inline_limit == 0) ||
3726                          (!sub_entry->entries || (eina_list_count(sub_entry->entries) <= inline_limit))))
3727                 {
3728                     /* Inline */
3729                     if (!sub_entry->entries)
3730                     {
3731                         /* Can't inline an empty submenu */
3732                         entry->entries = eina_list_append(entry->entries, sub_entry);
3733                     }
3734                     else if (inline_alias && (eina_list_count(sub_entry->entries) == 1))
3735                     {
3736                         Efreet_Menu *tmp;
3737
3738                         tmp = eina_list_data_get(sub_entry->entries);
3739                         sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3740                         IF_RELEASE(tmp->name);
3741                         tmp->name = sub_entry->name;
3742                         sub_entry->name = NULL;
3743                         IF_RELEASE(tmp->icon);
3744                         tmp->icon = sub_entry->icon;
3745                         sub_entry->icon = NULL;
3746                         entry->entries = eina_list_append(entry->entries, tmp);
3747                         efreet_menu_free(sub_entry);
3748                     }
3749                     else
3750                     {
3751                         Efreet_Menu *tmp;
3752
3753                         if (inline_header)
3754                         {
3755                             tmp = efreet_menu_entry_new();
3756                             tmp->type = EFREET_MENU_ENTRY_HEADER;
3757                             tmp->name = sub_entry->name;
3758                             sub_entry->name = NULL;
3759                             tmp->icon = sub_entry->icon;
3760                             sub_entry->icon = NULL;
3761                             entry->entries = eina_list_append(entry->entries, tmp);
3762                         }
3763                         while ((tmp = eina_list_data_get(sub_entry->entries)))
3764                         {
3765                             sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3766                             entry->entries = eina_list_append(entry->entries, tmp);
3767                         }
3768                         efreet_menu_free(sub_entry);
3769                     }
3770                 }
3771                 else
3772                     entry->entries = eina_list_append(entry->entries, sub_entry);
3773             }
3774             internal->sub_menus = eina_list_remove(internal->sub_menus, sub);
3775             efreet_menu_internal_free(sub);
3776         }
3777     }
3778     else if (internal->applications && layout->type == EFREET_MENU_LAYOUT_FILENAME)
3779     {
3780         Efreet_Menu_Desktop *md;
3781         md = eina_list_search_unsorted(internal->applications,
3782                                        (Eina_Compare_Cb)efreet_menu_cb_md_compare_ids, layout->name);
3783         if (md)
3784         {
3785             sub_entry = efreet_menu_layout_desktop(md);
3786             entry->entries = eina_list_append(entry->entries, sub_entry);
3787             internal->applications = eina_list_remove(internal->applications, md);
3788         }
3789     }
3790     else if (layout->type == EFREET_MENU_LAYOUT_MERGE)
3791     {
3792         if (internal->applications && !strcmp(layout->name, "files"))
3793         {
3794             Efreet_Menu_Desktop *md;
3795
3796             while ((md = eina_list_data_get(internal->applications)))
3797             {
3798                 internal->applications = eina_list_remove_list(internal->applications,
3799                                                                internal->applications);
3800                 sub_entry = eina_list_search_unsorted(entry->entries,
3801                                                       (Eina_Compare_Cb)efreet_menu_cb_entry_compare_desktop,
3802                                                       md->desktop);
3803                 if (!sub_entry)
3804                 {
3805                     sub_entry = efreet_menu_layout_desktop(md);
3806                     entry->entries = eina_list_append(entry->entries, sub_entry);
3807                 }
3808             }
3809             internal->applications = eina_list_free(internal->applications);
3810         }
3811         else if (internal->sub_menus && !strcmp(layout->name, "menus"))
3812         {
3813             Efreet_Menu_Internal *sub;
3814
3815             while ((sub = eina_list_data_get(internal->sub_menus)))
3816             {
3817                 internal->sub_menus = eina_list_remove_list(internal->sub_menus, internal->sub_menus);
3818                 if ((sub->directory && sub->directory->no_display) || sub->deleted)
3819                 {
3820                     efreet_menu_internal_free(sub);
3821                     continue;
3822                 }
3823                 sub_entry = eina_list_search_unsorted(entry->entries,
3824                                                       (Eina_Compare_Cb)efreet_menu_cb_entry_compare_menu,
3825                                                       sub);
3826                 if (!sub_entry)
3827                 {
3828                     sub_entry = efreet_menu_layout_menu(sub);
3829                     if (!internal->show_empty && efreet_menu_layout_is_empty(sub_entry))
3830                         efreet_menu_free(sub_entry);
3831                     else if (internal->in_line &&
3832                             ((internal->inline_limit == 0) ||
3833                              (!sub_entry->entries || (eina_list_count(sub_entry->entries) <= internal->inline_limit))))
3834                     {
3835                         /* Inline */
3836                         if (!sub_entry->entries)
3837                         {
3838                             /* Can't inline an empty submenu */
3839                             entry->entries = eina_list_append(entry->entries, sub_entry);
3840                         }
3841                         else if (internal->inline_alias && (eina_list_count(sub_entry->entries) == 1))
3842                         {
3843                             Efreet_Menu *tmp;
3844
3845                             tmp = eina_list_data_get(sub_entry->entries);
3846                             sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3847                             eina_stringshare_del(tmp->name);
3848                             tmp->name = sub_entry->name;
3849                             sub_entry->name = NULL;
3850                             IF_RELEASE(tmp->icon);
3851                             if (sub_entry->icon)
3852                             {
3853                                 tmp->icon = sub_entry->icon;
3854                                 sub_entry->icon = NULL;
3855                             }
3856                             entry->entries = eina_list_append(entry->entries, tmp);
3857                             efreet_menu_free(sub_entry);
3858                         }
3859                         else
3860                         {
3861                             Efreet_Menu *tmp;
3862
3863                             if (internal->inline_header)
3864                             {
3865                                 tmp = efreet_menu_entry_new();
3866                                 tmp->type = EFREET_MENU_ENTRY_HEADER;
3867                                 tmp->name = sub_entry->name;
3868                                 sub_entry->name = NULL;
3869                                 if (sub_entry->icon) tmp->icon = sub_entry->icon;
3870                                 sub_entry->icon = NULL;
3871                                 entry->entries = eina_list_append(entry->entries, tmp);
3872                             }
3873                             while ((tmp = eina_list_data_get(sub_entry->entries)))
3874                             {
3875                                 sub_entry->entries = eina_list_remove_list(sub_entry->entries,
3876                                                                            sub_entry->entries);
3877                                 entry->entries = eina_list_append(entry->entries, tmp);
3878                             }
3879                             efreet_menu_free(sub_entry);
3880                         }
3881                     }
3882                     else
3883                         entry->entries = eina_list_append(entry->entries, sub_entry);
3884                 }
3885                 efreet_menu_internal_free(sub);
3886             }
3887             IF_FREE_LIST(internal->sub_menus, efreet_menu_internal_free);
3888         }
3889         else if (internal->sub_menus && !strcmp(layout->name, "all"))
3890         {
3891             char *orig;
3892
3893             orig = layout->name;
3894             layout->name = "menus";
3895             efreet_menu_layout_entries_get(entry, internal, layout);
3896             layout->name = "files";
3897             efreet_menu_layout_entries_get(entry, internal, layout);
3898             layout->name = orig;
3899         }
3900     }
3901     else if (layout->type == EFREET_MENU_LAYOUT_SEPARATOR)
3902     {
3903         sub_entry = efreet_menu_entry_new();
3904         sub_entry->type = EFREET_MENU_ENTRY_SEPARATOR;
3905         entry->entries = eina_list_append(entry->entries, sub_entry);
3906     }
3907 }
3908
3909 static int
3910 efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal)
3911 {
3912     if (entry->type != EFREET_MENU_ENTRY_MENU) return 1;
3913     return ecore_str_compare(entry->name, internal->name.name);
3914 }
3915
3916 static int
3917 efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop)
3918 {
3919     if (entry->type != EFREET_MENU_ENTRY_DESKTOP) return -1;
3920     return ecore_str_compare(entry->name, desktop->name);
3921 }
3922
3923 static int
3924 efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old)
3925 {
3926     return ecore_str_compare(move->old_name, old);
3927 }
3928
3929 static int
3930 efreet_menu_layout_is_empty(Efreet_Menu *entry)
3931 {
3932     Efreet_Menu *sub_entry;
3933     Eina_List *l;
3934
3935     if (!entry->entries) return 1;
3936
3937     EINA_LIST_FOREACH(entry->entries, l, sub_entry)
3938     {
3939         if (sub_entry->type == EFREET_MENU_ENTRY_MENU) return 0;
3940         if (sub_entry->type == EFREET_MENU_ENTRY_DESKTOP) return 0;
3941     }
3942     return 1;
3943 }