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