/* Function: al_popup_menu */ bool al_popup_menu(ALLEGRO_MENU *popup, ALLEGRO_DISPLAY *display) { bool ret; ASSERT(popup); if (!popup->is_popup_menu || popup->parent) return false; if (!display) display = al_get_current_display(); /* Set the entire menu tree as owned by the display */ _al_walk_over_menu(popup, set_menu_display_r, display); ret = _al_show_popup_menu(display, popup); if (!ret) { _al_walk_over_menu(popup, set_menu_display_r, NULL); } return ret; }
/* [user thread] */ bool _al_destroy_menu(ALLEGRO_MENU *menu) { ARGS *args; if (!menu->extra1) return true; args = create_args(); args->menu = menu; wait_for_args(do_destroy_menu, args); /* TODO: decrease_window_counter(); ... but only if this is a top level menu */ _al_walk_over_menu(menu, clear_menu_extras, NULL); return true; }
/* [user thread] */ bool _al_destroy_menu_item_at(ALLEGRO_MENU_ITEM *item, int i) { if (item->extra1) { ARGS *args = make_menu_item_args(item, i); if (!args) return false; wait_for_args(do_destroy_menu_item_at, args); if (item->popup) { /* if this has a submenu, then deleting this item will have automatically deleted all of its GTK widgets */ _al_walk_over_menu(item->popup, clear_menu_extras, NULL); } } return true; }
/* [user thread] */ bool _al_destroy_menu(ALLEGRO_MENU *menu) { ARGS args; if (!menu->extra1) return true; if (!_al_gtk_init_args(&args, sizeof(args))) return false; args.menu = menu; _al_gtk_wait_for_args(do_destroy_menu, &args); _al_walk_over_menu(menu, clear_menu_extras, NULL); return true; }
/* [user thread] */ bool _al_hide_display_menu(ALLEGRO_DISPLAY *display, ALLEGRO_MENU *menu) { GtkWidget *gtk_window; ARGS args; if (!(gtk_window = _al_gtk_get_window(display))) return false; if (!_al_gtk_init_args(&args, sizeof(args))) return false; args.gtk_window = gtk_window; args.menu = menu; _al_gtk_wait_for_args(do_hide_display_menu, &args); _al_walk_over_menu(menu, clear_menu_extras, NULL); return true; }
/* Recursively walks over the entire menu structure, calling the user proc once per menu item, * and once per menu. The deepest menu is called first. If the proc returns true, then the * process terminates. It is not safe for the proc to modify the structure (add/remove items). */ bool _al_walk_over_menu(ALLEGRO_MENU *menu, bool (*proc)(ALLEGRO_MENU *menu, ALLEGRO_MENU_ITEM *item, int index, void *extra), void *extra) { ALLEGRO_MENU_ITEM **slot; size_t i; ASSERT(menu); ASSERT(proc); for (i = 0; i < _al_vector_size(&menu->items); ++i) { slot = _al_vector_ref(&menu->items, i); if ((*slot)->popup && _al_walk_over_menu((*slot)->popup, proc, extra)) return true; if (proc(menu, *slot, i, extra)) return true; } return proc(menu, NULL, -1, extra); }
/* [user thread] */ bool _al_hide_display_menu(ALLEGRO_DISPLAY *display, ALLEGRO_MENU *menu) { #ifdef CREATE_WINDOW_FOR_MENU ARGS *args; if (!(args = create_args())) return false; args->display = display; args->menu = menu; wait_for_args(do_hide_display_menu, args); _al_walk_over_menu(menu, clear_menu_extras, NULL); return true; #else (void) display; (void) menu; return false; #endif }
/* Function: al_find_menu_item */ bool al_find_menu_item(ALLEGRO_MENU *haystack, uint16_t id, ALLEGRO_MENU **menu, int *index) { ALLEGRO_MENU_ITEM item; ASSERT(haystack); /* Abuse the ALLEGRO_MENU_ITEM struct as a container for the _al_walk_over_menu callback. * If found, it will return true, and the "parent" field will be the menu that and * the "id" will be the index. */ item.id = id; if (!_al_walk_over_menu(haystack, find_menu_item_r, &item)) return false; if (menu) *menu = item.parent; if (index) *index = item.id; return true; }
/* Function: al_set_display_menu */ bool al_set_display_menu(ALLEGRO_DISPLAY *display, ALLEGRO_MENU *menu) { DISPLAY_MENU *dm = NULL; size_t i; int menu_height = _al_get_menu_display_height(); bool automatic_menu_display_resize = true; const char* automatic_menu_display_resize_value = al_get_config_value(al_get_system_config(), "compatibility", "automatic_menu_display_resize"); if (automatic_menu_display_resize_value && strcmp(automatic_menu_display_resize_value, "false") == 0) automatic_menu_display_resize = false; ASSERT(display); /* Check if this display has a menu associated with it */ for (i = 0; i < _al_vector_size(&display_menus); ++i) { dm = (DISPLAY_MENU *) _al_vector_ref(&display_menus, i); if (dm->display == display) break; } /* If no display was found, reset dm to NULL */ if (i == _al_vector_size(&display_menus)) dm = NULL; if (!menu) { /* Removing the menu */ if (!dm) return false; _al_hide_display_menu(display, dm->menu); _al_walk_over_menu(dm->menu, set_menu_display_r, NULL); _al_vector_delete_at(&display_menus, i); if (automatic_menu_display_resize && menu_height > 0) { display->extra_resize_height = 0; al_resize_display(display, al_get_display_width(display), al_get_display_height(display)); } } else { /* Setting the menu. It must not currently be attached to any * display, and it cannot have a parent menu. */ if (menu->display || menu->parent) return false; if (dm) { /* hide the existing menu */ _al_hide_display_menu(display, dm->menu); _al_walk_over_menu(dm->menu, set_menu_display_r, NULL); } if (!_al_show_display_menu(display, menu)) { /* Unable to set the new menu, but already have hidden the * previous one, so delete the display_menus slot. */ if (dm) _al_vector_delete_at(&display_menus, i); return false; } /* Set the entire menu tree as owned by the display */ _al_walk_over_menu(menu, set_menu_display_r, display); if (!dm) dm = _al_vector_alloc_back(&display_menus); if (automatic_menu_display_resize && menu_height > 0) { /* Temporarily disable the constraints so we don't send a RESIZE_EVENT. */ bool old_constraints = display->use_constraints; display->use_constraints = false; display->extra_resize_height = menu_height; al_resize_display(display, al_get_display_width(display), al_get_display_height(display)); display->use_constraints = old_constraints; } dm->display = display; dm->menu = menu; } return true; }
/* Function: al_insert_menu_item */ int al_insert_menu_item(ALLEGRO_MENU *parent, int pos, char const *title, uint16_t id, int flags, ALLEGRO_BITMAP *icon, ALLEGRO_MENU *submenu) { ALLEGRO_MENU_ITEM *item; ALLEGRO_MENU_ITEM **slot; _AL_MENU_ID *menu_id; size_t i; ASSERT(parent); /* If not found, then treat as an append. */ if (!interpret_menu_id_param(&parent, &pos)) pos = _al_vector_size(&parent->items); /* At this point pos == the _index_ of where to insert */ /* The sub-menu must not already be in use. */ if (submenu && (submenu->display || submenu->parent || submenu->is_popup_menu)) return -1; item = create_menu_item(title, id, flags, submenu); if (!item) return -1; item->parent = parent; set_item_icon(item, icon); i = (size_t) pos; if (i >= _al_vector_size(&parent->items)) { /* Append */ i = _al_vector_size(&parent->items); slot = _al_vector_alloc_back(&parent->items); } else { /* Insert */ slot = _al_vector_alloc_mid(&parent->items, i); } if (!slot) { destroy_menu_item(item); return -1; } *slot = item; if (submenu) { submenu->parent = item; if (parent->display) _al_walk_over_menu(submenu, set_menu_display_r, parent->display); } _al_insert_menu_item_at(item, (int) i); if (id) { /* Append the menu's ID to the search vector */ menu_id = (_AL_MENU_ID *) _al_vector_alloc_back(&menu_ids); menu_id->unique_id = item->unique_id; menu_id->id = id; menu_id->menu = parent; } return (int) i; }