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