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