2 * This conforms with the freedesktop.org Desktop Menu Specification version 0.92
4 * This is gonna be repetative and slow. The idea is to first get it to correctly
5 * follow the spec in a way that is easy to check. Only then can we start to
6 * optomize into "ugly but fast".
8 * There are notes at the very end about all those nasty steps we need to do to
14 #include "Ecore_Desktop.h"
15 #include "ecore_desktop_private.h"
16 #include "ecore_private.h"
24 struct _ecore_desktop_menu_expand_apps_data
31 struct _ecore_desktop_menu_unxml_data
36 Ecore_Desktop_Tree *stack, *merge_stack;
37 int unallocated, level;
40 struct _ecore_desktop_menu_generate_data
43 Ecore_Desktop_Tree *rules;
44 Ecore_Hash *pool, *apps;
47 Ecore_Desktop_Tree *rule;
51 struct _ecore_desktop_menu_legacy_data
53 Ecore_Desktop_Tree *merge;
54 Ecore_Desktop_Tree *current;
58 int length, menu_length, level;
61 static int _ecore_desktop_menu_make_apps(const void *data,
62 Ecore_Desktop_Tree * tree,
63 int element, int level);
64 static Ecore_Desktop_Tree *_ecore_desktop_menu_get0(char *file,
66 merge_stack, int level);
67 static Ecore_Desktop_Tree *_ecore_desktop_menu_create_menu(void);
68 static int _ecore_desktop_menu_unxml(const void *data,
69 Ecore_Desktop_Tree * tree,
70 int element, int level);
71 static int _ecore_desktop_menu_check_directory(void *data,
73 static int _ecore_desktop_menu_check_menu(void *data,
75 static int _ecore_desktop_menu_legacy_menu_dir(void *data,
77 static int _ecore_desktop_menu_legacy_menu(void *data,
79 static void _ecore_desktop_menu_unxml_rules(Ecore_Desktop_Tree * rules,
80 Ecore_Desktop_Tree * tree,
81 char type, char sub_type);
82 static void _ecore_desktop_menu_unxml_moves(Ecore_Desktop_Tree * menu,
83 Ecore_Desktop_Tree * tree);
84 static void _ecore_desktop_menu_add_dirs(Ecore_Desktop_Tree * tree,
85 Ecore_List * paths, const char *pre,
86 const char *post, char *extra,
88 static int _ecore_desktop_menu_expand_apps(struct
89 _ecore_desktop_menu_unxml_data
90 *unxml_data, char *app_dir,
92 static int _ecore_desktop_menu_check_app(void *data, const char *path);
94 static int _ecore_desktop_menu_merge(const void *data,
95 Ecore_Desktop_Tree * tree,
96 int element, int level);
97 static int _ecore_desktop_menu_expand_default_dirs(const void *data,
103 static int _ecore_desktop_menu_generate(const void *data,
104 Ecore_Desktop_Tree * tree,
105 int element, int level);
106 static int _ecore_desktop_menu_is_include(const void *data, Ecore_Desktop_Tree * tree, int element, int level __UNUSED__);
107 static void _ecore_desktop_menu_inherit_apps(void *value,
109 static void _ecore_desktop_menu_select_app(void *value,
111 static int _ecore_desktop_menu_apply_rules(struct
112 _ecore_desktop_menu_generate_data
114 Ecore_Desktop_Tree * rule,
116 Ecore_Desktop * desktop);
119 * @defgroup Ecore_Desktop_Menu_Group menu Functions
121 * Functions that deal with freedesktop.org menus.
125 ecore_desktop_menu_for_each(void (*func)
126 (const char *name, const char *path, const char *directory, Ecore_Hash * apps))
130 /* Find the main menu file. */
131 menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
132 "applications.menu", -1, NULL,
136 /* Try various quirks of various systems and other wms. */
137 menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
138 "kde-applications.menu", -1, NULL,
142 menu_file = ecore_desktop_paths_file_find(ecore_desktop_paths_menus,
143 "debian-menu.menu", -1, NULL,
145 /* FIXME: If all else fails, run debians funky menu generator shit. */
151 Ecore_Desktop_Tree *menus;
153 /* convert the xml into menus */
154 menus = ecore_desktop_menu_get(menu_file);
157 /* create the .desktop and order files from the menu */
158 ecore_desktop_tree_foreach(menus, 0, _ecore_desktop_menu_make_apps,
160 // FIXME: Can't free this just yet, causes major memory corruption.
161 // this leaks a LOT (428,200 bytes in one valgrind run...)
162 // ecore_desktop_tree_del(menus);
169 _ecore_desktop_menu_make_apps(const void *data, Ecore_Desktop_Tree * tree,
170 int element, int level __UNUSED__)
172 if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
174 if (strncmp((char *)tree->elements[element].element, "<MENU ", 6) == 0)
181 void (*func) (char *name, char *path, char *directory,
185 name = (char *)tree->elements[element].element;
188 path = strchr(directory, '<');
190 directory = strchr(path, '<');
192 len = strlen(directory);
195 path = strdup(directory);
198 path[len - 1] = '\0';
199 /* FIXME: Figure out what to do if it's just ".directory". */
200 directory = ecore_desktop_paths_file_find(ecore_desktop_paths_directories, path, 0, NULL, NULL);
208 path = (char *)tree->elements[element + 1].element;
209 // pool = (Ecore_Hash *) tree->elements[element + 2].element;
210 apps = (Ecore_Hash *) tree->elements[element + 4].element;
213 printf("OUTPUTTING MENU - %s \t\t%s \t\t%s\n", path, name, directory);
216 func(name, path, directory, apps);
224 * Decode a freedesktop.org menu XML jungle.
226 * Using the algorithm specified by freedesktop.org, fully decode
227 * a menu based on an initial menu file.
229 * @param file The base file for the menu.
230 * @return The resulting menu tree.
231 * @ingroup Ecore_Desktop_Menu_Group
235 ecore_desktop_menu_get(char *file)
237 return _ecore_desktop_menu_get0(file, NULL, 0);
240 static Ecore_Desktop_Tree *
241 _ecore_desktop_menu_get0(char *file, Ecore_Desktop_Tree * merge_stack,
244 Ecore_Desktop_Tree *menu_xml;
245 struct _ecore_desktop_menu_unxml_data data;
249 data.stack = ecore_desktop_tree_new(NULL);
250 /* FIXME data.base and data.path leak */
251 data.base = ecore_file_strip_ext(ecore_file_file_get(file));
252 data.path = ecore_file_dir_get(file);
253 if ((level == 0) && (merge_stack == NULL))
254 merge_stack = ecore_desktop_tree_new(NULL);
256 printf("MENU FILE %d - %s\n", level, file);
258 menu_xml = ecore_desktop_xmlame_get(file);
259 if ((data.stack) && (data.base) && (data.path) && (merge_stack)
266 data.merge_stack = merge_stack;
268 /* Setup the merge stack. */
269 if (merge_stack->size <= level)
271 /* XXX storing a pointer to a static string probably isn't a good idea. this is most likely the cause of the valgrind error mentioned below. (once this function has exited that memory will no longer be valid) */
272 while (merge_stack->size < level)
273 ecore_desktop_tree_add(merge_stack, "");
274 ecore_desktop_tree_add(merge_stack, file);
277 merge_stack->elements[level].element = file;
279 /* Find out if we are looping. */
280 for (i = 0; i < level; i++)
284 /* I can safely assume that they are all strings. */
285 text = (char *)merge_stack->elements[i].element;
286 /* printf("--- %s --- %s\n", text, file);
289 if (strcmp(text, file) == 0)
291 /* FIXME: valgrind says -
292 ==5761== Conditional jump or move depends on uninitialised value(s)
293 ==5761== at 0x1B90565C: strcmp (in /usr/lib/valgrind/vgpreload_memcheck.so)
294 ==5761== by 0x1BA3E984: _ecore_desktop_menu_get0 (ecore_desktop_menu.c:176)
295 ==5761== by 0x1BA3FE3F: _ecore_desktop_menu_merge (ecore_desktop_menu.c:1146)
296 ==5761== by 0x1BA3D5A2: ecore_desktop_tree_foreach (ecore_desktop_tree.c:267)
297 * which is the strcmp just above. But it doesn't complain about the first two if's,
298 * or the printf, which I inserted to try and track this down.
299 * No idea what it actually is complaining about, so I'll comment it for future study.
301 * This is probably caused by the fact that a static string was passed into the
302 * tree. See the comment above where this occurs.
305 "\n### Oops, infinite menu merging loop detected at %s\n",
313 /* Get on with it. */
314 ecore_desktop_tree_foreach(menu_xml, 0, _ecore_desktop_menu_unxml,
316 ecore_desktop_tree_foreach(menu_xml, 0, _ecore_desktop_menu_merge,
319 /* The rest of this is only done after ALL the menus have been merged. */
322 ecore_desktop_tree_foreach(menu_xml, 0,
323 _ecore_desktop_menu_expand_default_dirs,
327 ecore_desktop_tree_dump(menu_xml, 0);
331 data.unallocated = FALSE;
332 ecore_desktop_tree_foreach(menu_xml, 0,
333 _ecore_desktop_menu_generate,
336 data.unallocated = TRUE;
337 ecore_desktop_tree_foreach(menu_xml, 0,
338 _ecore_desktop_menu_generate,
342 // ecore_desktop_tree_dump(menu_xml, 0);
353 E_FN_DEL(ecore_desktop_tree_del, (menu_xml));
356 E_FN_DEL(ecore_desktop_tree_del, (merge_stack));
360 E_FN_DEL(ecore_desktop_tree_del, (data.stack));
368 _ecore_desktop_menu_unxml(const void *data, Ecore_Desktop_Tree * tree,
369 int element, int level)
371 struct _ecore_desktop_menu_unxml_data *unxml_data;
373 unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
374 if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
376 if (strncmp((char *)tree->elements[element].element, "<!", 2) == 0)
378 tree->elements[element].type =
379 ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
380 tree->elements[element].element = NULL;
382 else if (strcmp((char *)tree->elements[element].element, "<Menu") == 0)
384 Ecore_Desktop_Tree *menu;
386 menu = _ecore_desktop_menu_create_menu();
390 char *flags = " ", *name = "", *directory =
394 flags = (char *)menu->elements[0].element;
396 tree->elements[element].element = menu;
397 tree->elements[element].type =
398 ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE;
399 for (i = element + 1; i < tree->size; i++)
403 if (tree->elements[i].type ==
404 ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
407 ((char *)tree->elements[i].element, "<!",
410 tree->elements[i].type =
411 ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
412 tree->elements[i].element = NULL;
416 ((char *)tree->elements[i].element,
424 ((char *)tree->elements[i].element,
425 "<NotDeleted/") == 0)
432 ((char *)tree->elements[i].element,
433 "<OnlyUnallocated/") == 0)
440 ((char *)tree->elements[i].element,
441 "<NotOnlyUnallocated/") == 0)
448 ((char *)tree->elements[i].element,
455 ecore_desktop_tree_extend(menu,
457 elements[i].element);
461 else if (tree->elements[i].type ==
462 ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
464 Ecore_Desktop_Tree *sub;
467 (Ecore_Desktop_Tree *) tree->elements[i].element;
468 if ((sub) && (sub->size))
470 if (sub->elements[0].type ==
471 ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
474 ((char *)sub->elements[0].element,
480 strdup((char *)sub->elements[1].
483 "<MENU <%.4s> <%s> <%s>",
484 flags, name, directory);
485 menu->elements[0].element =
487 ecore_desktop_tree_track(menu, name);
488 ecore_desktop_tree_track(menu,
493 (char *)menu->elements[0].element;
495 /* The rest of this is probably not needed, except to ease debugging. */
496 if (unxml_data->stack->size <= level)
498 while (unxml_data->stack->size <
500 ecore_desktop_tree_add
501 (unxml_data->stack, "");
502 ecore_desktop_tree_add
503 (unxml_data->stack, name);
507 elements[level].element = name;
509 for (i = 0; i <= level; i++)
513 /* I can safely assume that they are all strings. */
515 (char *)unxml_data->stack->
519 sprintf(&temp[length],
528 menu_path = strdup(temp);
529 sprintf(temp, "<MENU_PATH %s",
531 menu->elements[1].element =
533 ecore_desktop_tree_track(menu,
535 ecore_desktop_tree_track(menu,
541 /* FIXME: Move this to later in the sequence. */
544 ((char *)sub->elements[0].element,
548 strdup((char *)sub->elements[1].
551 "<MENU <%.4s> <%s> <%s>",
552 flags, name, directory);
553 menu->elements[0].element =
555 ecore_desktop_tree_track(menu,
557 ecore_desktop_tree_track(menu,
562 (char *)menu->elements[0].element;
568 ((char *)sub->elements[0].element,
571 _ecore_desktop_menu_unxml(data, sub,
574 ecore_desktop_tree_add_child(menu,
581 /* FIXME: Dunno if this causes a memory leak, but for now we play it safe. */
582 tree->elements[i].type =
583 ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
584 tree->elements[i].element = NULL;
589 ((char *)sub->elements[0].element,
592 _ecore_desktop_menu_unxml_moves
599 && (sub->elements[1].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
600 && (((char *)sub->elements[1].element)[0] != '<'))
604 snprintf(temp, PATH_MAX, "%s %s",
605 (char *)sub->elements[0].element,
606 (char *)sub->elements[1].element);
607 ecore_desktop_tree_extend(menu, temp);
612 ecore_desktop_tree_add_child
614 tree->elements[i].type =
615 ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
616 tree->elements[i].element =
625 if (tree->elements[i].type ==
626 ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
627 ecore_desktop_tree_del((Ecore_Desktop_Tree *)
630 tree->elements[i].type =
631 ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
632 tree->elements[i].element = NULL;
636 /* Add it if it has not been deleted. */
639 tree->elements[element].element = menu;
640 tree->elements[element].type =
641 ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE;
649 static Ecore_Desktop_Tree *
650 _ecore_desktop_menu_create_menu()
652 Ecore_Desktop_Tree *menu, *rules;
653 Ecore_Hash *pool, *apps;
655 menu = ecore_desktop_tree_new(NULL);
656 rules = ecore_desktop_tree_new(NULL);
657 pool = ecore_hash_new(ecore_str_hash, ecore_str_compare);
658 apps = ecore_hash_new(ecore_str_hash, ecore_str_compare);
659 if ((menu) && (rules) && (pool) && (apps))
661 ecore_hash_free_key_cb_set(pool, free);
662 ecore_hash_free_value_cb_set(pool, free);
663 ecore_hash_free_key_cb_set(apps, free);
664 ecore_hash_free_value_cb_set(apps, free);
665 ecore_desktop_tree_extend(menu, "<MENU < > <> <>");
666 ecore_desktop_tree_extend(menu, "<MENU_PATH ");
667 ecore_desktop_tree_add_hash(menu, pool);
668 ecore_desktop_tree_add_child(menu, rules);
669 ecore_desktop_tree_add_hash(menu, apps);
674 ecore_hash_destroy(apps);
676 ecore_hash_destroy(pool);
678 ecore_desktop_tree_del(rules);
680 ecore_desktop_tree_del(menu);
688 _ecore_desktop_menu_check_directory(void *data, const char *path)
691 Ecore_Desktop_Tree *merge;
693 merge = (Ecore_Desktop_Tree *) data;
694 p = strrchr(path, '.');
697 if (strcmp(p, ".directory") == 0)
699 char merge_file[PATH_MAX];
701 sprintf(merge_file, "<Directory %s", path);
702 ecore_desktop_tree_extend(merge, merge_file);
710 _ecore_desktop_menu_check_menu(void *data, const char *path)
713 Ecore_Desktop_Tree *merge;
715 merge = (Ecore_Desktop_Tree *) data;
716 p = strrchr(path, '.');
719 if (strcmp(p, ".menu") == 0)
721 char merge_file[PATH_MAX];
723 sprintf(merge_file, "<MergeFile type=\"path\" %s", path);
724 ecore_desktop_tree_extend(merge, merge_file);
732 _ecore_desktop_menu_legacy_menu_dir(void *data, const char *path)
734 struct _ecore_desktop_menu_legacy_data *legacy_data;
735 Ecore_Desktop_Tree *menu;
740 legacy_data = (struct _ecore_desktop_menu_legacy_data *)data;
741 for (i = legacy_data->length; path[i] != '\0'; i++)
748 legacy_data->current = legacy_data->merge;
749 legacy_data->level = 0;
751 else if (count <= legacy_data->level)
753 if ((legacy_data->current) && (legacy_data->current->parent))
755 legacy_data->current = legacy_data->current->parent;
756 legacy_data->level--;
760 start = legacy_data->length;
762 temp_path = strdup(path);
765 for (i = legacy_data->length; temp_path[i] != '\0'; i++)
767 if (temp_path[i] == '/')
769 if (count >= legacy_data->level)
772 menu = _ecore_desktop_menu_create_menu();
777 sprintf(temp, "<MENU < L> <%s> <>",
779 menu->elements[0].element = strdup(temp);
780 ecore_desktop_tree_track(menu,
781 menu->elements[0].element);
782 sprintf(temp, "<MENU_PATH %s/%s", legacy_data->menu,
783 &temp_path[legacy_data->length]);
784 menu->elements[1].element = strdup(temp);
785 ecore_desktop_tree_track(menu,
786 menu->elements[1].element);
788 ecore_desktop_tree_add_child(legacy_data->current,
790 /* This is not needed, but if it was, this is where it would go.
791 * sprintf(temp, "<AppDir %s/", temp_path);
792 * ecore_desktop_tree_extend(menu, temp);
794 sprintf(temp, "<DirectoryDir %s/", temp_path);
795 ecore_desktop_tree_extend(menu, temp);
797 legacy_data->current = menu;
798 legacy_data->level++;
809 legacy_data->level = count;
815 _ecore_desktop_menu_legacy_menu(void *data, const char *path)
817 struct _ecore_desktop_menu_legacy_data *legacy_data;
821 int i, count = 0, menu_count = 0;
823 legacy_data = (struct _ecore_desktop_menu_legacy_data *)data;
824 if ((legacy_data->current) && (legacy_data->current->size > 0) &&
825 (legacy_data->current->elements[1].type ==
826 ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING))
828 menu_path = (char *)legacy_data->current->elements[1].element;
829 menu_path += 12 + legacy_data->menu_length;
834 printf(" PROBLEM IN LEGACYDIR FILE - %s - %s %s\n",
835 legacy_data->prefix, legacy_data->path,
836 &path[legacy_data->length]);
839 for (i = legacy_data->length; path[i] != '\0'; i++)
844 for (i = 0; menu_path[i] != '\0'; i++)
846 if (menu_path[i] == '/')
849 /* FIXME: The (legacy_data->current) test is just to patch a seggie, find out why. */
850 while ((menu_count >= count) && (legacy_data->current))
852 legacy_data->current = legacy_data->current->parent;
855 if ((legacy_data->current) && (legacy_data->current->size > 0) &&
856 (legacy_data->current->elements[1].type ==
857 ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING))
859 menu_path = (char *)legacy_data->current->elements[1].element;
860 menu_path += 12 + legacy_data->menu_length;
864 /* FIXME: Bugger, a corner case.
865 * If this is .directory, then maybe ignore it?
866 * If this is a desktop, we need to put it into the pool of the menu that legacy_data->merge will get merged into.
869 printf(" LEGACYDIR FILE - %s - %s %s\n", legacy_data->prefix,
870 legacy_data->path, &path[legacy_data->length]);
874 menu_count = strlen(menu_path);
877 file = (char *)&path[legacy_data->length + menu_count];
878 count = strlen(file);
880 if (strcmp(".directory", file) == 0)
882 menu_path = (char *)legacy_data->current->elements[0].element;
883 menu_count = strlen(menu_path);
884 menu_path[menu_count - 3] = '\0';
885 sprintf(temp, "%s <.directory>", menu_path);
886 legacy_data->current->elements[0].element = strdup(temp);
887 ecore_desktop_tree_track(legacy_data->current,
888 legacy_data->current->elements[0].element);
890 else if (strcmp(".desktop", &file[count - 8]) == 0)
893 Ecore_Desktop_Tree *rules;
895 pool = (Ecore_Hash *) legacy_data->current->elements[2].element;
897 (Ecore_Desktop_Tree *) legacy_data->current->elements[3].element;
898 if (rules->size == 0)
900 Ecore_Desktop_Tree *new_rules;
902 new_rules = ecore_desktop_tree_new(NULL);
904 ecore_desktop_tree_add_child(rules, new_rules);
906 sprintf(temp, "%s%s", legacy_data->prefix, file);
907 ecore_hash_set(pool, strdup(temp), strdup(path));
910 // ("POOLING - _ecore_desktop_menu_legacy_menu(void *data, %s) - %s - %s\n",
911 // path, file, temp);
915 rules = (Ecore_Desktop_Tree *) rules->elements[0].element;
916 sprintf(temp, "IOF %s%s", legacy_data->prefix, file);
917 ecore_desktop_tree_extend(rules, temp);
922 printf("PROBLEM - _ecore_desktop_menu_legacy_menu(void *data, %s) - %s\n",
930 _ecore_desktop_menu_unxml_rules(Ecore_Desktop_Tree * rules,
931 Ecore_Desktop_Tree * tree, char type,
937 for (i = 0; i < tree->size; i++)
939 if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
941 /* FIXME <All /> or <All foo="bar"> would be valid input (which this won't catch) */
942 if (strcmp((char *)tree->elements[i].element, "<All/") == 0)
944 sprintf(temp, "%c%cA", type, sub_type);
945 ecore_desktop_tree_extend(rules, temp);
947 else if (strcmp((char *)tree->elements[i].element, "<Filename") ==
950 sprintf(temp, "%c%cF %s", type, sub_type,
951 (char *)tree->elements[i + 1].element);
952 ecore_desktop_tree_extend(rules, temp);
954 else if (strcmp((char *)tree->elements[i].element, "<Category") ==
957 sprintf(temp, "%c%cC %s", type, sub_type,
958 (char *)tree->elements[i + 1].element);
959 ecore_desktop_tree_extend(rules, temp);
961 else if (strcmp((char *)tree->elements[i].element, "<Or") == 0)
963 _ecore_desktop_menu_unxml_rules(rules,
964 (Ecore_Desktop_Tree *) tree->
965 elements[i + 1].element,
968 else if ((strcmp((char *)tree->elements[i].element, "<And") == 0)
969 || (strcmp((char *)tree->elements[i].element, "<Not") ==
973 Ecore_Desktop_Tree *sub;
975 this_type = ((char *)tree->elements[i].element)[1];
976 sub = ecore_desktop_tree_new(NULL);
979 ecore_desktop_tree_add_child(rules, sub);
980 for (i++; i < tree->size; i++)
982 if (tree->elements[i].type ==
983 ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
984 _ecore_desktop_menu_unxml_rules(sub,
994 else if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
996 _ecore_desktop_menu_unxml_rules(rules,
997 (Ecore_Desktop_Tree *) tree->
998 elements[i].element, type,
1005 _ecore_desktop_menu_unxml_moves(Ecore_Desktop_Tree * menu,
1006 Ecore_Desktop_Tree * tree)
1012 for (i = 0; i < tree->size; i++)
1014 if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1016 Ecore_Desktop_Tree *sub;
1018 sub = (Ecore_Desktop_Tree *) tree->elements[i].element;
1019 if ((sub) && (sub->size))
1021 if (sub->elements[0].type ==
1022 ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1024 if (strcmp((char *)sub->elements[0].element, "<Old") ==
1026 old = strdup((char *)sub->elements[1].element);
1027 if (strcmp((char *)sub->elements[0].element, "<New") ==
1029 new = strdup((char *)sub->elements[1].element);
1035 char temp[PATH_MAX * 2];
1037 sprintf(temp, "<MOVE <%s> <%s>", old, new);
1038 ecore_desktop_tree_extend(menu, temp);
1049 _ecore_desktop_menu_add_dirs(Ecore_Desktop_Tree * tree, Ecore_List * paths,
1050 const char *pre, const char *post __UNUSED__, char *extra, int element __UNUSED__)
1052 char t[PATH_MAX], *this_path;
1054 /* reverse the order of the dirs. */
1055 ecore_list_first_goto(paths);
1056 while ((this_path = ecore_list_next(paths)) != NULL)
1059 sprintf(t, "%s %s%s-merged/", pre, this_path, extra);
1061 sprintf(t, "%s %s", pre, this_path);
1063 ecore_desktop_tree_extend(tree, t);
1068 _ecore_desktop_menu_expand_apps(struct _ecore_desktop_menu_unxml_data
1069 *unxml_data, char *app_dir, Ecore_Hash * pool)
1073 struct _ecore_desktop_menu_expand_apps_data our_data;
1076 our_data.pool = pool;
1077 sprintf(dir, "%s", app_dir);
1079 sprintf(dir, "%s/%s", unxml_data->path, app_dir);
1080 our_data.path = dir;
1081 our_data.length = strlen(dir);
1084 ("EXPANDING - _ecore_desktop_menu_expand_apps(unxml_data, %s) - %s\n",
1087 ecore_desktop_paths_recursive_search(dir, NULL, -1, NULL,
1088 _ecore_desktop_menu_check_app,
1095 _ecore_desktop_menu_check_app(void *data, const char *path)
1098 struct _ecore_desktop_menu_expand_apps_data *our_data;
1100 our_data = (struct _ecore_desktop_menu_expand_apps_data *)data;
1101 p = strrchr(path, '.');
1104 if (strcmp(p, ".desktop") == 0)
1109 file = strdup(path + our_data->length);
1110 if ((file) && (path))
1112 for (i = 0; file[i] != '\0'; i++)
1115 ecore_hash_set(our_data->pool, file, strdup(path));
1118 // ("POOLING - _ecore_desktop_menu_check_app(void *data, %s) - %s\n",
1125 printf("PROBLEM - _ecore_desktop_menu_check_app(void *data, %s)\n",
1134 _ecore_desktop_menu_merge(const void *data, Ecore_Desktop_Tree * tree,
1135 int element, int level)
1137 struct _ecore_desktop_menu_unxml_data *unxml_data;
1138 Ecore_Desktop_Tree *merge;
1141 unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
1142 merge = ecore_desktop_tree_new(NULL);
1143 if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1147 string = (char *)tree->elements[element].element;
1148 if (strcmp(string, "<DefaultMergeDirs/") == 0)
1150 if (unxml_data->base)
1151 _ecore_desktop_menu_add_dirs(merge, ecore_desktop_paths_menus,
1152 "<MergeDir", "</MergeDir",
1153 unxml_data->base, element);
1156 else if (strcmp(string, "<KDELegacyDirs/") == 0)
1158 _ecore_desktop_menu_add_dirs(merge,
1159 ecore_desktop_paths_kde_legacy,
1160 "<LegacyDir prefix=\"kde-\"",
1161 "</LegacyDir", NULL, element);
1164 else if (strncmp(string, "<MergeDir ", 10) == 0)
1166 char merge_path[PATH_MAX];
1168 if (string[10] == '/')
1169 sprintf(merge_path, "%s", &string[10]);
1171 sprintf(merge_path, "%s%s", unxml_data->path, &string[10]);
1172 ecore_desktop_paths_recursive_search(merge_path, NULL, -1, NULL,
1173 _ecore_desktop_menu_check_menu,
1177 else if (strncmp(string, "<LegacyDir ", 11) == 0)
1179 char merge_path[PATH_MAX];
1180 struct _ecore_desktop_menu_legacy_data legacy_data;
1183 legacy_data.prefix = NULL;
1184 legacy_data.merge = merge;
1185 legacy_data.current = merge;
1186 legacy_data.level = 0;
1187 legacy_data.menu = (char *)tree->elements[0].element;
1188 legacy_data.menu += 14;
1189 legacy_data.menu_length =
1190 strchr(legacy_data.menu, '>') - legacy_data.menu;
1191 legacy_data.menu[legacy_data.menu_length] = '\0';
1192 if (strncmp(string, "prefix=\"", 8) == 0)
1195 legacy_data.prefix = string;
1196 while ((*string != '"') && (*string != '\0'))
1198 if (*string != '\0')
1200 while ((*string == ' ') && (*string != '\0'))
1203 if (string[0] == '/')
1204 sprintf(merge_path, "%s", string);
1206 sprintf(merge_path, "%s%s", unxml_data->path, string);
1207 legacy_data.path = merge_path;
1208 legacy_data.length = strlen(merge_path);
1210 printf("<LEGACYDIR> - %s - %s\n", legacy_data.prefix, merge_path);
1212 ecore_desktop_paths_recursive_search(merge_path, NULL, -1,
1213 _ecore_desktop_menu_legacy_menu_dir,
1214 _ecore_desktop_menu_legacy_menu,
1216 legacy_data.menu[legacy_data.menu_length] = '>';
1219 else if (strncmp(string, "<MergeFile ", 11) == 0)
1221 char merge_path[PATH_MAX];
1224 /* FIXME: need to weed out duplicate <MergeFile's, use the last one. */
1226 if (strncmp(string, "type=\"", 6) == 0)
1229 if (strncmp(string, "parent\"", 7) == 0)
1231 while ((*string != '"') && (*string != '\0'))
1233 if (*string != '\0')
1235 while ((*string == ' ') && (*string != '\0'))
1240 if (string[0] == '/')
1241 snprintf(merge_path, PATH_MAX, "%s", string);
1243 snprintf(merge_path, PATH_MAX, "%s/%s", unxml_data->path, string);
1245 else /* This is a parent type MergeFile. */
1250 /* The spec is a little unclear, and the examples may look like they
1251 * contradict the description, but it all makes sense if you cross
1252 * reference it with the XDG Base Directory Specification (version 0.6).
1253 * To make things harder, parent type MergeFiles never appear on my box.
1255 * What you do is this.
1257 * Just plain ignore the specified path in the MergeFile element, it's for
1258 * legacy apps that don't understand parent types.
1260 * Take the XDG_CONFIG_DIRS stuff as a whole ($XDG_CONFIG_HOME, then
1261 * $XDG_CONFIG_DIRS), in this code that will be ecore_desktop_paths_config.
1263 * If this menu file is from one of the directories in ecore_desktop_paths_config,
1264 * scan the rest of ecore_desktop_paths_config looking for the new menu. In other
1265 * words start searching in the next ecore_desktop_paths_config entry after the one
1266 * that this menu is in.
1268 * The file to look for is the path to this menu with the portion from
1269 * ecore_desktop_paths_config stripped off the beginning. For instance, the top level
1270 * menu file is typically /etc/xdg/menus/applications.menu, and /etc/xdg is
1271 * typically in ecore_desktop_paths_config, so search for menus/applications.menu.
1273 * If this menu file is NOT from one of the directories in ecore_desktop_paths_menus,
1276 * The first one found wins, if none are found, don't merge anything.
1279 merge_path[0] = '\0';
1280 ecore_list_first_goto(ecore_desktop_paths_config);
1281 while ((xdg_path = ecore_list_next(ecore_desktop_paths_config)) != NULL)
1285 int length = strlen(xdg_path);
1287 if (strncmp(xdg_path, unxml_data->file, length) == 0)
1292 snprintf(merge_path, PATH_MAX, "%s%s", xdg_path, &(unxml_data->file)[found]);
1293 if (ecore_file_exists(merge_path))
1295 merge_path[0] = '\0';
1300 if (merge_path[0] != '\0')
1302 Ecore_Desktop_Tree *new_menu;
1305 _ecore_desktop_menu_get0(merge_path,
1306 unxml_data->merge_stack,
1310 if (new_menu->size > 1)
1312 if (new_menu->elements[1].type ==
1313 ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1316 (Ecore_Desktop_Tree *) new_menu->
1317 elements[1].element;
1318 if (new_menu->size > 0)
1320 if (new_menu->elements[0].type ==
1321 ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1324 (Ecore_Desktop_Tree *) new_menu->
1325 elements[0].element;
1326 ecore_desktop_tree_remove(merge, 0);
1327 ecore_desktop_tree_remove(merge, 1);
1328 ecore_desktop_tree_remove(merge, 2);
1329 ecore_desktop_tree_remove(merge, 3);
1330 ecore_desktop_tree_remove(merge, 4);
1331 /* FIXME: The MENU_PATHs need to be prefixed. */
1335 ("FUCK an error in _ecore_desktop_menu_merge(%s)\n",
1340 ("FUCK another error in _ecore_desktop_menu_merge(%s)\n",
1345 ("FUCK ME! An error in _ecore_desktop_menu_merge(%s)\n",
1356 if ((merge) && (merge->size))
1357 ecore_desktop_tree_merge(tree, element + 1, merge);
1359 tree->elements[element].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
1360 tree->elements[element].element = NULL;
1367 _ecore_desktop_menu_expand_default_dirs(const void *data,
1368 Ecore_Desktop_Tree * tree, int element,
1369 int level __UNUSED__)
1371 struct _ecore_desktop_menu_unxml_data *unxml_data;
1372 Ecore_Desktop_Tree *merge;
1375 unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
1376 merge = ecore_desktop_tree_new(NULL);
1377 if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1381 string = (char *)tree->elements[element].element;
1382 if (strcmp(string, "<DefaultAppDirs/") == 0)
1384 _ecore_desktop_menu_add_dirs(merge, ecore_desktop_paths_desktops,
1385 "<AppDir", "</AppDir", NULL, element);
1388 else if (strcmp(string, "<DefaultDirectoryDirs/") == 0)
1390 _ecore_desktop_menu_add_dirs(merge,
1391 ecore_desktop_paths_directories,
1392 "<DirectoryDir", "</DirectoryDir",
1399 if ((merge) && (merge->size))
1400 ecore_desktop_tree_merge(tree, element + 1, merge);
1402 tree->elements[element].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
1403 tree->elements[element].element = NULL;
1410 _ecore_desktop_menu_generate(const void *data, Ecore_Desktop_Tree * tree,
1411 int element, int level)
1413 struct _ecore_desktop_menu_unxml_data *unxml_data;
1415 unxml_data = (struct _ecore_desktop_menu_unxml_data *)data;
1416 if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1418 if (strncmp((char *)tree->elements[element].element, "<MENU ", 6) == 0)
1421 struct _ecore_desktop_menu_generate_data generate_data;
1423 generate_data.unallocated = unxml_data->unallocated;
1424 generate_data.name = (char *)tree->elements[element].element;
1425 generate_data.path = (char *)tree->elements[element + 1].element;
1426 generate_data.pool = (Ecore_Hash *) tree->elements[element + 2].element;
1427 generate_data.rules = (Ecore_Desktop_Tree *) tree->elements[element + 3].element;
1428 generate_data.apps = (Ecore_Hash *) tree->elements[element + 4].element;
1430 /* generate and inherit the pools on the first pass, and preparse the include/exclude logic. */
1431 if (!generate_data.unallocated)
1435 for (i = element + 5; i < tree->size; i++)
1440 if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1442 string = (char *)tree->elements[i].element;
1443 if (strncmp(string, "<AppDir ", 8) == 0)
1445 _ecore_desktop_menu_expand_apps(unxml_data, &string[8], generate_data.pool);
1448 else if (strncmp(string, "<DirectoryDir ", 14) == 0)
1450 char merge_path[PATH_MAX];
1451 Ecore_Desktop_Tree *merge;
1453 if (string[14] == '/')
1454 sprintf(merge_path, "%s", &string[14]);
1456 sprintf(merge_path, "%s%s", unxml_data->path, &string[14]);
1457 merge = ecore_desktop_tree_new(NULL);
1460 ecore_desktop_paths_recursive_search(merge_path, NULL, -1, NULL, _ecore_desktop_menu_check_directory, merge);
1461 ecore_desktop_tree_merge(tree, i + 1, merge);
1466 else if (tree->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1468 Ecore_Desktop_Tree *sub;
1470 sub = (Ecore_Desktop_Tree *) tree->elements[i].element;
1471 if ((sub) && (sub->size))
1473 if (sub->elements[0].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1475 string = (char *)sub->elements[0].element;
1476 if ((strcmp(string, "<Include") == 0) || (strcmp(string, "<Exclude") == 0))
1478 Ecore_Desktop_Tree *new_sub;
1480 new_sub = ecore_desktop_tree_new(NULL);
1483 ecore_desktop_tree_add_child(generate_data.rules, new_sub);
1484 _ecore_desktop_menu_unxml_rules(new_sub, sub, string[1], 'O');
1494 tree->elements[i].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_NULL;
1495 tree->elements[i].element = NULL;
1499 if (unxml_data->stack->size <= level)
1501 while (unxml_data->stack->size < level)
1502 ecore_desktop_tree_add_hash(unxml_data->stack, generate_data.pool);
1503 ecore_desktop_tree_add_hash(unxml_data->stack, generate_data.pool);
1507 unxml_data->stack->elements[level].type = ECORE_DESKTOP_TREE_ELEMENT_TYPE_HASH;
1508 unxml_data->stack->elements[level].element = generate_data.pool;
1510 for (i = level - 1; i >= 0; i--)
1512 if (unxml_data->stack->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_HASH)
1514 Ecore_Hash *ancestor;
1516 ancestor = (Ecore_Hash *) unxml_data->stack->elements[i].element;
1517 ecore_hash_for_each_node(ancestor, _ecore_desktop_menu_inherit_apps, generate_data.pool);
1522 /* Process the rules. */
1523 if (generate_data.name[9] == (generate_data.unallocated ? 'O' : ' '))
1526 printf("MAKING MENU - %s \t\t%s\n", generate_data.path, generate_data.name);
1527 ecore_desktop_tree_dump(generate_data.rules, 0);
1530 for (i = 0; i < generate_data.rules->size; i++)
1532 if (generate_data.rules->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1534 generate_data.rule = (Ecore_Desktop_Tree *) generate_data.rules->elements[i].element;
1535 if (generate_data.rule->size > 0)
1539 /* Find out if this is an include or an exclude. This info may be way down in the tree. */
1540 if (generate_data.rule->elements[0].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1541 ecore_desktop_tree_foreach(generate_data.rule, 0, _ecore_desktop_menu_is_include, &type);
1544 generate_data.include = TRUE;
1545 ecore_hash_for_each_node(generate_data.pool, _ecore_desktop_menu_select_app, &generate_data);
1549 generate_data.include = FALSE;
1550 ecore_hash_for_each_node(generate_data.apps, _ecore_desktop_menu_select_app, &generate_data);
1555 printf("Fuck, a bug in _ecore_desktop_menus.\n");
1564 _ecore_desktop_menu_is_include(const void *data, Ecore_Desktop_Tree * tree, int element, int level __UNUSED__)
1568 result = (char *)data;
1569 if (tree->elements[element].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_STRING)
1571 *result = ((char *)tree->elements[element].element)[0];
1579 _ecore_desktop_menu_inherit_apps(void *value, void *user_data)
1581 Ecore_Hash_Node *node;
1585 pool = (Ecore_Hash *) user_data;
1586 node = (Ecore_Hash_Node *) value;
1587 key = (char *)node->key;
1588 app = (char *)node->value;
1590 // printf("CHECKING %s - %s\n", app, key);
1592 if (!ecore_hash_get(pool, key))
1593 ecore_hash_set(pool, strdup(key), strdup(app));
1597 _ecore_desktop_menu_select_app(void *value, void *user_data)
1599 Ecore_Hash_Node *node;
1600 Ecore_Desktop *desktop;
1601 struct _ecore_desktop_menu_generate_data *generate_data;
1604 node = (Ecore_Hash_Node *) value;
1605 generate_data = (struct _ecore_desktop_menu_generate_data *)user_data;
1606 key = (char *)node->key;
1607 app = (char *)node->value;
1609 /* FIXME: pass an actuall language parameter. */
1610 desktop = ecore_desktop_get(app, NULL);
1614 if ((generate_data->unallocated) && (desktop->allocated))
1617 if (_ecore_desktop_menu_apply_rules(generate_data, generate_data->rule, key, desktop))
1619 desktop->allocated = TRUE;
1620 if (generate_data->include)
1622 ecore_hash_set(generate_data->apps, key, strdup(app));
1624 printf("INCLUDING %s%s - %s\n", ((generate_data->unallocated) ? "UNALLOCATED " : ""), app, key);
1629 ecore_hash_remove(generate_data->apps, key);
1631 printf("EXCLUDING %s%s - %s\n", ((generate_data->unallocated) ? "UNALLOCATED " : ""), app, key);
1639 _ecore_desktop_menu_apply_rules(struct _ecore_desktop_menu_generate_data *generate_data, Ecore_Desktop_Tree * rule, char *key, Ecore_Desktop * desktop)
1645 for (i = 0; i < rule->size; i++)
1647 if (rule->elements[i].type == ECORE_DESKTOP_TREE_ELEMENT_TYPE_TREE)
1649 result = _ecore_desktop_menu_apply_rules(generate_data, (Ecore_Desktop_Tree *) rule->elements[i].element, key, desktop);
1655 int sub_result = FALSE;
1657 rul = (char *)rule->elements[i].element;
1670 if (strcmp(key, &rul[4]) == 0)
1677 /* Try to match a category. */
1678 if ((desktop->Categories) && (ecore_hash_get(desktop->Categories, &rul[4]) != NULL))
1685 case 'A': result = TRUE; if (!sub_result) return FALSE; break;
1686 case 'N': result = TRUE; if (sub_result) return FALSE; break;
1687 default: if (sub_result) return TRUE; break;
1699 flags = " " or "MDO" the first letter of - Marked, Deleted, OnlyUnallocated
1713 include/exclude or all/file/category x
1715 include/exclude and/not all/file/category x
1721 if the rule string matches the desktop id return true
1722 otherwise return false
1725 loop through the apps categories
1726 as soon as one matches the rule string, return true
1727 otherwise return false.
1730 loop through the rules
1731 as soon as one matches, return true
1732 otherwise return false.
1735 process the sub rules, return the result
1738 loop through the rules
1739 as soon as one doesn't match, return false
1740 otherwise return true.
1743 loop through the rules
1744 as soon as one matches, return false
1745 otherwise return true.
1748 /* FDO PARSING STEPS.
1750 An asterisk in the first column marks those bits that are done.
1751 The spec is not numbered, so I have invented a numbering to help match the
1754 10000000 merge menus
1755 *11000000 expand <KDELegacyDir>'s to <LegacyDir>.
1756 *12000000 expand <LegacyDir>'s
1757 *12100000 for each dir (recursive)
1758 *12110000 create recursively nested <MENU < L> <dirname> <> element
1759 *12120000 // <AppDir>dirpath</AppDir>
1760 *12130000 <DirectoryDir>dirpath</DirectoryDir>
1761 *12140000 if exist .directory
1762 *12141000 add <.directory> to name
1764 *12160000 for each *.desktop
1765 12161000 if no categories in bar.desktop
1766 *12161100 <Filename>prefix-bar.desktop</Filename>
1767 12162000 add "Legacy" to categories
1768 *12163000 add any prefix to the desktop ID.
1769 *12164000 add it to the pool
1770 *12170000 </Include>
1771 *13000000 for each <MergeFile> and <MergeDir> element
1772 *13100000 get the root <Menu> elements from that elements file/s.
1773 *13200000 remove the <Name> element from those root <Menu> elements.
1774 *13300000 replace that element with the child elements of those root <Menu> elements.
1775 *13400000? expand the <DefaultMergeDirs> to <MergeDir>'s $XDG_CONFIG_DIRS/menus/foo-merged/ for the menu file foo.menu
1776 FIXME: The above should probably move to before 13000000
1777 *14000000 loop until all <MergeFile> and <MergeDir> elements are done,
1778 *14000000 careful to avoid infinite loops in files that reference each other.
1779 *15000000 for each <Menu> recursively
1780 15100000 consolidate duplicate child <Menu>s.
1781 *15200000 expand <DefaultAppDir>s and <DefaultDirectoryDir>s to <AppDir>s and <DirectoryDir>s.
1782 15300000 consolidate duplicate child <AppDir>s, <DirectoryDir>s, and <Directory>s.
1783 16000000 for each <Menu> recursively (but from the deepest level out)
1784 16100000 for each <Move>
1785 16110000 do the move.
1786 16120000 mark any <Menu> that has been affected.
1787 17000000 for each marked <Menu> recursively
1788 17100000 consolidate duplicate child <Menu>s.
1789 *18000000 for each <Menu> recursively
1790 *18100000 if there are <Deleted> elements that are not ovreridden by a <NotDelete> element
1791 *18110000 remove this <Menu> element and all it's children.
1793 *20000000 generate menus
1794 *21000000 for each <Menu> that is <NotOnlyUnallocated> (which is the default)
1795 *21100000 for each <AppDir>
1796 *21110000 for each .desktop
1797 *21111000 if it exists in the pool, replace it.
1798 *21112000 else add it to the pool.
1799 *21200000 for each parent <Menu>
1800 *21210000 for each .desktop in the pool
1801 *21211000 if it doesn't exist in the child <Menu> pool
1802 *21211100 add it to the pool.
1803 *21300000 for each <Include> and <Exclude>
1804 *21310000 if rule is an <Include>
1805 *21311000 for each .desktop in pool
1806 *21311100 for each rule
1807 *21311110 if rule matches .desktop in pool
1808 *21311111 add .desktop to menu.
1809 *21311112 mark it as allocated
1810 *21320000 if rule is an <Exclude>
1811 *21321000 for each .desktop in menu
1812 *21321100 for each rule
1813 *21321110 if rule matches .desktop in menu
1814 *21321111 remove .desktop from menu.
1815 *21321112 leave it as allocated.
1817 *30000000 generate unallocated menus
1818 *31000000 Same as for generate menus, but only the <OnlyUnallocated> ones.
1819 *32000000 Only the unallocated .desktop entries can be used.
1821 40000000 generate menu layout
1822 This part of the spec is a whole other rabbit hole, and optional. B-)
1826 /* THINGS TAHT CAN BE DUPLICATED
1828 AppDir last dup is used
1829 .desktop last dup is used
1830 DirectoryDir last dup is used
1831 .directory last dup is used
1832 Directory last dup is used, unless it points to a non existant entry, then work your way backwards
1833 LegacyDir last dup is used
1834 MergeDir last dup is used
1835 MergeFile last dup is used
1837 DefaultMergeDirs dirs that are earlier in the path go later in the menu, so they have priority
1838 KDELegacyDirs dirs that are earlier in the path go later in the menu, so they have priority
1840 OnlyUnallocated & NotOnlyUnallocated last one wins, default is NotOnlyUnallocated
1841 Deleted & NotDeleted last one wins
1843 Include & Exclude done in order