c8e9a98c7d132c913ca68345d0933f5fca6ada92
[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
293 static Efreet_Menu_Filter_Op *efreet_menu_filter_op_new(void);
294 static void efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op);
295
296 static Efreet_Menu_Desktop *efreet_menu_desktop_new(void);
297 static void efreet_menu_desktop_free(Efreet_Menu_Desktop *md);
298
299 static Efreet_Menu *efreet_menu_entry_new(void);
300
301 static int efreet_menu_handle_menu(Efreet_Menu_Internal *internal, Efreet_Xml *xml);
302 static int efreet_menu_handle_name(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
303
304 static int efreet_menu_handle_sub_menu(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
305 static int efreet_menu_handle_app_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
306 static int efreet_menu_handle_default_app_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
307 static int efreet_menu_handle_directory_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
308 static int efreet_menu_handle_default_directory_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
309 static int efreet_menu_handle_directory(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
310 static int efreet_menu_handle_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
311 static int efreet_menu_handle_not_only_unallocated(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
312 static int efreet_menu_handle_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
313 static int efreet_menu_handle_not_deleted(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
314 static int efreet_menu_handle_include(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
315 static int efreet_menu_handle_exclude(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
316 static int efreet_menu_handle_filename(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
317 static int efreet_menu_handle_category(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
318 static int efreet_menu_handle_all(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
319 static int efreet_menu_handle_and(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
320 static int efreet_menu_handle_or(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
321 static int efreet_menu_handle_not(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
322 static int efreet_menu_handle_merge_file(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
323 static int efreet_menu_handle_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
324 static int efreet_menu_handle_default_merge_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
325 static int efreet_menu_handle_legacy_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
326 static Efreet_Menu_Internal *efreet_menu_handle_legacy_dir_helper(Efreet_Menu_Internal *root,
327                                                 Efreet_Menu_Internal *parent,
328                                                 const char *legacy_dir,
329                                                 const char *prefix);
330 static int efreet_menu_handle_kde_legacy_dirs(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
331 static int efreet_menu_handle_move(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
332 static int efreet_menu_handle_old(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
333 static int efreet_menu_handle_new(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
334 static int efreet_menu_handle_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
335 static int efreet_menu_handle_default_layout(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
336
337 static int efreet_menu_handle_filter(Efreet_Menu_Internal *parent, Efreet_Xml *xml,
338                                                     Efreet_Menu_Filter_Type type);
339 static int efreet_menu_handle_filter_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
340 static int efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
341                                                       Efreet_Menu_Filter_Op_Type type);
342
343 static int efreet_menu_handle_layout_menuname(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
344 static int efreet_menu_handle_layout_filename(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
345 static int efreet_menu_handle_layout_separator(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
346 static int efreet_menu_handle_layout_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
347
348 static int efreet_menu_merge(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path);
349 static int efreet_menu_merge_dir(Efreet_Menu_Internal *parent, Efreet_Xml *xml, const char *path);
350
351 static int efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b);
352
353 static void efreet_menu_resolve_moves(Efreet_Menu_Internal *internal);
354 static void efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src);
355
356 static int efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b);
357 static int efreet_menu_cb_md_compare(Efreet_Menu_Desktop *a, Efreet_Menu_Desktop *b);
358
359 static int efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent);
360 static int efreet_menu_save_indent(FILE *f, int indent);
361
362 static void efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path);
363
364 /**
365  * @return Returns 1 on success, 0 on failure
366  * @brief Initializes the Efreet Menu system.
367  */
368 int
369 efreet_menu_init(void)
370 {
371     int i;
372
373     struct
374     {
375         char *key;
376         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
377     } menu_cbs[] = {
378         {"Menu", efreet_menu_handle_sub_menu},
379         {"AppDir", efreet_menu_handle_app_dir},
380         {"DefaultAppDirs", efreet_menu_handle_default_app_dirs},
381         {"DirectoryDir", efreet_menu_handle_directory_dir},
382         {"DefaultDirectoryDirs", efreet_menu_handle_default_directory_dirs},
383         {"Name", efreet_menu_handle_name},
384         {"Directory", efreet_menu_handle_directory},
385         {"OnlyUnallocated", efreet_menu_handle_only_unallocated},
386         {"NotOnlyUnallocated", efreet_menu_handle_not_only_unallocated},
387         {"Deleted", efreet_menu_handle_deleted},
388         {"NotDeleted", efreet_menu_handle_not_deleted},
389         {"Include", efreet_menu_handle_include},
390         {"Exclude", efreet_menu_handle_exclude},
391         {"MergeFile", efreet_menu_handle_merge_file},
392         {"MergeDir", efreet_menu_handle_merge_dir},
393         {"DefaultMergeDirs", efreet_menu_handle_default_merge_dirs},
394         {"LegacyDir", efreet_menu_handle_legacy_dir},
395         {"KDELegacyDirs", efreet_menu_handle_kde_legacy_dirs},
396         {"Move", efreet_menu_handle_move},
397         {"Layout", efreet_menu_handle_layout},
398         {"DefaultLayout", efreet_menu_handle_default_layout},
399         {NULL, NULL}
400     };
401
402     struct
403     {
404         char *key;
405         int (*cb)(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml);
406     } filter_cbs[] = {
407         {"Filename", efreet_menu_handle_filename},
408         {"Category", efreet_menu_handle_category},
409         {"All", efreet_menu_handle_all},
410         {"And", efreet_menu_handle_and},
411         {"Or", efreet_menu_handle_or},
412         {"Not", efreet_menu_handle_not},
413         {NULL, NULL}
414     };
415
416     struct
417     {
418         char *key;
419         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml);
420     } move_cbs[] = {
421         {"Old", efreet_menu_handle_old},
422         {"New", efreet_menu_handle_new},
423         {NULL, NULL}
424     };
425
426     struct
427     {
428         char *key;
429         int (*cb)(Efreet_Menu_Internal *parent, Efreet_Xml *xml, int def);
430     } layout_cbs[] = {
431         {"Menuname", efreet_menu_handle_layout_menuname},
432         {"Filename", efreet_menu_handle_layout_filename},
433         {"Separator", efreet_menu_handle_layout_separator},
434         {"Merge", efreet_menu_handle_layout_merge},
435         {NULL, NULL}
436     };
437
438     if (!eina_stringshare_init()) return 0;
439     if (!efreet_xml_init()) return 0;
440
441     efreet_menu_handle_cbs = eina_hash_string_superfast_new(NULL);
442     efreet_menu_filter_cbs = eina_hash_string_superfast_new(NULL);
443     efreet_menu_move_cbs = eina_hash_string_superfast_new(NULL);
444     efreet_menu_layout_cbs = eina_hash_string_superfast_new(NULL);
445     if (!efreet_menu_handle_cbs || !efreet_menu_filter_cbs
446             || !efreet_menu_move_cbs || !efreet_menu_layout_cbs)
447         return 0;
448
449     /* set Menu into it's own so we can check the XML is valid before trying
450      * to handle it */
451     efreet_tag_menu = eina_stringshare_add(menu_cbs[0].key);
452
453     for (i = 0; menu_cbs[i].key != NULL; i++)
454       {
455          eina_hash_del(efreet_menu_handle_cbs,
456                menu_cbs[i].key,
457                NULL);
458          eina_hash_add(efreet_menu_handle_cbs,
459                menu_cbs[i].key,
460                menu_cbs[i].cb);
461       }
462     for (i = 0; filter_cbs[i].key != NULL; i++)
463       {
464          eina_hash_del(efreet_menu_filter_cbs,
465                filter_cbs[i].key,
466                NULL);
467          eina_hash_add(efreet_menu_filter_cbs,
468                filter_cbs[i].key,
469                filter_cbs[i].cb);
470       }
471     for (i = 0; move_cbs[i].key != NULL; i++)
472       {
473          eina_hash_del(efreet_menu_move_cbs,
474                move_cbs[i].key,
475                NULL);
476          eina_hash_add(efreet_menu_move_cbs,
477                move_cbs[i].key,
478                move_cbs[i].cb);
479       }
480     for (i = 0; layout_cbs[i].key != NULL; i++)
481       {
482          eina_hash_del(efreet_menu_layout_cbs,
483                layout_cbs[i].key,
484                NULL);
485          eina_hash_add(efreet_menu_layout_cbs,
486                layout_cbs[i].key,
487                layout_cbs[i].cb);
488       }
489     return 1;
490 }
491
492 /**
493  * @return Returns no value
494  * @brief Initialize legacy kde support. This function blocks while
495  * the kde-config script is run.
496  */
497 EAPI int
498 efreet_menu_kde_legacy_init(void)
499 {
500     FILE *f;
501     char buf[PATH_MAX];
502     char *p, *s;
503
504     IF_FREE_LIST(efreet_menu_kde_legacy_dirs);
505
506     f = popen("kde-config --path apps", "r");
507     if (!f) return 0;
508
509     /* XXX if the return from kde-config is a line longer than PATH_MAX,
510      * this won't be correct (increase buffer and get the rest...) */
511     if (!fgets(buf, PATH_MAX, f))
512     {
513         printf("Error initializing KDE legacy information\n");
514         return 0;
515     }
516     s = buf;
517
518     p = strchr(s, ':');
519     while (p)
520     {
521         *p = '\0';
522         efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
523                             (void *)eina_stringshare_add(s));
524         s = p + 1;
525         p = strchr(s, ':');
526     }
527
528     if (*s)
529         efreet_menu_kde_legacy_dirs = eina_list_append(efreet_menu_kde_legacy_dirs,
530                             (void *)eina_stringshare_add(s));
531
532     pclose(f);
533     return 1;
534 }
535
536 /**
537  * @return Returns no value
538  * @brief Shuts down the Efreet menu system
539  */
540 void
541 efreet_menu_shutdown(void)
542 {
543     IF_FREE(efreet_menu_prefix);
544     IF_FREE(efreet_menu_file);
545
546     IF_FREE_HASH(efreet_menu_handle_cbs);
547     IF_FREE_HASH(efreet_menu_filter_cbs);
548     IF_FREE_HASH(efreet_menu_move_cbs);
549     IF_FREE_HASH(efreet_menu_layout_cbs);
550
551     IF_FREE_LIST(efreet_menu_kde_legacy_dirs);
552
553     IF_FREE_HASH(efreet_merged_menus);
554     IF_FREE_HASH(efreet_merged_dirs);
555
556     IF_RELEASE(efreet_tag_menu);
557
558     efreet_xml_shutdown();
559     eina_stringshare_shutdown();
560 }
561
562 /**
563  * @param name The internal name of the menu
564  * @return Returns the Efreet_Menu on success or
565  * NULL on failure
566  * @brief Creates a new menu
567  */
568 EAPI Efreet_Menu *
569 efreet_menu_new(const char *name)
570 {
571     Efreet_Menu *menu;
572
573     if (!name)
574     {
575         printf("Error creating a new menu, name is missing\n");
576         return NULL;
577     }
578     menu = efreet_menu_entry_new();
579     menu->type = EFREET_MENU_ENTRY_MENU;
580     menu->name = eina_stringshare_add(name);
581     return menu;
582 }
583
584 EAPI void
585 efreet_menu_file_set(const char *file)
586 {
587     IF_FREE(efreet_menu_file);
588     efreet_menu_file = NULL;
589     if (file) efreet_menu_file = strdup(file);
590 }
591
592 /**
593  * @return Returns the Efreet_Menu_Internal representation of the default menu or
594  * NULL if none found
595  * @brief Creates the default menu representation
596  */
597 EAPI Efreet_Menu *
598 efreet_menu_get(void)
599 {
600     char menu[PATH_MAX];
601     const char *dir;
602     Eina_List *config_dirs, *l;
603
604     /* check the users config directory first */
605     snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
606                         efreet_config_home_get(), efreet_menu_prefix_get());
607     if (ecore_file_exists(menu))
608         return efreet_menu_parse(menu);
609
610     if (efreet_menu_file)
611     {
612         if (ecore_file_exists(efreet_menu_file))
613           return efreet_menu_parse(efreet_menu_file);
614     }
615
616     /* fallback to the XDG_CONFIG_DIRS */
617     config_dirs = efreet_config_dirs_get();
618     EINA_LIST_FOREACH(config_dirs, l, dir)
619     {
620         snprintf(menu, sizeof(menu), "%s/menus/%sapplications.menu",
621                                     dir, efreet_menu_prefix_get());
622         if (ecore_file_exists(menu))
623             return efreet_menu_parse(menu);
624     }
625
626     return NULL;
627 }
628
629 /**
630  * @param path: The path of the menu to load
631  * @return Returns the Efreet_Menu_Internal representation on success or NULL on
632  * failure
633  * @brief Parses the given .menu file and creates the menu representation
634  */
635 EAPI Efreet_Menu *
636 efreet_menu_parse(const char *path)
637 {
638     Efreet_Xml *xml;
639     Efreet_Menu_Internal *internal = NULL;
640     Efreet_Menu *entry = NULL;
641     Eina_List *search_dirs;
642
643     xml = efreet_xml_new(path);
644     if (!xml) return NULL;
645
646     /* make sure we've got a <Menu> to start with */
647     if (xml->tag != efreet_tag_menu)
648     {
649         printf("Menu file didn't start with <Menu> tag.\n");
650         efreet_xml_del(xml);
651         return NULL;
652     }
653
654     IF_FREE_HASH(efreet_merged_menus);
655     efreet_merged_menus = eina_hash_string_superfast_new(NULL);
656
657     IF_FREE_HASH(efreet_merged_dirs);
658     efreet_merged_dirs = eina_hash_string_superfast_new(NULL);
659
660     /* split appart the filename and the path */
661     internal = efreet_menu_internal_new();
662
663     /* Set default values */
664     internal->show_empty = 0;
665     internal->in_line = 0;
666     internal->inline_limit = 4;
667     internal->inline_header = 1;
668     internal->inline_alias = 0;
669
670     search_dirs = efreet_config_dirs_get();
671
672     efreet_menu_path_set(internal, path);
673     if (!efreet_menu_handle_menu(internal, xml))
674     {
675         efreet_xml_del(xml);
676         efreet_menu_internal_free(internal);
677         return NULL;
678     }
679     efreet_xml_del(xml);
680
681     efreet_menu_resolve_moves(internal);
682
683     if (!efreet_menu_process_dirs(internal))
684     {
685         efreet_menu_internal_free(internal);
686         return NULL;
687     }
688
689     /* handle all .desktops */
690     if (!efreet_menu_process(internal, 0))
691     {
692         efreet_menu_internal_free(internal);
693         return NULL;
694     }
695
696     /* handle menus with only unallocated .desktops */
697     if (!efreet_menu_process(internal, 1))
698     {
699         efreet_menu_internal_free(internal);
700         return NULL;
701     }
702
703     /* layout menu */
704     entry = efreet_menu_layout_menu(internal);
705     efreet_menu_internal_free(internal);
706     return entry;
707 }
708
709 /**
710  * @param menu: The menu to work with
711  * @param path: The path where the menu should be saved
712  * @return Returns 1 on success, 0 on failure
713  * @brief Saves the menu to file
714  */
715 EAPI int
716 efreet_menu_save(Efreet_Menu *menu, const char *path)
717 {
718     FILE *f;
719     int ret;
720
721     f = fopen(path, "w");
722     if (!f) return 0;
723     fprintf(f, "<?xml version=\"1.0\"?>\n");
724     fprintf(f, "<!DOCTYPE Menu PUBLIC \"-//freedesktop//DTD Menu 1.0//EN\" "
725                 "\"http://standards.freedesktop.org/menu-spec/menu-1.0.dtd\">\n");
726     ret = efreet_menu_save_menu(menu, f, 0);
727     fclose(f);
728     return ret;
729 }
730
731 static int
732 efreet_menu_save_menu(Efreet_Menu *menu, FILE *f, int indent)
733 {
734     Eina_List *l;
735
736     efreet_menu_save_indent(f, indent);
737     fprintf(f, "<Menu>\n");
738     if (menu->name)
739     {
740         efreet_menu_save_indent(f, indent + 1);
741         fprintf(f, "<Name>%s</Name>\n", menu->name);
742     }
743
744     if (indent == 0)
745     {
746         /* Only save these for the root element */
747         efreet_menu_save_indent(f, indent + 1);
748         fprintf(f, "<DefaultAppDirs/>\n");
749         efreet_menu_save_indent(f, indent + 1);
750         fprintf(f, "<DefaultDirectoryDirs/>\n");
751     }
752
753     if (menu->desktop)
754     {
755         efreet_menu_save_indent(f, indent + 1);
756         fprintf(f, "<Directory>%s</Directory>\n", menu->desktop->orig_path);
757     }
758
759     if (menu->entries)
760     {
761         Efreet_Menu *entry;
762         int has_desktop = 0, has_menu = 0;
763
764         efreet_menu_save_indent(f, indent + 1);
765         fprintf(f, "<Layout>\n");
766         EINA_LIST_FOREACH(menu->entries, l, entry)
767         {
768             if (entry->type == EFREET_MENU_ENTRY_MENU)
769             {
770                 efreet_menu_save_indent(f, indent + 2);
771                 fprintf(f, "<Menuname>%s</Menuname>\n", entry->id);
772                 has_menu = 1;
773             }
774             else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
775             {
776                 efreet_menu_save_indent(f, indent + 2);
777                 fprintf(f, "<Filename>%s</Filename>\n", entry->id);
778                 has_desktop = 1;
779             }
780             else if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
781             {
782                 efreet_menu_save_indent(f, indent + 2);
783                 fprintf(f, "<Separator/>\n");
784             }
785         }
786         efreet_menu_save_indent(f, indent + 1);
787         fprintf(f, "</Layout>\n");
788
789         if (has_desktop)
790         {
791             efreet_menu_save_indent(f, indent + 1);
792             fprintf(f, "<Include>\n");
793             EINA_LIST_FOREACH(menu->entries, l, entry)
794             {
795                 if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
796                 {
797                     efreet_menu_save_indent(f, indent + 2);
798                     fprintf(f, "<Filename>%s</Filename>\n", entry->id);
799                 }
800             }
801             efreet_menu_save_indent(f, indent + 1);
802             fprintf(f, "</Include>\n");
803         }
804
805         if (has_menu)
806         {
807             EINA_LIST_FOREACH(menu->entries, l, entry)
808             {
809                 if (entry->type == EFREET_MENU_ENTRY_MENU)
810                     efreet_menu_save_menu(entry, f, indent + 1);
811             }
812         }
813     }
814     efreet_menu_save_indent(f, indent);
815     fprintf(f, "</Menu>\n");
816     return 1;
817 }
818
819 static int
820 efreet_menu_save_indent(FILE *f, int indent)
821 {
822     int i;
823
824     for (i = 0; i < indent; i++)
825         fprintf(f, "  ");
826     return 1;
827 }
828
829 /**
830  * @param menu: The menu to work with
831  * @param desktop: The desktop to insert
832  * @param pos: The position to place the new desktop
833  * @return Returns 1 on success, 0 on failure
834  * @brief Insert a desktop element in a menu structure. Only accepts desktop files
835  * in default directories.
836  */
837 EAPI int
838 efreet_menu_desktop_insert(Efreet_Menu *menu, Efreet_Desktop *desktop, int pos)
839 {
840     Efreet_Menu *entry;
841     const char *id;
842
843     if (!desktop || !menu) return 0;
844     id = efreet_util_path_to_file_id(desktop->orig_path);
845     if (!id) return 0;
846
847     entry = efreet_menu_entry_new();
848     entry->type = EFREET_MENU_ENTRY_DESKTOP;
849     entry->id = eina_stringshare_add(id);
850     entry->name = eina_stringshare_add(desktop->name);
851     if (desktop->icon) entry->icon = eina_stringshare_add(desktop->icon);
852     efreet_desktop_ref(desktop);
853     entry->desktop = desktop;
854
855     if (pos < 0 || pos >= eina_list_count(menu->entries))
856         menu->entries = eina_list_append(menu->entries, entry);
857     else
858     {
859         menu->entries = eina_list_append_relative(menu->entries, entry,
860                                                   eina_list_nth(menu->entries, pos));
861     }
862     return 1;
863 }
864
865 /**
866  * @param menu: The menu to work with
867  * @param desktop: The desktop to remove
868  * @return Returns 1 on success, 0 on failure
869  * @brief Remove a desktop element in a menu structure. Only accepts desktop files
870  * in default directories.
871  */
872 EAPI int
873 efreet_menu_desktop_remove(Efreet_Menu *menu, Efreet_Desktop *desktop)
874 {
875     Efreet_Menu *entry;
876
877     if (!desktop || !menu) return 0;
878
879     entry = eina_list_search_unsorted(menu->entries,
880                                       (Eina_Compare_Cb)efreet_menu_cb_entry_compare_desktop,
881                             desktop);
882     if (entry)
883     {
884         menu->entries = eina_list_remove(menu->entries, entry);
885         efreet_menu_free(entry);
886         return 1;
887     }
888     return 0;
889 }
890
891 /**
892  * @param menu: The menu to work with
893  * @param menu: The menu to work with
894  * @param indent: The indent level to print the menu at
895  * @return Returns no value
896  * @brief Dumps the contents of the menu to the command line
897  */
898 EAPI void
899 efreet_menu_dump(Efreet_Menu *menu, const char *indent)
900 {
901     Eina_List *l;
902
903     printf("%s%s: ", indent, menu->name);
904     printf("%s\n", (menu->icon ? menu->icon : "No icon"));
905
906     /* XXX dump the rest of the menu info */
907
908     if (menu->entries)
909     {
910         Efreet_Menu *entry;
911         char *new_indent;
912         int len;
913
914         len = strlen(indent) + 3;
915         new_indent = malloc(sizeof(char *) * len);
916         snprintf(new_indent, len, "%s  ", indent);
917
918         EINA_LIST_FOREACH(menu->entries, l, entry)
919         {
920             if (entry->type == EFREET_MENU_ENTRY_SEPARATOR)
921                 printf("%s|---\n", new_indent);
922             else if (entry->type == EFREET_MENU_ENTRY_DESKTOP)
923                 printf("%s|-%s\n", new_indent, entry->name);
924             else if (entry->type == EFREET_MENU_ENTRY_MENU)
925                 efreet_menu_dump(entry, new_indent);
926             else if (entry->type == EFREET_MENU_ENTRY_HEADER)
927                 printf("%s|---%s\n", new_indent, entry->name);
928         }
929
930         FREE(new_indent);
931     }
932 }
933
934 /**
935  * @param user_dir: The user directory to work with
936  * @param system_dirs: The system directories to work with
937  * @param suffix: The path suffix to add
938  * @return Returns the list of directories
939  * @brief Creates the list of directories based on the user
940  * dir, system dirs and given suffix.
941  */
942 Eina_List *
943 efreet_default_dirs_get(const char *user_dir, Eina_List *system_dirs,
944                                                     const char *suffix)
945 {
946     const char *xdg_dir;
947     char dir[PATH_MAX];
948     Eina_List *list = NULL;
949     Eina_List *l;
950
951     snprintf(dir, sizeof(dir), "%s/%s", user_dir, suffix);
952     list = eina_list_append(list, strdup(dir));
953
954     EINA_LIST_FOREACH(system_dirs, l, xdg_dir)
955     {
956         snprintf(dir, sizeof(dir), "%s/%s", xdg_dir, suffix);
957         list = eina_list_append(list, strdup(dir));
958     }
959
960     return list;
961 }
962
963 /**
964  * @internal
965  * @return Returns a new Efreet_Menu_Internal struct
966  * @brief Allocates and initializes a new Efreet_Menu_Internal structure
967  */
968 static Efreet_Menu_Internal *
969 efreet_menu_internal_new(void)
970 {
971     Efreet_Menu_Internal *internal;
972
973     internal = NEW(Efreet_Menu_Internal, 1);
974     internal->show_empty = -1;
975     internal->in_line = -1;
976     internal->inline_limit = -1;
977     internal->inline_header = -1;
978     internal->inline_alias = -1;
979
980     return internal;
981 }
982
983 /**
984  * @param menu: The menu to free
985  * @return Returns no value
986  * @brief Frees up the given menu structure
987  */
988 void
989 efreet_menu_internal_free(Efreet_Menu_Internal *internal)
990 {
991    void *d;
992    
993     if (!internal) return;
994
995     IF_FREE(internal->file.path);
996     IF_FREE(internal->file.name);
997
998     IF_RELEASE(internal->name.internal);
999     internal->name.name = NULL;
1000
1001     IF_FREE_LIST(internal->applications);
1002
1003     IF_FREE_DLIST(internal->directories);
1004    EINA_LIST_FREE(internal->app_dirs, d) efreet_menu_app_dir_free(d);
1005 //    IF_FREE_LIST(internal->app_dirs);
1006     IF_FREE_LIST(internal->app_pool);
1007     IF_FREE_DLIST(internal->directory_dirs);
1008     IF_FREE_HASH(internal->directory_cache);
1009
1010     IF_FREE_LIST(internal->moves);
1011     IF_FREE_LIST(internal->filters);
1012
1013     IF_FREE_LIST(internal->sub_menus);
1014
1015     IF_FREE_LIST(internal->layout);
1016     IF_FREE_LIST(internal->default_layout);
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  * @return Returns a new Efreet_Menu_Filter_Op on success or NULL on failure
2440  * @brief Creates and initializes an Efreet_Menu_Filter_Op structure
2441  */
2442 static Efreet_Menu_Filter_Op *
2443 efreet_menu_filter_op_new(void)
2444 {
2445     Efreet_Menu_Filter_Op *op;
2446
2447     op = NEW(Efreet_Menu_Filter_Op, 1);
2448
2449     return op;
2450 }
2451
2452 /**
2453  * @internal
2454  * @param op: The operation to work with
2455  * @return Returns no value.
2456  * @brief Frees the given operation and all sub data
2457  */
2458 static void
2459 efreet_menu_filter_op_free(Efreet_Menu_Filter_Op *op)
2460 {
2461     if (!op) return;
2462
2463     IF_FREE_LIST(op->categories);
2464     IF_FREE_LIST(op->filenames);
2465     IF_FREE_LIST(op->filters);
2466
2467     FREE(op);
2468 }
2469
2470 /**
2471  * @internal
2472  * @return Returns a new Efreet_Menu_Desktop on success or NULL on failure
2473  * @brief Creates and returns an Efreet_Menu_Desktop
2474  */
2475 static Efreet_Menu_Desktop *
2476 efreet_menu_desktop_new(void)
2477 {
2478     Efreet_Menu_Desktop *md;
2479
2480     md = NEW(Efreet_Menu_Desktop, 1);
2481
2482     return md;
2483 }
2484
2485 /**
2486  * @internal
2487  * @param md: The Efreet_Menu_Desktop to free
2488  * @return Returns no value
2489  * @brief Frees the given structure
2490  */
2491 static void
2492 efreet_menu_desktop_free(Efreet_Menu_Desktop *md)
2493 {
2494     IF_RELEASE(md->id);
2495     if (md->desktop) efreet_desktop_free(md->desktop);
2496     FREE(md);
2497 }
2498
2499 /**
2500  * @internal
2501  * @return Returns a new Efreet_Menu on success or NULL on failure
2502  * @brief Creates and returns an Efreet_Menu
2503  */
2504 static Efreet_Menu *
2505 efreet_menu_entry_new(void)
2506 {
2507     Efreet_Menu *entry;
2508
2509     entry = NEW(Efreet_Menu, 1);
2510
2511     return entry;
2512 }
2513
2514 /**
2515  * @internal
2516  * @param entry: The Efreet_Menu to free
2517  * @return Returns no value
2518  * @brief Frees the given structure
2519  */
2520 EAPI void
2521 efreet_menu_free(Efreet_Menu *entry)
2522 {
2523     IF_RELEASE(entry->name);
2524     IF_RELEASE(entry->icon);
2525     IF_FREE_LIST(entry->entries);
2526     IF_RELEASE(entry->id);
2527     if (entry->desktop) efreet_desktop_free(entry->desktop);
2528     FREE(entry);
2529 }
2530
2531 /**
2532  * @internal
2533  * @param op: The op to add a child too
2534  * @param xml: The XML tree of the child
2535  * @param type: The type of child to add
2536  * @return Returns 1 on success or 0 on failure
2537  * @brief Parses the given XML tree and populates a new child operation.
2538  */
2539 static int
2540 efreet_menu_handle_filter_child_op(Efreet_Menu_Filter_Op *op, Efreet_Xml *xml,
2541                                                 Efreet_Menu_Filter_Op_Type type)
2542 {
2543     Efreet_Menu_Filter_Op *child_op;
2544
2545     child_op = efreet_menu_filter_op_new();
2546     child_op->type = type;
2547
2548     if (!efreet_menu_handle_filter_op(child_op, xml))
2549     {
2550         efreet_menu_filter_op_free(child_op);
2551         return 0;
2552     }
2553
2554     op->filters = eina_list_append(op->filters, child_op);
2555
2556     return 1;
2557 }
2558
2559 /**
2560  * @internal
2561  * @param menu: The menu to work with
2562  * @param only_unallocated: Do we only look for unallocated items?
2563  * @return Returns 1 if we've successfully processed the menu, 0 otherwise
2564  * @brief Handles the processing of the menu data to retrieve the .desktop
2565  * files for the menu
2566  */
2567 static int
2568 efreet_menu_process(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2569 {
2570     Eina_List *l;
2571
2572     /* a menu _MUST_ have a name */
2573     if (!internal->name.internal || (internal->name.internal[0] == '\0'))
2574         return 0;
2575
2576     /* handle filtering out .desktop files as needed. This deals with all
2577      * .desktop files */
2578     efreet_menu_process_filters(internal, only_unallocated);
2579
2580     if (internal->sub_menus)
2581     {
2582         Efreet_Menu_Internal *sub_internal;
2583
2584         EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
2585         {
2586             sub_internal->parent = internal;
2587             efreet_menu_process(sub_internal, only_unallocated);
2588         }
2589     }
2590
2591     return 1;
2592 }
2593
2594 /* This will walk through all of the app dirs and load all the .desktop
2595  * files into the cache for the menu. The .desktop files will have their
2596  * allocated flag set to 0 */
2597 static int
2598 efreet_menu_process_dirs(Efreet_Menu_Internal *internal)
2599 {
2600     Eina_List *l;
2601
2602     /* Scan application directories for .desktop files */
2603     if (!efreet_menu_app_dirs_process(internal))
2604         return 0;
2605
2606     /* Scan directory directories for .directory file */
2607     if (!efreet_menu_directory_dirs_process(internal))
2608         return 0;
2609
2610     if (internal->sub_menus)
2611     {
2612         Efreet_Menu_Internal *sub_internal;
2613
2614         EINA_LIST_FOREACH(internal->sub_menus, l, sub_internal)
2615         {
2616             sub_internal->parent = internal;
2617             efreet_menu_process_dirs(sub_internal);
2618         }
2619     }
2620
2621     return 1;
2622 }
2623
2624 /**
2625  * @internal
2626  * @param menu: the menu to process
2627  * @param only_unallocated: Only handle menus taht deal with unallocated items
2628  * @return Returns no value
2629  * @brief Handles the processing of the filters attached to the given menu.
2630  *
2631  * For each include filter we'll add the items to our applications array. Each
2632  * exclude filter will remove items from the applications array
2633  */
2634 static void
2635 efreet_menu_process_filters(Efreet_Menu_Internal *internal, unsigned int only_unallocated)
2636 {
2637     Efreet_Menu_Filter *filter;
2638     Efreet_Menu_Desktop *md;
2639     Eina_List *l, *ll;
2640
2641     int included = 0;
2642
2643     /* nothing to do if we're checking the other option */
2644     if (only_unallocated != internal->only_unallocated) return;
2645
2646     while (internal->applications)
2647         internal->applications = eina_list_remove_list(internal->applications,
2648                                                        internal->applications);
2649
2650     if (!internal->filters) return;
2651
2652     EINA_LIST_FOREACH(internal->filters, l, filter)
2653     {
2654         /* skip excludes until we get an include */
2655         if (!included && (filter->type == EFREET_MENU_FILTER_EXCLUDE))
2656             continue;
2657         included = 1;
2658
2659         if (filter->type == EFREET_MENU_FILTER_INCLUDE)
2660         {
2661             Eina_Hash *matches;
2662
2663             matches = eina_hash_string_superfast_new(NULL);
2664             internal->applications = efreet_menu_process_app_pool(internal->app_pool, internal->applications,
2665                                         matches, filter, internal->only_unallocated);
2666             if (internal->parent)
2667             {
2668                 Efreet_Menu_Internal *parent;
2669
2670                 parent = internal->parent;
2671                 do {
2672                     internal->applications = efreet_menu_process_app_pool(parent->app_pool,
2673                                                 internal->applications, matches, filter,
2674                                                 internal->only_unallocated);
2675                 } while ((parent = parent->parent));
2676             }
2677             eina_hash_free(matches);
2678         }
2679         else
2680         {
2681             /* check each item in our menu so far and see if it's excluded */
2682             l = internal->applications;
2683             while ((md = eina_list_data_get(l)))
2684             {
2685                 ll = eina_list_next(l);
2686                 if (efreet_menu_filter_matches(filter->op, md))
2687                     internal->applications = eina_list_remove_list(internal->applications, l);
2688                 l = ll;
2689             }
2690         }
2691     }
2692
2693     /* sort the menu applications. we do this in process filters so it will only
2694      * be done once per menu.*/
2695     if (internal->applications)
2696     {
2697         int count;
2698
2699         count = eina_list_count(internal->applications);
2700         if (count)
2701         {
2702             Ecore_Sheap *sheap;
2703             Efreet_Menu_Desktop *md;
2704
2705             sheap = ecore_sheap_new(
2706                         ECORE_COMPARE_CB(efreet_menu_cb_md_compare), count);
2707             while ((md = eina_list_data_get(internal->applications)))
2708             {
2709                 internal->applications = eina_list_remove_list(internal->applications,
2710                                                                internal->applications);
2711                 ecore_sheap_insert(sheap, md);
2712             }
2713             while ((md = ecore_sheap_extract(sheap)))
2714             {
2715                 if (md->desktop->no_display) continue;
2716                 internal->applications = eina_list_append(internal->applications, md);
2717             }
2718
2719             ecore_sheap_destroy(sheap);
2720         }
2721     }
2722 }
2723
2724 /**
2725  * @internal
2726  * @param pool: The app pool to iterate
2727  * @param applications: The list of applications to append too
2728  * @param matches: The hash of previously matched ids
2729  * @param filter: The menu filter to run on the pool items
2730  * @param only_unallocated: Do we check only unallocated pool items?
2731  * @return Returns no value.
2732  * @brief This will iterate the items in @a pool and append them to @a
2733  * applications if they match the @a filter given and aren't previoulsy entered
2734  * in @a matches. If @a only_unallocated is set we'll only only at the
2735  * .desktop files that haven't been previoulsy matched
2736  */
2737 static Eina_List *
2738 efreet_menu_process_app_pool(Eina_List *pool, Eina_List *applications,
2739                                   Eina_Hash *matches,
2740                                   Efreet_Menu_Filter *filter,
2741                                   unsigned int only_unallocated)
2742 {
2743     Efreet_Menu_Desktop *md;
2744     Eina_List *l;
2745
2746     if (!pool) return NULL;
2747
2748     EINA_LIST_FOREACH(pool, l, md)
2749     {
2750         if (eina_hash_find(matches, md->id)) continue;
2751         if (only_unallocated && md->allocated) continue;
2752         if (efreet_menu_filter_matches(filter->op, md))
2753         {
2754             applications = eina_list_append(applications, md);
2755             eina_hash_add(matches, (void *)md->id, md);
2756             md->allocated = 1;
2757         }
2758     }
2759     return applications;
2760 }
2761
2762 /**
2763  * @internal
2764  * @param op: The filter operation to execute
2765  * @param md: The desktop to run the filter on
2766  * @return Returns 1 if this desktop matches the given filter, 0 otherwise
2767  * @brief This will execute the given @a filter on the given desktop
2768  */
2769 static int
2770 efreet_menu_filter_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2771 {
2772     if (op->type == EFREET_MENU_FILTER_OP_OR)
2773         return efreet_menu_filter_or_matches(op, md);
2774
2775     if (op->type == EFREET_MENU_FILTER_OP_AND)
2776         return efreet_menu_filter_and_matches(op, md);
2777
2778     if (op->type == EFREET_MENU_FILTER_OP_NOT)
2779         return efreet_menu_filter_not_matches(op, md);
2780
2781     return 0;
2782 }
2783
2784 /**
2785  * @internal
2786  * @param op: The filter operation to execute
2787  * @param md: The desktop to execute on
2788  * @return Returns 1 if the desktop matches, 0 otherwise
2789  * @brief Executes the OR operation, @a op, on the desktop, @a md.
2790  */
2791 static int
2792 efreet_menu_filter_or_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2793 {
2794     Efreet_Menu_Filter_Op *child;
2795     Eina_List *l;
2796     char *t;
2797
2798     if (op->all) return 1;
2799
2800     if (op->categories && md->desktop->categories)
2801     {
2802         EINA_LIST_FOREACH(op->categories, l, t)
2803         {
2804             if (eina_list_search_unsorted(md->desktop->categories,
2805                                           (Eina_Compare_Cb)strcmp, t))
2806                 return 1;
2807         }
2808     }
2809
2810     if (op->filenames)
2811     {
2812         EINA_LIST_FOREACH(op->filenames, l, t)
2813             if (!strcmp(t, md->id)) return 1;
2814     }
2815
2816     if (op->filters)
2817     {
2818         EINA_LIST_FOREACH(op->filters, l, child)
2819         {
2820             if (efreet_menu_filter_matches(child, md))
2821                 return 1;
2822         }
2823     }
2824
2825     return 0;
2826 }
2827
2828 /**
2829  * @internal
2830  * @param op: The filter operation to execute
2831  * @param md: The desktop to execute on
2832  * @return Returns 1 if the desktop matches, 0 otherwise
2833  * @brief Executes the AND operation, @a op, on the desktop, @a md.
2834  */
2835 static int
2836 efreet_menu_filter_and_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2837 {
2838     Efreet_Menu_Filter_Op *child;
2839     Eina_List *l;
2840     char *t;
2841
2842     if (op->categories)
2843     {
2844         if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
2845             return 0;
2846
2847         EINA_LIST_FOREACH(op->categories, l, t)
2848         {
2849             if (!eina_list_search_unsorted(md->desktop->categories,
2850                                            (Eina_Compare_Cb)strcmp, t))
2851                 return 0;
2852         }
2853     }
2854
2855     if (op->filenames)
2856     {
2857         EINA_LIST_FOREACH(op->filenames, l, t)
2858         {
2859             if (strcmp(t, md->id)) return 0;
2860         }
2861     }
2862
2863     if (op->filters)
2864     {
2865         EINA_LIST_FOREACH(op->filters, l, child)
2866         {
2867             if (!efreet_menu_filter_matches(child, md))
2868                 return 0;
2869         }
2870     }
2871
2872     return 1;
2873 }
2874
2875 /**
2876  * @internal
2877  * @param op: The filter operation to execute
2878  * @param md: The desktop to execute on
2879  * @return Returns 1 if the desktop matches, 0 otherwise
2880  * @brief Executes the NOT operation, @a op, on the desktop, @a md.
2881  */
2882 static int
2883 efreet_menu_filter_not_matches(Efreet_Menu_Filter_Op *op, Efreet_Menu_Desktop *md)
2884 {
2885     Efreet_Menu_Filter_Op *child;
2886     Eina_List *l;
2887     char *t;
2888
2889     /* !all means no desktops match */
2890     if (op->all) return 0;
2891
2892     if (op->categories)
2893     {
2894         if ((eina_list_count(op->categories) > 0) && !md->desktop->categories)
2895             return 1;
2896
2897         EINA_LIST_FOREACH(op->categories, l, t) 
2898         {
2899             if (eina_list_search_unsorted(md->desktop->categories,
2900                                           (Eina_Compare_Cb)strcmp, t))
2901                 return 0;
2902         }
2903     }
2904
2905     if (op->filenames)
2906     {
2907         EINA_LIST_FOREACH(op->filenames, l, t)
2908         {
2909             if (!strcmp(t, md->id)) return 0;
2910         }
2911     }
2912
2913     if (op->filters)
2914     {
2915         EINA_LIST_FOREACH(op->filters, l, child)
2916         {
2917             if (efreet_menu_filter_matches(child, md))
2918                 return 0;
2919         }
2920     }
2921
2922     return 1;
2923 }
2924
2925 /**
2926  * @internal
2927  * @param dest: The destination menu
2928  * @param src: The source menu
2929  * @return Returns no value
2930  * @brief Takes the child elements of the menu @a src and puts then on the
2931  * _start_ of the menu @a dest.
2932  */
2933 static void
2934 efreet_menu_concatenate(Efreet_Menu_Internal *dest, Efreet_Menu_Internal *src)
2935 {
2936     Efreet_Menu_Internal *submenu;
2937
2938     if (!dest || !src) return;
2939
2940     if (!dest->directory && src->directory)
2941     {
2942         dest->directory = src->directory;
2943         src->directory = NULL;
2944     }
2945
2946     if (!dest->seen_allocated && src->seen_allocated)
2947     {
2948         dest->only_unallocated = src->only_unallocated;
2949         dest->seen_allocated = 1;
2950     }
2951
2952     if (!dest->seen_deleted && src->seen_deleted)
2953     {
2954         dest->deleted = src->deleted;
2955         dest->seen_deleted = 1;
2956     }
2957
2958     if (src->directories)
2959     {
2960         efreet_menu_create_directories_list(dest);
2961         ecore_dlist_prepend_list(dest->directories, src->directories);
2962     }
2963
2964     if (src->app_dirs)
2965     {
2966         efreet_menu_create_app_dirs_list(dest);
2967         dest->app_dirs = eina_list_merge(src->app_dirs, dest->app_dirs);
2968         src->app_dirs = NULL;
2969     }
2970
2971     if (src->directory_dirs)
2972     {
2973         efreet_menu_create_directory_dirs_list(dest);
2974         ecore_dlist_prepend_list(dest->directory_dirs, src->directory_dirs);
2975     }
2976
2977     if (src->moves)
2978     {
2979         efreet_menu_create_move_list(dest);
2980         dest->moves = eina_list_merge(src->moves, dest->moves);
2981         src->moves = NULL;
2982     }
2983
2984     if (src->filters)
2985     {
2986         efreet_menu_create_filter_list(dest);
2987         dest->filters = eina_list_merge(src->filters, dest->filters);
2988         src->filters = NULL;
2989     }
2990
2991     if (src->sub_menus)
2992     {
2993         efreet_menu_create_sub_menu_list(dest);
2994
2995         while ((submenu = eina_list_data_get(eina_list_last(src->sub_menus))))
2996         {
2997             Efreet_Menu_Internal *match;
2998
2999             src->sub_menus = eina_list_remove(src->sub_menus, submenu);
3000             /* if this menu is in the list already we just add to that */
3001             if ((match = eina_list_search_unsorted(dest->sub_menus,
3002                                                    (Eina_Compare_Cb)efreet_menu_cb_menu_compare,
3003                                                    submenu)))
3004             {
3005                 efreet_menu_concatenate(match, submenu);
3006                 efreet_menu_internal_free(submenu);
3007             }
3008             else
3009                 dest->sub_menus = eina_list_prepend(dest->sub_menus, submenu);
3010         }
3011     }
3012 }
3013
3014 /**
3015  * @internal
3016  * @param menu: The menu to work with
3017  * @return Returns no value
3018  * @brief Handles any \<Move\> commands in the menus
3019  */
3020 static void
3021 efreet_menu_resolve_moves(Efreet_Menu_Internal *internal)
3022 {
3023     Efreet_Menu_Internal *child;
3024     Efreet_Menu_Move *move;
3025     Eina_List *l;
3026
3027     /* child moves are handled before parent moves */
3028     if (internal->sub_menus)
3029     {
3030         EINA_LIST_FOREACH(internal->sub_menus, l, child)
3031             efreet_menu_resolve_moves(child);
3032     }
3033
3034     /* nothing to do if this menu has no moves */
3035     if (!internal->moves) return;
3036
3037     EINA_LIST_FOREACH(internal->moves, l, move)
3038     {
3039         Efreet_Menu_Internal *origin, *dest, *parent;
3040
3041         /* if the origin path doesn't exist we do nothing */
3042         origin = efreet_menu_by_name_find(internal, move->old_name, &parent);
3043         if (!origin) continue;
3044
3045         /* remove the origin menu from the parent */
3046         parent->sub_menus = eina_list_remove(parent->sub_menus, origin);
3047
3048         /* if the destination path doesn't exist we just rename the origin
3049          * menu and append to the parents list of children */
3050         dest = efreet_menu_by_name_find(internal, move->new_name, &parent);
3051         if (!dest)
3052         {
3053             char *path, *tmp, *t;
3054
3055             /* if the dest path has /'s in it then we need to add menus to
3056              * fill out the paths */
3057             t = strdup(move->new_name);
3058             tmp = t;
3059             path = strchr(tmp, '/');
3060             while (path)
3061             {
3062                 Efreet_Menu_Internal *ancestor;
3063
3064                 *path = '\0';
3065
3066                 ancestor = efreet_menu_internal_new();
3067                 ancestor->name.internal = eina_stringshare_add(tmp);
3068
3069                 efreet_menu_create_sub_menu_list(parent);
3070                 parent->sub_menus = eina_list_append(parent->sub_menus, ancestor);
3071
3072                 parent = ancestor;
3073                 tmp = ++path;
3074                 path = strchr(tmp, '/');
3075             }
3076
3077             IF_RELEASE(origin->name.internal);
3078             origin->name.internal = eina_stringshare_add(tmp);
3079
3080             efreet_menu_create_sub_menu_list(parent);
3081             parent->sub_menus = eina_list_append(parent->sub_menus, origin);
3082
3083             FREE(t);
3084         }
3085         else
3086         {
3087             efreet_menu_concatenate(dest, origin);
3088             efreet_menu_internal_free(origin);
3089         }
3090     }
3091     IF_FREE_LIST(internal->moves);
3092 }
3093
3094 /**
3095  * @internal
3096  * @param menu: The menu to start searching from
3097  * @param name: The menu name to find
3098  * @param parent: The parent of the found menu
3099  * @return Returns the menu with the given @a name or NULL if none found
3100  * @brief Searches the menu tree starting at @a menu looking for a menu with
3101  * @a name.
3102  */
3103 static Efreet_Menu_Internal *
3104 efreet_menu_by_name_find(Efreet_Menu_Internal *internal, const char *name, Efreet_Menu_Internal **parent)
3105 {
3106     char *part, *tmp, *ptr;
3107
3108     if (parent) *parent = internal;
3109
3110     /* find the correct parent menu */
3111     tmp = strdup(name);
3112     ptr = tmp;
3113     part = strchr(ptr, '/');
3114     while (part)
3115     {
3116         *part = '\0';
3117
3118         if (!(internal = eina_list_search_unsorted(internal->sub_menus,
3119                                                    (Eina_Compare_Cb)efreet_menu_cb_compare_names,
3120                                                    ptr)))
3121         {
3122             FREE(tmp);
3123             return NULL;
3124         }
3125
3126         ptr = ++part;
3127         part = strchr(ptr, '/');
3128     }
3129
3130     if (parent) *parent = internal;
3131
3132     /* find the menu in the parent list */
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     FREE(tmp);
3142     return internal;
3143 }
3144
3145 static void
3146 efreet_menu_path_set(Efreet_Menu_Internal *internal, const char *path)
3147 {
3148     char *tmp, *p;
3149
3150     tmp = strdup(path);
3151     p = strrchr(tmp, '/');
3152     if (p)
3153     {
3154         *p = '\0';
3155         p++;
3156
3157         internal->file.path = strdup(tmp);
3158         internal->file.name = strdup(p);
3159     }
3160     FREE(tmp);
3161 }
3162
3163 /**
3164  * @internal
3165  * @return Returns a new Efreet_Menu_Move struct on success or NULL on failure
3166  * @brief Creates an returns a new Efreet_Menu_Move struct or NULL on failure
3167  */
3168 static Efreet_Menu_Move *
3169 efreet_menu_move_new(void)
3170 {
3171     Efreet_Menu_Move *move;
3172
3173     move = NEW(Efreet_Menu_Move, 1);
3174
3175     return move;
3176 }
3177
3178 /**
3179  * @internal
3180  * @param move: The Efreet_Menu_Move to free
3181  * @return Returns no value.
3182  * @brief Frees the given move structure
3183  */
3184 static void
3185 efreet_menu_move_free(Efreet_Menu_Move *move)
3186 {
3187     if (!move) return;
3188
3189     IF_FREE(move->old_name);
3190     IF_FREE(move->new_name);
3191
3192     FREE(move);
3193 }
3194
3195 /**
3196  * @internal
3197  * @return Returns a new Efreet_Menu_App_Dir on success or NULL on failure
3198  * @brief Creates and initializes a new Efreet_Menu_App_Dir structure
3199  */
3200 static Efreet_Menu_App_Dir *
3201 efreet_menu_app_dir_new(void)
3202 {
3203     Efreet_Menu_App_Dir *dir;
3204
3205     dir = NEW(Efreet_Menu_App_Dir, 1);
3206
3207     return dir;
3208 }
3209
3210 static void
3211 efreet_menu_app_dir_free(Efreet_Menu_App_Dir *dir)
3212 {
3213    free(dir->path);
3214    free(dir->prefix);
3215    free(dir);
3216 }
3217
3218 /**
3219  * @internal
3220  * @param a: The app dir to compare too
3221  * @param b: The path to compare too
3222  * @return Returns 1 if the strings are equals, 0 otherwise
3223  * @brief Compares the too strings
3224  */
3225 static int
3226 efreet_menu_cb_app_dirs_compare(Efreet_Menu_App_Dir *a, const char *b)
3227 {
3228     return ecore_str_compare(a->path, b);
3229 }
3230
3231 static void
3232 efreet_menu_create_sub_menu_list(Efreet_Menu_Internal *internal)
3233 {
3234     if (!internal || internal->sub_menus) return;
3235
3236     internal->sub_menus = NULL;
3237 }
3238
3239 static void
3240 efreet_menu_create_app_dirs_list(Efreet_Menu_Internal *internal)
3241 {
3242     if (!internal || internal->app_dirs) return;
3243
3244     internal->app_dirs = NULL;
3245 }
3246
3247 static void
3248 efreet_menu_create_directory_dirs_list(Efreet_Menu_Internal *internal)
3249 {
3250     if (!internal || internal->directory_dirs) return;
3251
3252     internal->directory_dirs = ecore_dlist_new();
3253     ecore_dlist_free_cb_set(internal->directory_dirs, free);
3254 }
3255
3256 static void
3257 efreet_menu_create_move_list(Efreet_Menu_Internal *internal)
3258 {
3259     if (!internal || internal->moves) return;
3260
3261     internal->moves = NULL;
3262 }
3263
3264 static void
3265 efreet_menu_create_filter_list(Efreet_Menu_Internal *internal)
3266 {
3267     if (!internal || internal->filters) return;
3268
3269     internal->filters = NULL;
3270 }
3271
3272 static void
3273 efreet_menu_create_layout_list(Efreet_Menu_Internal *internal)
3274 {
3275     if (!internal || internal->layout) return;
3276
3277     internal->layout = NULL;
3278 }
3279
3280 static void
3281 efreet_menu_create_default_layout_list(Efreet_Menu_Internal *internal)
3282 {
3283     if (!internal || internal->default_layout) return;
3284
3285     internal->default_layout = NULL;
3286 }
3287
3288 static void
3289 efreet_menu_create_directories_list(Efreet_Menu_Internal *internal)
3290 {
3291     if (!internal || internal->directories) return;
3292
3293     internal->directories = ecore_dlist_new();
3294    ecore_dlist_free_cb_set(internal->directories, free);
3295 }
3296
3297 static char *
3298 efreet_menu_path_get(Efreet_Menu_Internal *internal, const char *suffix)
3299 {
3300     char path[PATH_MAX];
3301     size_t len;
3302
3303     /* see if we've got an absolute or relative path */
3304     if (suffix[0] == '/')
3305         snprintf(path, sizeof(path), "%s", suffix);
3306
3307     else
3308     {
3309         if (!internal->file.path)
3310         {
3311             printf("efreet_menu_handle_app_dir() missing menu path ...\n");
3312             return NULL;
3313         }
3314         snprintf(path, sizeof(path), "%s/%s", internal->file.path, suffix);
3315     }
3316
3317     len = strlen(path);
3318     while (path[len] == '/') path[len--] = '\0';
3319
3320     return strdup(path);
3321 }
3322
3323 static int
3324 efreet_menu_cb_menu_compare(Efreet_Menu_Internal *a, Efreet_Menu_Internal *b)
3325 {
3326     return ecore_str_compare(a->name.internal, b->name.internal);
3327 }
3328
3329 static int
3330 efreet_menu_app_dirs_process(Efreet_Menu_Internal *internal)
3331 {
3332     Efreet_Menu_App_Dir *app_dir;
3333     Efreet_Menu_Desktop *md;
3334     Eina_List *l;
3335
3336     EINA_LIST_FREE(internal->app_pool, md)
3337       efreet_menu_desktop_free(md);
3338
3339     EINA_LIST_FOREACH(internal->app_dirs, l, app_dir)
3340             efreet_menu_app_dir_scan(internal, app_dir->path, app_dir->prefix, app_dir->legacy);
3341
3342     return 1;
3343 }
3344
3345 static int
3346 efreet_menu_app_dir_scan(Efreet_Menu_Internal *internal, const char *path, const char *id, int legacy)
3347 {
3348     Efreet_Desktop *desktop;
3349     Efreet_Menu_Desktop *menu_desktop;
3350     DIR *files;
3351     char buf[PATH_MAX], buf2[PATH_MAX];
3352     struct dirent *file;
3353     char *ext;
3354
3355     files = opendir(path);
3356     if (!files) return 1;
3357
3358     while ((file = readdir(files)))
3359     {
3360         if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
3361         snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name);
3362         if (id)
3363             snprintf(buf2, PATH_MAX, "%s-%s", id, file->d_name);
3364         else
3365             strcpy(buf2, file->d_name);
3366
3367         if (ecore_file_is_dir(buf))
3368         {
3369             if (!legacy)
3370                 efreet_menu_app_dir_scan(internal, buf, buf2, legacy);
3371         }
3372         else
3373         {
3374             ext = strrchr(buf, '.');
3375
3376             if (!ext || strcmp(ext, ".desktop")) continue;
3377             desktop = efreet_desktop_get(buf);
3378
3379             if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_APPLICATION)
3380             {
3381                 if (desktop) efreet_desktop_free(desktop);
3382                 continue;
3383             }
3384             /* Don't add two files with the same id in the app pool */
3385             if (eina_list_search_unsorted(internal->app_pool,
3386                                           (Eina_Compare_Cb)efreet_menu_cb_md_compare_ids,
3387                                           buf2))
3388                 continue;
3389
3390             menu_desktop = efreet_menu_desktop_new();
3391             menu_desktop->desktop = desktop;
3392             menu_desktop->id = eina_stringshare_add(buf2);
3393             internal->app_pool = eina_list_prepend(internal->app_pool, menu_desktop);
3394         }
3395     }
3396     closedir(files);
3397
3398     return 1;
3399 }
3400
3401 /**
3402  * @internal
3403  * @param menu: The menu to work with
3404  * @return Returns 1 on success or 0 on failure
3405  * @brief Process the directory dirs in @a menu
3406  */
3407 static int
3408 efreet_menu_directory_dirs_process(Efreet_Menu_Internal *internal)
3409 {
3410     const char *path;
3411
3412     if (internal->directory_dirs)
3413     {
3414         internal->directory_cache =
3415           eina_hash_string_superfast_new(EINA_FREE_CB(efreet_desktop_free));
3416
3417         ecore_dlist_last_goto(internal->directory_dirs);
3418         while ((path = ecore_dlist_previous(internal->directory_dirs)))
3419             efreet_menu_directory_dir_scan(path, NULL, internal->directory_cache);
3420     }
3421
3422     if (internal->directories)
3423     {
3424         ecore_dlist_last_goto(internal->directories);
3425         while ((path = ecore_dlist_previous(internal->directories)))
3426         {
3427             internal->directory = efreet_menu_directory_get(internal, path);
3428             if (internal->directory) break;
3429         }
3430     }
3431     if (!internal->directory)
3432         internal->name.name = internal->name.internal;
3433     else
3434         internal->name.name = internal->directory->name;
3435
3436     return 1;
3437 }
3438
3439 /**
3440  * @internal
3441  * @param path: The path to scan
3442  * @param relative_path: The relative portion of the path
3443  * @param cache: The cache to populate
3444  * @return Returns 1 on success or 0 on failure
3445  * @brief Scans the given directory dir for .directory files and adds the
3446  * applications to the cache
3447  */
3448 static int
3449 efreet_menu_directory_dir_scan(const char *path, const char *relative_path,
3450                                Eina_Hash *cache)
3451 {
3452     Efreet_Desktop *desktop;
3453     DIR *files;
3454     char buf[PATH_MAX], buf2[PATH_MAX];
3455     struct dirent *file;
3456     char *ext;
3457
3458     files = opendir(path);
3459     if (!files) return 1;
3460
3461     while ((file = readdir(files)))
3462     {
3463         if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) continue;
3464         snprintf(buf, PATH_MAX, "%s/%s", path, file->d_name);
3465         if (relative_path)
3466             snprintf(buf2, PATH_MAX, "%s/%s", relative_path, file->d_name);
3467         else
3468             strcpy(buf2, file->d_name);
3469
3470         if (ecore_file_is_dir(buf))
3471             efreet_menu_directory_dir_scan(buf, buf2, cache);
3472
3473         else
3474         {
3475             ext = strrchr(buf, '.');
3476             if (!ext || strcmp(ext, ".directory")) continue;
3477
3478             desktop = efreet_desktop_get(buf);
3479             if (!desktop || desktop->type != EFREET_DESKTOP_TYPE_DIRECTORY)
3480             {
3481                 efreet_desktop_free(desktop);
3482                 continue;
3483             }
3484
3485             eina_hash_del(cache, buf2, NULL);
3486             eina_hash_add(cache, buf2, desktop);
3487         }
3488     }
3489     closedir(files);
3490
3491     return 1;
3492 }
3493
3494 /**
3495  * @internal
3496  * @param menu: The menu to work with
3497  * @param path: The path to work with
3498  * @return Returns the desktop file for this path or NULL if none exists
3499  * @brief Finds the desktop file for the given path.
3500  */
3501 static Efreet_Desktop *
3502 efreet_menu_directory_get(Efreet_Menu_Internal *internal, const char *path)
3503 {
3504     Efreet_Desktop *dir;
3505
3506     if (internal->directory_cache)
3507     {
3508         dir = eina_hash_find(internal->directory_cache, path);
3509         if (dir) return dir;
3510     }
3511
3512     if (internal->parent)
3513         return efreet_menu_directory_get(internal->parent, path);
3514
3515     return NULL;
3516 }
3517
3518 /**
3519  * @internal
3520  * @param a: The first desktop
3521  * @param b: The second desktop
3522  * @return Returns the comparison of the desktop files
3523  * @brief Compares the desktop files.
3524  */
3525 static int
3526 efreet_menu_cb_md_compare(Efreet_Menu_Desktop *a, Efreet_Menu_Desktop *b)
3527 {
3528 #ifdef STRICT_SPEC
3529     return strcmp(ecore_file_file_get(a->desktop->orig_path), ecore_file_file_get(b->desktop->orig_path));
3530 #else
3531     return strcasecmp(a->desktop->name, b->desktop->name);
3532 #endif
3533 }
3534
3535 static int
3536 efreet_menu_cb_compare_names(Efreet_Menu_Internal *internal, const char *name)
3537 {
3538     return strcmp(internal->name.internal, name);
3539 }
3540
3541 static int
3542 efreet_menu_cb_md_compare_ids(Efreet_Menu_Desktop *md, const char *name)
3543 {
3544     return strcmp(md->id, name);
3545 }
3546
3547 static Efreet_Menu *
3548 efreet_menu_layout_menu(Efreet_Menu_Internal *internal)
3549 {
3550     Efreet_Menu *entry;
3551     Eina_List *layout = NULL;
3552     Eina_List *l;
3553
3554     if (internal->parent)
3555     {
3556         /* Copy default layout rules */
3557         if (internal->show_empty == -1)    internal->show_empty = internal->parent->show_empty;
3558         if (internal->in_line == -1)       internal->in_line = internal->parent->in_line;
3559         if (internal->inline_limit == -1)  internal->inline_limit = internal->parent->inline_limit;
3560         if (internal->inline_header == -1) internal->inline_header = internal->parent->inline_header;
3561         if (internal->inline_alias == -1)  internal->inline_alias = internal->parent->inline_alias;
3562     }
3563
3564     if (internal->layout)
3565         layout = internal->layout;
3566
3567     else if (internal->parent)
3568     {
3569         Efreet_Menu_Internal *parent;
3570         parent = internal->parent;
3571         do
3572         {
3573             layout = parent->default_layout;
3574             parent = parent->parent;
3575         } while (!layout && parent);
3576     }
3577
3578     /* init entry */
3579     entry = efreet_menu_entry_new();
3580     entry->type = EFREET_MENU_ENTRY_MENU;
3581     entry->id = eina_stringshare_add(internal->name.internal);
3582     entry->name = eina_stringshare_add(internal->name.name);
3583     if (internal->directory)
3584     {
3585         entry->icon = eina_stringshare_add(internal->directory->icon);
3586         efreet_desktop_ref(internal->directory);
3587         entry->desktop = internal->directory;
3588     }
3589     entry->entries = NULL;
3590
3591 #if 1 //STRICT_SPEC
3592     if (internal->sub_menus)
3593     {
3594         internal->sub_menus = eina_list_sort(internal->sub_menus,
3595                                              0,
3596                                              (Eina_Compare_Cb)efreet_menu_cb_menu_compare);
3597     }
3598 #endif
3599
3600     if (layout)
3601     {
3602         Efreet_Menu_Layout *lay;
3603
3604         EINA_LIST_FOREACH(layout, l, lay)
3605             efreet_menu_layout_entries_get(entry, internal, lay);
3606     }
3607     else
3608     {
3609         /* Default layout, first menus, then desktop */
3610         if (internal->sub_menus)
3611         {
3612             Efreet_Menu_Internal *sub;
3613
3614             EINA_LIST_FOREACH(internal->sub_menus, l, sub)
3615             {
3616                 Efreet_Menu *sub_entry;
3617                 if ((sub->directory && sub->directory->no_display) || sub->deleted) continue;
3618                 sub_entry = efreet_menu_layout_menu(sub);
3619                 /* Don't show empty menus */
3620                 if (!sub_entry->entries)
3621                 {
3622                     efreet_menu_free(sub_entry);
3623                     continue;
3624                 }
3625                 entry->entries = eina_list_append(entry->entries, sub_entry);
3626             }
3627         }
3628
3629         if (internal->applications)
3630         {
3631             Efreet_Menu_Desktop *md;
3632
3633             EINA_LIST_FOREACH(internal->applications, l, md)
3634             {
3635                 Efreet_Menu *sub_entry;
3636                 sub_entry = efreet_menu_layout_desktop(md);
3637                 entry->entries = eina_list_append(entry->entries, sub_entry);
3638             }
3639         }
3640     }
3641
3642     /* Don't keep this list around if it is empty */
3643
3644     return entry;
3645 }
3646
3647 static Efreet_Menu *
3648 efreet_menu_layout_desktop(Efreet_Menu_Desktop *md)
3649 {
3650     Efreet_Menu *entry;
3651
3652     /* init entry */
3653     entry = efreet_menu_entry_new();
3654     entry->type = EFREET_MENU_ENTRY_DESKTOP;
3655     entry->id = eina_stringshare_add(md->id);
3656     entry->name = eina_stringshare_add(md->desktop->name);
3657     if (md->desktop->icon) entry->icon = eina_stringshare_add(md->desktop->icon);
3658     efreet_desktop_ref(md->desktop);
3659     entry->desktop = md->desktop;
3660
3661     return entry;
3662 }
3663
3664 static void
3665 efreet_menu_layout_entries_get(Efreet_Menu *entry, Efreet_Menu_Internal *internal,
3666         Efreet_Menu_Layout *layout)
3667 {
3668     Efreet_Menu *sub_entry;
3669
3670     if (internal->sub_menus && layout->type == EFREET_MENU_LAYOUT_MENUNAME)
3671     {
3672         Efreet_Menu_Internal *sub;
3673
3674         /* Efreet_Menu_Layout might be from DefaultLayout, so we need a local copy */
3675         int show_empty, in_line, inline_limit, inline_header, inline_alias;
3676
3677         if (layout->show_empty == -1) show_empty = internal->show_empty;
3678         else show_empty = layout->show_empty;
3679
3680         if (layout->in_line == -1) in_line = internal->in_line;
3681         else in_line = layout->in_line;
3682
3683         if (layout->inline_limit == -1) inline_limit = internal->inline_limit;
3684         else inline_limit = layout->inline_limit;
3685
3686         if (layout->inline_header == -1) inline_header = internal->inline_header;
3687         else inline_header = layout->inline_header;
3688
3689         if (layout->inline_alias == -1) inline_alias = internal->inline_alias;
3690         else inline_alias = layout->inline_alias;
3691
3692         sub = eina_list_search_unsorted(internal->sub_menus,
3693                                         (Eina_Compare_Cb)efreet_menu_cb_compare_names, layout->name);
3694         if (sub)
3695         {
3696             if (!(sub->directory && sub->directory->no_display) && !sub->deleted)
3697             {
3698                 sub_entry = efreet_menu_layout_menu(sub);
3699                 if (!show_empty && efreet_menu_layout_is_empty(sub_entry))
3700                     efreet_menu_free(sub_entry);
3701                 else if (in_line &&
3702                         ((inline_limit == 0) ||
3703                          (!sub_entry->entries || (eina_list_count(sub_entry->entries) <= inline_limit))))
3704                 {
3705                     /* Inline */
3706                     if (!sub_entry->entries)
3707                     {
3708                         /* Can't inline an empty submenu */
3709                         entry->entries = eina_list_append(entry->entries, sub_entry);
3710                     }
3711                     else if (inline_alias && (eina_list_count(sub_entry->entries) == 1))
3712                     {
3713                         Efreet_Menu *tmp;
3714
3715                         tmp = eina_list_data_get(sub_entry->entries);
3716                         sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3717                         IF_RELEASE(tmp->name);
3718                         tmp->name = sub_entry->name;
3719                         sub_entry->name = NULL;
3720                         IF_RELEASE(tmp->icon);
3721                         tmp->icon = sub_entry->icon;
3722                         sub_entry->icon = NULL;
3723                         entry->entries = eina_list_append(entry->entries, tmp);
3724                         efreet_menu_free(sub_entry);
3725                     }
3726                     else
3727                     {
3728                         Efreet_Menu *tmp;
3729
3730                         if (inline_header)
3731                         {
3732                             tmp = efreet_menu_entry_new();
3733                             tmp->type = EFREET_MENU_ENTRY_HEADER;
3734                             tmp->name = sub_entry->name;
3735                             sub_entry->name = NULL;
3736                             tmp->icon = sub_entry->icon;
3737                             sub_entry->icon = NULL;
3738                             entry->entries = eina_list_append(entry->entries, tmp);
3739                         }
3740                         while ((tmp = eina_list_data_get(sub_entry->entries)))
3741                         {
3742                             sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3743                             entry->entries = eina_list_append(entry->entries, tmp);
3744                         }
3745                         efreet_menu_free(sub_entry);
3746                     }
3747                 }
3748                 else
3749                     entry->entries = eina_list_append(entry->entries, sub_entry);
3750             }
3751             internal->sub_menus = eina_list_remove(internal->sub_menus, sub);
3752             efreet_menu_internal_free(sub);
3753         }
3754     }
3755     else if (internal->applications && layout->type == EFREET_MENU_LAYOUT_FILENAME)
3756     {
3757         Efreet_Menu_Desktop *md;
3758         md = eina_list_search_unsorted(internal->applications,
3759                                        (Eina_Compare_Cb)efreet_menu_cb_md_compare_ids, layout->name);
3760         if (md)
3761         {
3762             sub_entry = efreet_menu_layout_desktop(md);
3763             entry->entries = eina_list_append(entry->entries, sub_entry);
3764             internal->applications = eina_list_remove(internal->applications, md);
3765         }
3766     }
3767     else if (layout->type == EFREET_MENU_LAYOUT_MERGE)
3768     {
3769         if (internal->applications && !strcmp(layout->name, "files"))
3770         {
3771             Efreet_Menu_Desktop *md;
3772
3773             while ((md = eina_list_data_get(internal->applications)))
3774             {
3775                 internal->applications = eina_list_remove_list(internal->applications,
3776                                                                internal->applications);
3777                 sub_entry = eina_list_search_unsorted(entry->entries,
3778                                                       (Eina_Compare_Cb)efreet_menu_cb_entry_compare_desktop,
3779                                                       md->desktop);
3780                 if (!sub_entry)
3781                 {
3782                     sub_entry = efreet_menu_layout_desktop(md);
3783                     entry->entries = eina_list_append(entry->entries, sub_entry);
3784                 }
3785             }
3786             IF_FREE_LIST(internal->applications);
3787         }
3788         else if (internal->sub_menus && !strcmp(layout->name, "menus"))
3789         {
3790             Efreet_Menu_Internal *sub;
3791
3792             while ((sub = eina_list_data_get(internal->sub_menus)))
3793             {
3794                 internal->sub_menus = eina_list_remove_list(internal->sub_menus, internal->sub_menus);
3795                 if ((sub->directory && sub->directory->no_display) || sub->deleted)
3796                 {
3797                     efreet_menu_internal_free(sub);
3798                     continue;
3799                 }
3800                 sub_entry = eina_list_search_unsorted(entry->entries,
3801                                                       (Eina_Compare_Cb)efreet_menu_cb_entry_compare_menu,
3802                                                       sub);
3803                 if (!sub_entry)
3804                 {
3805                     sub_entry = efreet_menu_layout_menu(sub);
3806                     if (!internal->show_empty && efreet_menu_layout_is_empty(sub_entry))
3807                         efreet_menu_free(sub_entry);
3808                     else if (internal->in_line &&
3809                             ((internal->inline_limit == 0) ||
3810                              (!sub_entry->entries || (eina_list_count(sub_entry->entries) <= internal->inline_limit))))
3811                     {
3812                         /* Inline */
3813                         if (!sub_entry->entries)
3814                         {
3815                             /* Can't inline an empty submenu */
3816                             entry->entries = eina_list_append(entry->entries, sub_entry);
3817                         }
3818                         else if (internal->inline_alias && (eina_list_count(sub_entry->entries) == 1))
3819                         {
3820                             Efreet_Menu *tmp;
3821
3822                             tmp = eina_list_data_get(sub_entry->entries);
3823                             sub_entry->entries = eina_list_remove_list(sub_entry->entries, sub_entry->entries);
3824                             eina_stringshare_del(tmp->name);
3825                             tmp->name = sub_entry->name;
3826                             sub_entry->name = NULL;
3827                             IF_RELEASE(tmp->icon);
3828                             if (sub_entry->icon)
3829                             {
3830                                 tmp->icon = sub_entry->icon;
3831                                 sub_entry->icon = NULL;
3832                             }
3833                             entry->entries = eina_list_append(entry->entries, tmp);
3834                             efreet_menu_free(sub_entry);
3835                         }
3836                         else
3837                         {
3838                             Efreet_Menu *tmp;
3839
3840                             if (internal->inline_header)
3841                             {
3842                                 tmp = efreet_menu_entry_new();
3843                                 tmp->type = EFREET_MENU_ENTRY_HEADER;
3844                                 tmp->name = sub_entry->name;
3845                                 sub_entry->name = NULL;
3846                                 if (sub_entry->icon) tmp->icon = sub_entry->icon;
3847                                 sub_entry->icon = NULL;
3848                                 entry->entries = eina_list_append(entry->entries, tmp);
3849                             }
3850                             while ((tmp = eina_list_data_get(sub_entry->entries)))
3851                             {
3852                                 sub_entry->entries = eina_list_remove_list(sub_entry->entries,
3853                                                                            sub_entry->entries);
3854                                 entry->entries = eina_list_append(entry->entries, tmp);
3855                             }
3856                             efreet_menu_free(sub_entry);
3857                         }
3858                     }
3859                     else
3860                         entry->entries = eina_list_append(entry->entries, sub_entry);
3861                 }
3862                 efreet_menu_internal_free(sub);
3863             }
3864             IF_FREE_LIST(internal->sub_menus);
3865         }
3866         else if (internal->sub_menus && !strcmp(layout->name, "all"))
3867         {
3868             char *orig;
3869
3870             orig = layout->name;
3871             layout->name = "menus";
3872             efreet_menu_layout_entries_get(entry, internal, layout);
3873             layout->name = "files";
3874             efreet_menu_layout_entries_get(entry, internal, layout);
3875             layout->name = orig;
3876         }
3877     }
3878     else if (layout->type == EFREET_MENU_LAYOUT_SEPARATOR)
3879     {
3880         sub_entry = efreet_menu_entry_new();
3881         sub_entry->type = EFREET_MENU_ENTRY_SEPARATOR;
3882         entry->entries = eina_list_append(entry->entries, sub_entry);
3883     }
3884 }
3885
3886 static int
3887 efreet_menu_cb_entry_compare_menu(Efreet_Menu *entry, Efreet_Menu_Internal *internal)
3888 {
3889     if (entry->type != EFREET_MENU_ENTRY_MENU) return 1;
3890     return ecore_str_compare(entry->name, internal->name.name);
3891 }
3892
3893 static int
3894 efreet_menu_cb_entry_compare_desktop(Efreet_Menu *entry, Efreet_Desktop *desktop)
3895 {
3896     if (entry->type != EFREET_MENU_ENTRY_DESKTOP) return -1;
3897     return ecore_str_compare(entry->name, desktop->name);
3898 }
3899
3900 static int
3901 efreet_menu_cb_move_compare(Efreet_Menu_Move *move, const char *old)
3902 {
3903     return ecore_str_compare(move->old_name, old);
3904 }
3905
3906 static int
3907 efreet_menu_layout_is_empty(Efreet_Menu *entry)
3908 {
3909     Efreet_Menu *sub_entry;
3910     Eina_List *l;
3911
3912     if (!entry->entries) return 1;
3913
3914     EINA_LIST_FOREACH(entry->entries, l, sub_entry)
3915     {
3916         if (sub_entry->type == EFREET_MENU_ENTRY_MENU) return 0;
3917         if (sub_entry->type == EFREET_MENU_ENTRY_DESKTOP) return 0;
3918     }
3919     return 1;
3920 }