/* * This file is part of NetSurf, http://netsurf.sourceforge.net/ * Licensed under the GNU General Public License, * http://www.opensource.org/licenses/gpl-license * Copyright 2003 Phil Mellor * Copyright 2005 James Bursa * Copyright 2003 John M Bell * Copyright 2005 Richard Wilson */ /** \file * Menu creation and handling (implementation). */ #include #include #include #include "oslib/os.h" #include "oslib/osbyte.h" #include "oslib/osgbpb.h" #include "oslib/territory.h" #include "oslib/wimp.h" #include "netsurf/desktop/gui.h" #include "netsurf/render/box.h" #include "netsurf/riscos/dialog.h" #include "netsurf/render/form.h" #include "netsurf/riscos/configure.h" #include "netsurf/riscos/gui.h" #include "netsurf/riscos/global_history.h" #include "netsurf/riscos/help.h" #include "netsurf/riscos/menus.h" #include "netsurf/riscos/options.h" #include "netsurf/riscos/save.h" #include "netsurf/riscos/tinct.h" #include "netsurf/riscos/theme.h" #include "netsurf/riscos/treeview.h" #include "netsurf/riscos/wimp.h" #include "netsurf/riscos/wimp_event.h" #include "netsurf/utils/log.h" #include "netsurf/utils/messages.h" #include "netsurf/utils/url.h" #include "netsurf/utils/utils.h" #include "netsurf/utils/utf8.h" struct ns_menu_entry { const char *text; /**< menu text (from messages) */ menu_action action; /**< associated action */ wimp_w sub_window; /**< sub-window if any */ }; struct ns_menu { const char *title; struct ns_menu_entry entries[1]; }; #define NS_MENU(N) \ struct { \ const char *title; \ struct ns_menu_entry entries[N]; \ } struct menu_definition_entry { menu_action action; /**< menu action */ wimp_menu_entry *menu_entry; /**< corresponding menu entry */ const char *entry_key; /**< Messages key for entry text */ struct menu_definition_entry *next; /**< next menu entry */ }; struct menu_definition { wimp_menu *menu; /**< corresponding menu */ const char *title_key; /**< Messages key for title text */ int current_encoding; /**< Identifier for current text encoding of menu text (as per OS_Byte,71,127) */ struct menu_definition_entry *entries; /**< menu entries */ struct menu_definition *next; /**< next menu */ }; static wimp_menu *ro_gui_menu_define_menu(struct ns_menu *menu); static void ro_gui_menu_define_menu_add(struct menu_definition *definition, struct ns_menu *menu, int depth, wimp_menu_entry *parent_entry, int first, int last, const char *prefix, int prefix_length); static struct menu_definition *ro_gui_menu_find_menu(wimp_menu *menu); static struct menu_definition_entry *ro_gui_menu_find_entry(wimp_menu *menu, menu_action action); static menu_action ro_gui_menu_find_action(wimp_menu *menu, wimp_menu_entry *menu_entry); static void ro_gui_menu_set_entry_shaded(wimp_menu *menu, menu_action action, bool shaded); static void ro_gui_menu_set_entry_ticked(wimp_menu *menu, menu_action action, bool ticked); static void ro_gui_menu_get_window_details(wimp_w w, struct gui_window **g, struct browser_window **bw, struct content **content, struct toolbar **toolbar, struct tree **tree); static int ro_gui_menu_get_checksum(void); static bool ro_gui_menu_prepare_url_suggest(void); static void ro_gui_menu_prepare_pageinfo(struct gui_window *g); static void ro_gui_menu_prepare_objectinfo(struct box *box); static void ro_gui_menu_refresh_toolbar(struct toolbar *toolbar); static bool ro_gui_menu_translate(struct menu_definition *menu); /* default menu item flags */ #define DEFAULT_FLAGS (wimp_ICON_TEXT | wimp_ICON_FILLED | \ (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | \ (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT)) /** Whether the search box was opened as a sub-menu */ static bool ro_gui_menu_search_window_menu; /** The currently defined menus to perform actions for */ static struct menu_definition *ro_gui_menu_definitions; /** The current menu being worked with (may not be open) */ wimp_menu *current_menu; /** Whether a menu is currently open */ bool current_menu_open = false; /** Box for object under menu, or 0 if no object. */ static struct box *current_menu_object_box = 0; /** Menu of options for form select controls. */ static wimp_menu *gui_form_select_menu = 0; /** Form control which gui_form_select_menu is for. */ static struct form_control *gui_form_select_control; /** Window that owns the current menu */ wimp_w current_menu_window; /** Icon that owns the current menu (only valid for popup menus) */ static wimp_i current_menu_icon; /** The height of the iconbar menu */ int iconbar_menu_height = 5 * 44; /** The available menus */ wimp_menu *iconbar_menu, *browser_menu, *hotlist_menu, *global_history_menu, *image_quality_menu, *browser_toolbar_menu, *tree_toolbar_menu, *proxy_auth_menu, *languages_menu; /** URL suggestion menu */ static wimp_MENU(GLOBAL_HISTORY_RECENT_URLS) url_suggest; wimp_menu *url_suggest_menu = (wimp_menu *)&url_suggest; /* the values given in PRM 3-157 for how to check menus/windows are * incorrect so we use a hack of checking if the sub-menu has bit 0 * set which is undocumented but true of window handles on * all target OS versions */ #define IS_MENU(menu) !((int)(menu) & 1) /** * Create menu structures. */ void ro_gui_menu_init(void) { /* iconbar menu */ NS_MENU(9) iconbar_definition = { "NetSurf", { { "Info", NO_ACTION, dialog_info }, { "AppHelp", HELP_OPEN_CONTENTS, 0 }, { "Open", NO_ACTION, 0 }, { "Open.OpenURL", BROWSER_NAVIGATE_URL, dialog_openurl }, { "Open.HotlistShow", HOTLIST_SHOW, 0 }, { "Open.HistGlobal", HISTORY_SHOW_GLOBAL, 0 }, { "Choices", CHOICES_SHOW, 0 }, { "Quit", APPLICATION_QUIT, 0 }, {NULL, 0, 0} } }; iconbar_menu = ro_gui_menu_define_menu( (struct ns_menu *)&iconbar_definition); /* browser menu */ NS_MENU(66) browser_definition = { "NetSurf", { { "Page", BROWSER_PAGE, 0 }, { "Page.PageInfo",BROWSER_PAGE_INFO, dialog_pageinfo }, { "Page.Save", BROWSER_SAVE, dialog_saveas }, { "Page.SaveComp", BROWSER_SAVE_COMPLETE, dialog_saveas }, { "Page.Export", NO_ACTION, 0 }, { "Page.Export.Draw", BROWSER_EXPORT_DRAW, dialog_saveas }, { "Page.Export.Text", BROWSER_EXPORT_TEXT, dialog_saveas }, { "Page.SaveURL", NO_ACTION, 0 }, { "Page.SaveURL.URI", BROWSER_SAVE_URL_URI, dialog_saveas }, { "Page.SaveURL.URL", BROWSER_SAVE_URL_URL, dialog_saveas }, { "Page.SaveURL.LinkText", BROWSER_SAVE_URL_TEXT, dialog_saveas }, { "_Page.Print", BROWSER_PRINT, dialog_print }, { "Page.NewWindow", BROWSER_NEW_WINDOW, 0 }, { "Page.ViewSrc", BROWSER_VIEW_SOURCE, 0 }, { "Object", BROWSER_OBJECT, 0 }, { "Object.ObjInfo", BROWSER_OBJECT_INFO, dialog_objinfo }, { "Object.ObjSave", BROWSER_OBJECT_SAVE, dialog_saveas }, { "Object.Export", NO_ACTION, 0 }, { "Object.Export.Sprite", BROWSER_OBJECT_EXPORT_SPRITE, dialog_saveas }, { "_Object.SaveURL", NO_ACTION, 0 }, { "Object.SaveURL.URI", BROWSER_OBJECT_SAVE_URL_URI, dialog_saveas }, { "Object.SaveURL.URL", BROWSER_OBJECT_SAVE_URL_URL, dialog_saveas }, { "Object.SaveURL.LinkText", BROWSER_OBJECT_SAVE_URL_TEXT, dialog_saveas }, { "Object.ObjReload", BROWSER_OBJECT_RELOAD, 0 }, { "Navigate", NO_ACTION, 0 }, { "Navigate.Home", BROWSER_NAVIGATE_HOME, 0 }, { "Navigate.Back", BROWSER_NAVIGATE_BACK, 0 }, { "_Navigate.Forward", BROWSER_NAVIGATE_FORWARD, 0 }, { "Navigate.Reload", BROWSER_NAVIGATE_RELOAD_ALL, 0 }, { "Navigate.Stop", BROWSER_NAVIGATE_STOP, 0 }, { "View", NO_ACTION, 0 }, { "View.ScaleView", BROWSER_SCALE_VIEW, dialog_zoom }, { "View.Images", NO_ACTION, 0 }, { "View.Images.ForeImg", BROWSER_IMAGES_FOREGROUND, 0 }, { "View.Images.BackImg", BROWSER_IMAGES_BACKGROUND, 0 }, { "View.Toolbars", NO_ACTION, 0 }, { "View.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, { "View.Toolbars.ToolAddress", TOOLBAR_ADDRESS_BAR, 0 }, { "View.Toolbars.ToolThrob", TOOLBAR_THROBBER, 0 }, { "_View.Toolbars.ToolStatus", TOOLBAR_STATUS_BAR, 0 }, { "View.Toolbars.EditToolbar", TOOLBAR_EDIT, 0 }, { "_View.Render", NO_ACTION, 0 }, { "View.Render.RenderAnims", BROWSER_BUFFER_ANIMS, 0 }, { "View.Render.RenderAll", BROWSER_BUFFER_ALL, 0 }, { "View.OptDefault", BROWSER_SAVE_VIEW, 0 }, { "Utilities", NO_ACTION, 0 }, { "Utilities.Hotlist", HOTLIST_SHOW, 0 }, { "Utilities.Hotlist.HotlistAdd", HOTLIST_ADD_URL, 0 }, { "Utilities.Hotlist.HotlistShow", HOTLIST_SHOW, 0 }, { "Utilities.History", HISTORY_SHOW_GLOBAL, 0 }, { "Utilities.History.HistLocal", HISTORY_SHOW_LOCAL, 0 }, { "Utilities.History.HistGlobal", HISTORY_SHOW_GLOBAL, 0 }, { "Utilities.FindText", BROWSER_FIND_TEXT, dialog_search }, { "Utilities.Window", NO_ACTION, 0 }, { "Utilities.Window.WindowSave", BROWSER_WINDOW_DEFAULT, 0 }, { "Utilities.Window.WindowStagr", BROWSER_WINDOW_STAGGER, 0 }, { "_Utilities.Window.WindowSize", BROWSER_WINDOW_COPY, 0 }, { "Utilities.Window.WindowReset", BROWSER_WINDOW_RESET, 0 }, { "Help", HELP_OPEN_CONTENTS, 0 }, { "Help.HelpContent", HELP_OPEN_CONTENTS, 0 }, { "Help.HelpGuide", HELP_OPEN_GUIDE, 0 }, { "_Help.HelpInfo", HELP_OPEN_INFORMATION, 0 }, { "_Help.HelpAbout", HELP_OPEN_ABOUT, 0 }, { "Help.HelpInter", HELP_LAUNCH_INTERACTIVE, 0 }, {NULL, 0, 0} } }; browser_menu = ro_gui_menu_define_menu( (struct ns_menu *)&browser_definition); /* hotlist menu */ NS_MENU(24) hotlist_definition = { "Hotlist", { { "Hotlist", NO_ACTION, 0 }, { "Hotlist.New", NO_ACTION, 0 }, { "Hotlist.New.Folder", TREE_NEW_FOLDER, dialog_folder }, { "Hotlist.New.Link", TREE_NEW_LINK, dialog_entry }, { "_Hotlist.Export", HOTLIST_EXPORT, dialog_saveas }, { "Hotlist.Expand", TREE_EXPAND_ALL, 0 }, { "Hotlist.Expand.All", TREE_EXPAND_ALL, 0 }, { "Hotlist.Expand.Folders", TREE_EXPAND_FOLDERS, 0 }, { "Hotlist.Expand.Links", TREE_EXPAND_LINKS, 0 }, { "Hotlist.Collapse", TREE_COLLAPSE_ALL, 0 }, { "Hotlist.Collapse.All", TREE_COLLAPSE_ALL, 0 }, { "Hotlist.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 }, { "Hotlist.Collapse.Links", TREE_COLLAPSE_LINKS, 0 }, { "Hotlist.Toolbars", NO_ACTION, 0 }, { "_Hotlist.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, { "Hotlist.Toolbars.EditToolbar", TOOLBAR_EDIT, 0 }, { "Selection", TREE_SELECTION, 0 }, { "Selection.Edit", TREE_SELECTION_EDIT, (wimp_w)1 }, { "Selection.Launch", TREE_SELECTION_LAUNCH, 0 }, { "Selection.Delete", TREE_SELECTION_DELETE, 0 }, { "SelectAll", TREE_SELECT_ALL, 0 }, { "Clear", TREE_CLEAR_SELECTION, 0 }, {NULL, 0, 0} } }; hotlist_menu = ro_gui_menu_define_menu( (struct ns_menu *)&hotlist_definition); /* history menu */ NS_MENU(19) global_history_definition = { "History", { { "History", NO_ACTION, 0 }, { "_History.Export", HISTORY_EXPORT, dialog_saveas }, { "History.Expand", TREE_EXPAND_ALL, 0 }, { "History.Expand.All", TREE_EXPAND_ALL, 0 }, { "History.Expand.Folders", TREE_EXPAND_FOLDERS, 0 }, { "History.Expand.Links", TREE_EXPAND_LINKS, 0 }, { "History.Collapse", TREE_COLLAPSE_ALL, 0 }, { "History.Collapse.All", TREE_COLLAPSE_ALL, 0 }, { "History.Collapse.Folders", TREE_COLLAPSE_FOLDERS, 0 }, { "History.Collapse.Links", TREE_COLLAPSE_LINKS, 0 }, { "History.Toolbars", NO_ACTION, 0 }, { "_History.Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, { "History.Toolbars.EditToolbar",TOOLBAR_EDIT, 0 }, { "Selection", TREE_SELECTION, 0 }, { "Selection.Launch", TREE_SELECTION_LAUNCH, 0 }, { "Selection.Delete", TREE_SELECTION_DELETE, 0 }, { "SelectAll", TREE_SELECT_ALL, 0 }, { "Clear", TREE_CLEAR_SELECTION, 0 }, {NULL, 0, 0} } }; global_history_menu = ro_gui_menu_define_menu( (struct ns_menu *)&global_history_definition); /* image quality menu */ NS_MENU(5) images_definition = { "Display", { { "ImgStyle0", NO_ACTION, 0 }, { "ImgStyle1", NO_ACTION, 0 }, { "ImgStyle2", NO_ACTION, 0 }, { "ImgStyle3", NO_ACTION, 0 }, {NULL, 0, 0} } }; image_quality_menu = ro_gui_menu_define_menu( (struct ns_menu *)&images_definition); /* browser toolbar menu */ NS_MENU(7) browser_toolbar_definition = { "Toolbar", { { "Toolbars", NO_ACTION, 0 }, { "Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, { "Toolbars.ToolAddress", TOOLBAR_ADDRESS_BAR, 0 }, { "Toolbars.ToolThrob", TOOLBAR_THROBBER, 0 }, { "Toolbars.ToolStatus", TOOLBAR_STATUS_BAR, 0 }, { "EditToolbar", TOOLBAR_EDIT, 0 }, {NULL, 0, 0} } }; browser_toolbar_menu = ro_gui_menu_define_menu( (struct ns_menu *)&browser_toolbar_definition); /* tree toolbar menu */ NS_MENU(4) tree_toolbar_definition = { "Toolbar", { { "Toolbars", NO_ACTION, 0 }, { "Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, { "EditToolbar", TOOLBAR_EDIT, 0 }, {NULL, 0, 0} } }; tree_toolbar_menu = ro_gui_menu_define_menu( (struct ns_menu *)&tree_toolbar_definition); /* proxy menu */ NS_MENU(4) proxy_auth_definition = { "ProxyAuth", { { "ProxyNone", NO_ACTION, 0 }, { "ProxyBasic", NO_ACTION, 0 }, { "ProxyNTLM", NO_ACTION, 0 }, {NULL, 0, 0} } }; proxy_auth_menu = ro_gui_menu_define_menu( (struct ns_menu *)&proxy_auth_definition); /* special case menus */ url_suggest_menu->title_data.indirected_text.text = (char*)messages_get("URLSuggest"); ro_gui_menu_init_structure(url_suggest_menu, GLOBAL_HISTORY_RECENT_URLS); /* Note: This table *must* be kept in sync with the LangNames file */ NS_MENU(48) lang_definition = { "Languages", { { "lang_af", NO_ACTION, 0 }, { "lang_bm", NO_ACTION, 0 }, { "lang_ca", NO_ACTION, 0 }, { "lang_cs", NO_ACTION, 0 }, { "lang_cy", NO_ACTION, 0 }, { "lang_da", NO_ACTION, 0 }, { "lang_de", NO_ACTION, 0 }, { "lang_en", NO_ACTION, 0 }, { "lang_es", NO_ACTION, 0 }, { "lang_et", NO_ACTION, 0 }, { "lang_eu", NO_ACTION, 0 }, { "lang_ff", NO_ACTION, 0 }, { "lang_fi", NO_ACTION, 0 }, { "lang_fr", NO_ACTION, 0 }, { "lang_ga", NO_ACTION, 0 }, { "lang_gl", NO_ACTION, 0 }, { "lang_ha", NO_ACTION, 0 }, { "lang_hr", NO_ACTION, 0 }, { "lang_hu", NO_ACTION, 0 }, { "lang_id", NO_ACTION, 0 }, { "lang_is", NO_ACTION, 0 }, { "lang_it", NO_ACTION, 0 }, { "lang_lt", NO_ACTION, 0 }, { "lang_lv", NO_ACTION, 0 }, { "lang_ms", NO_ACTION, 0 }, { "lang_mt", NO_ACTION, 0 }, { "lang_nl", NO_ACTION, 0 }, { "lang_no", NO_ACTION, 0 }, { "lang_pl", NO_ACTION, 0 }, { "lang_pt", NO_ACTION, 0 }, { "lang_rn", NO_ACTION, 0 }, { "lang_ro", NO_ACTION, 0 }, { "lang_rw", NO_ACTION, 0 }, { "lang_sk", NO_ACTION, 0 }, { "lang_sl", NO_ACTION, 0 }, { "lang_so", NO_ACTION, 0 }, { "lang_sq", NO_ACTION, 0 }, { "lang_sr", NO_ACTION, 0 }, { "lang_sv", NO_ACTION, 0 }, { "lang_sw", NO_ACTION, 0 }, { "lang_tr", NO_ACTION, 0 }, { "lang_uz", NO_ACTION, 0 }, { "lang_vi", NO_ACTION, 0 }, { "lang_wo", NO_ACTION, 0 }, { "lang_xs", NO_ACTION, 0 }, { "lang_yo", NO_ACTION, 0 }, { "lang_zu", NO_ACTION, 0 }, { NULL, 0, 0 } } }; languages_menu = ro_gui_menu_define_menu( (struct ns_menu *)&lang_definition); } /** * Display a menu. */ void ro_gui_menu_create(wimp_menu *menu, int x, int y, wimp_w w) { int doc_x, doc_y; wimp_window_state state; struct gui_window *g; os_error *error; int i; menu_action action; struct menu_definition *definition; /* translate menu, if necessary (this returns quickly * if there's nothing to be done) */ definition = ro_gui_menu_find_menu(menu); if (definition) { if (!ro_gui_menu_translate(definition)) { warn_user("NoMemory", 0); return; } } /* read the object under the pointer for a new gui_window menu */ if ((!current_menu) && (menu == browser_menu)) { state.w = w; error = xwimp_get_window_state(&state); if (error) { LOG(("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return; } g = ro_gui_window_lookup(w); assert(g); doc_x = window_x_units(x, &state) / 2 / g->option.scale; doc_y = -window_y_units(y, &state) / 2 / g->option.scale; current_menu_object_box = NULL; if (g->bw->current_content && g->bw->current_content->type == CONTENT_HTML) current_menu_object_box = box_object_at_point( g->bw->current_content, doc_x, doc_y); } /* store the menu characteristics */ current_menu = menu; current_menu_window = w; current_menu_icon = -1; ro_gui_menu_search_window_menu = false; /* prepare the menu state */ if (menu == url_suggest_menu) { if (!ro_gui_menu_prepare_url_suggest()) return; } else if (menu == recent_search_menu) { if (!ro_gui_search_prepare_menu()) return; } else { i = 0; do { action = ro_gui_menu_find_action(menu, &menu->entries[i]); if (action != NO_ACTION) ro_gui_menu_prepare_action(w, action, false); } while (!(menu->entries[i++].menu_flags & wimp_MENU_LAST)); } /* create the menu */ current_menu_open = true; error = xwimp_create_menu(menu, x - 64, y); if (error) { LOG(("xwimp_create_menu: 0x%x: %s", error->errnum, error->errmess)); warn_user("MenuError", error->errmess); ro_gui_menu_closed(true); } } /** * Display a pop-up menu next to the specified icon. * * \param menu menu to open * \param w window handle * \param i icon handle */ void ro_gui_popup_menu(wimp_menu *menu, wimp_w w, wimp_i i) { wimp_window_state state; wimp_icon_state icon_state; os_error *error; state.w = w; icon_state.w = w; icon_state.i = i; error = xwimp_get_window_state(&state); if (error) { LOG(("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess)); warn_user("MenuError", error->errmess); return; } error = xwimp_get_icon_state(&icon_state); if (error) { LOG(("xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess)); warn_user("MenuError", error->errmess); return; } ro_gui_menu_create(menu, state.visible.x0 + icon_state.icon.extent.x1 + 64, state.visible.y1 + icon_state.icon.extent.y1 - state.yscroll, w); current_menu_icon = i; } /** * Clean up after a menu has been closed, or forcible close an open menu. * * \param cleanup Call any terminating functions (sub-window isn't going to be instantly re-opened) */ void ro_gui_menu_closed(bool cleanup) { struct gui_window *g; struct browser_window *bw; struct content *c; struct toolbar *t; struct tree *tree; os_error *error; if (current_menu) { error = xwimp_create_menu(wimp_CLOSE_MENU, 0, 0); if (error) { LOG(("xwimp_create_menu: 0x%x: %s", error->errnum, error->errmess)); warn_user("MenuError", error->errmess); } ro_gui_menu_get_window_details(current_menu_window, &g, &bw, &c, &t, &tree); current_menu = NULL; if (cleanup) { /* end any search operation that was started so that the text doesn't remain highlighted */ if (ro_gui_menu_search_window_menu) ro_gui_search_end(dialog_search); if (tree) ro_gui_tree_menu_closed(tree); } } current_menu_window = NULL; current_menu_icon = NULL; current_menu_open = false; gui_form_select_control = NULL; } /** * The content has changed, reset object references */ void ro_gui_menu_objects_moved(void) { gui_form_select_control = NULL; current_menu_object_box = NULL; ro_gui_menu_prepare_action(0, BROWSER_OBJECT, false); if ((current_menu) && (current_menu == gui_form_select_menu)) ro_gui_menu_closed(true); } /** * Handle menu selection. */ void ro_gui_menu_selection(wimp_selection *selection) { int i, j; wimp_menu_entry *menu_entry; menu_action action; wimp_pointer pointer; struct gui_window *g = NULL; wimp_menu *menu; os_error *error; int previous_menu_icon = current_menu_icon; char *url; /* if we are using gui_multitask then menu selection events * may be delivered after the menu has been closed. As such, * we simply ignore these events. */ if (!current_menu) return assert(current_menu_window); /* get the menu entry and associated action */ menu_entry = ¤t_menu->entries[selection->items[0]]; for (i = 1; selection->items[i] != -1; i++) menu_entry = &menu_entry->sub_menu-> entries[selection->items[i]]; action = ro_gui_menu_find_action(current_menu, menu_entry); /* perform menu action */ if (action != NO_ACTION) ro_gui_menu_handle_action(current_menu_window, action, false); /* perform non-automated actions */ if (current_menu == url_suggest_menu) { g = ro_gui_toolbar_lookup(current_menu_window); if (g) { url = url_suggest_menu->entries[selection->items[0]].data.indirected_text.text; gui_window_set_url(g, url); browser_window_go(g->bw, url, 0); global_history_add_recent(url); } } else if ((current_menu == gui_form_select_menu) && (selection->items[0] >= 0)) { g = ro_gui_window_lookup(current_menu_window); assert(g); browser_window_form_select(g->bw, gui_form_select_control, selection->items[0]); } /* allow automatic menus to have their data updated */ ro_gui_wimp_event_menu_selection(current_menu_window, current_menu_icon, current_menu, selection); /* re-open the menu for Adjust clicks */ error = xwimp_get_pointer_info(&pointer); if (error) { LOG(("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); ro_gui_menu_closed(true); return; } if (pointer.buttons != wimp_CLICK_ADJUST) { ro_gui_menu_closed(true); return; } /* re-prepare all the visible enties */ i = 0; menu = current_menu; do { j = 0; do { action = ro_gui_menu_find_action(current_menu, &menu->entries[j]); if (action != NO_ACTION) ro_gui_menu_prepare_action(current_menu_window, action, false); } while (!(menu->entries[j++].menu_flags & wimp_MENU_LAST)); j = selection->items[i++]; if (j != -1) { menu = menu->entries[j].sub_menu; if ((!menu) || (menu == wimp_NO_SUB_MENU)) break; } } while (j != -1); if (current_menu == gui_form_select_menu) gui_create_form_select_menu(g->bw, gui_form_select_control); else ro_gui_menu_create(current_menu, 0, 0, current_menu_window); current_menu_icon = previous_menu_icon; } /** * Handle Message_MenuWarning. */ void ro_gui_menu_warning(wimp_message_menu_warning *warning) { int i; menu_action action; wimp_menu_entry *menu_entry; wimp_menu *sub_menu; os_error *error; assert(current_menu); assert(current_menu_window); /* get the sub-menu of the warning */ if (warning->selection.items[0] == -1) return; menu_entry = ¤t_menu->entries[warning->selection.items[0]]; for (i = 1; warning->selection.items[i] != -1; i++) menu_entry = &menu_entry->sub_menu-> entries[warning->selection.items[i]]; if (IS_MENU(menu_entry->sub_menu)) { sub_menu = menu_entry->sub_menu; i = 0; do { action = ro_gui_menu_find_action(current_menu, &sub_menu->entries[i]); if (action != NO_ACTION) ro_gui_menu_prepare_action(current_menu_window, action, false); } while (!(sub_menu->entries[i++].menu_flags & wimp_MENU_LAST)); } else { action = ro_gui_menu_find_action(current_menu, menu_entry); if (action != NO_ACTION) ro_gui_menu_prepare_action(current_menu_window, action, true); /* remove the close icon */ ro_gui_wimp_update_window_furniture((wimp_w)menu_entry->sub_menu, wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_BACK_ICON, 0); } /* open the sub-menu */ error = xwimp_create_sub_menu(menu_entry->sub_menu, warning->pos.x, warning->pos.y); if (error) { LOG(("xwimp_create_sub_menu: 0x%x: %s", error->errnum, error->errmess)); warn_user("MenuError", error->errmess); } } /** * Refresh a toolbar after it has been updated * * \param toolbar the toolbar to update */ void ro_gui_menu_refresh_toolbar(struct toolbar *toolbar) { int height; assert(toolbar); toolbar->reformat_buttons = true; height = toolbar->height; ro_gui_theme_process_toolbar(toolbar, -1); if (toolbar->type == THEME_BROWSER_TOOLBAR) { ro_gui_window_update_dimensions( ro_gui_window_lookup(current_menu_window), height - toolbar->height); } else if (toolbar->type == THEME_HOTLIST_TOOLBAR) { tree_resized(hotlist_tree); xwimp_force_redraw((wimp_w)hotlist_tree->handle, 0,-16384, 16384, 16384); } else if (toolbar->type == THEME_HISTORY_TOOLBAR) { tree_resized(global_history_tree); xwimp_force_redraw((wimp_w)global_history_tree->handle, 0,-16384, 16384, 16384); } } /** * Builds the URL suggestion menu */ bool ro_gui_menu_prepare_url_suggest(void) { char **suggest_text; int suggestions; int i; suggest_text = global_history_get_recent(&suggestions); if (suggestions < 1) return false; for (i = 0; i < suggestions; i++) { url_suggest_menu->entries[i].menu_flags = 0; url_suggest_menu->entries[i].data.indirected_text.text = suggest_text[i]; url_suggest_menu->entries[i].data.indirected_text.size = strlen(suggest_text[i]) + 1; } url_suggest_menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED; url_suggest_menu->entries[suggestions - 1].menu_flags |= wimp_MENU_LAST; return true; } /** * Update navigate menu status and toolbar icons. * * /param gui the gui_window to update */ void ro_gui_prepare_navigate(struct gui_window *gui) { int suggestions; ro_gui_menu_prepare_action(gui->window, HOTLIST_SHOW, false); ro_gui_menu_prepare_action(gui->window, BROWSER_NAVIGATE_STOP, false); ro_gui_menu_prepare_action(gui->window, BROWSER_NAVIGATE_RELOAD_ALL, false); ro_gui_menu_prepare_action(gui->window, BROWSER_NAVIGATE_BACK, false); ro_gui_menu_prepare_action(gui->window, BROWSER_NAVIGATE_FORWARD, false); ro_gui_menu_prepare_action(gui->window, HOTLIST_SHOW, false); ro_gui_menu_prepare_action(gui->window, BROWSER_SAVE, false); ro_gui_menu_prepare_action(gui->window, BROWSER_PRINT, false); ro_gui_menu_prepare_action(gui->window, BROWSER_SCALE_VIEW, false); ro_gui_menu_prepare_action(gui->window, BROWSER_FIND_TEXT, false); if (gui->toolbar) { global_history_get_recent(&suggestions); ro_gui_set_icon_shaded_state(gui->toolbar->toolbar_handle, ICON_TOOLBAR_SUGGEST, (suggestions <= 0)); } } /** * Prepare the page info window for use * * \param g the gui_window to set the display icons for */ void ro_gui_menu_prepare_pageinfo(struct gui_window *g) { struct content *c = g->bw->current_content; char icon_buf[20] = "file_xxx"; char enc_buf[40]; char enc_token[10] = "Encoding0"; const char *icon = icon_buf; const char *title = "-"; const char *url = "-"; const char *enc = "-"; const char *mime = "-"; assert(c); if (c->title) title = c->title; if (c->url) url = c->url; if (c->mime_type) mime = c->mime_type; sprintf(icon_buf, "file_%x", ro_content_filetype(c)); if (!ro_gui_wimp_sprite_exists(icon_buf)) sprintf(icon_buf, "file_xxx"); if (c->type == CONTENT_HTML) { if (c->data.html.encoding) { enc_token[8] = '0' + c->data.html.encoding_source; snprintf(enc_buf, sizeof enc_buf, "%s (%s)", c->data.html.encoding, messages_get(enc_token)); enc = enc_buf; } else { enc = messages_get("EncodingUnk"); } } ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_ICON, icon); ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_TITLE, title); ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_URL, url); ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_ENC, enc); ro_gui_set_icon_string(dialog_pageinfo, ICON_PAGEINFO_TYPE, mime); } /** * Prepare the object info window for use * * \param box the box to set the display icons for */ void ro_gui_menu_prepare_objectinfo(struct box *box) { char icon_buf[20] = "file_xxx"; const char *url = "-"; const char *target = "-"; const char *mime = "-"; sprintf(icon_buf, "file_%.3x", ro_content_filetype(box->object)); if (!ro_gui_wimp_sprite_exists(icon_buf)) sprintf(icon_buf, "file_xxx"); if (box->object->url) url = box->object->url; if (box->href) target = box->href; if (box->object->mime_type) mime = box->object->mime_type; ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_ICON, icon_buf); ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_URL, url); ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_TARGET, target); ro_gui_set_icon_string(dialog_objinfo, ICON_OBJINFO_TYPE, mime); } /** * Display a menu of options for a form select control. * * \param bw browser window containing form control * \param control form control of type GADGET_SELECT */ void gui_create_form_select_menu(struct browser_window *bw, struct form_control *control) { unsigned int i, entries; char *text_convert, *temp; struct form_option *option; wimp_pointer pointer; os_error *error; bool reopen = true; utf8_convert_ret err; assert(control); for (entries = 0, option = control->data.select.items; option; option = option->next) entries++; if (entries == 0) { ro_gui_menu_closed(true); return; } if ((gui_form_select_menu) && (control != gui_form_select_control)) { for (i = 0; ; i++) { free(gui_form_select_menu->entries[i].data. indirected_text.text); if (gui_form_select_menu->entries[i].menu_flags & wimp_MENU_LAST) break; } free(gui_form_select_menu->title_data.indirected_text.text); free(gui_form_select_menu); gui_form_select_menu = 0; } if (!gui_form_select_menu) { reopen = false; gui_form_select_menu = malloc(wimp_SIZEOF_MENU(entries)); if (!gui_form_select_menu) { warn_user("NoMemory", 0); ro_gui_menu_closed(true); return; } err = utf8_to_local_encoding(messages_get("SelectMenu"), 0, &text_convert); if (err != UTF8_CONVERT_OK) { /* badenc should never happen */ assert(err != UTF8_CONVERT_BADENC); LOG(("utf8_to_local_encoding failed")); warn_user("NoMemory", 0); ro_gui_menu_closed(true); return; } gui_form_select_menu->title_data.indirected_text.text = text_convert; ro_gui_menu_init_structure(gui_form_select_menu, entries); } for (i = 0, option = control->data.select.items; option; i++, option = option->next) { gui_form_select_menu->entries[i].menu_flags = 0; if (option->selected) gui_form_select_menu->entries[i].menu_flags = wimp_MENU_TICKED; if (!reopen) { /* convert spaces to hard spaces to stop things * like 'Go Home' being treated as if 'Home' is a * keyboard shortcut and right aligned in the menu. */ temp = cnv_space2nbsp(option->text); if (!temp) { LOG(("cnv_space2nbsp failed")); warn_user("NoMemory", 0); ro_gui_menu_closed(true); return; } err = utf8_to_local_encoding(temp, 0, &text_convert); if (err != UTF8_CONVERT_OK) { /* A bad encoding should never happen, * so assert this */ assert(err != UTF8_CONVERT_BADENC); LOG(("utf8_to_enc failed")); warn_user("NoMemory", 0); ro_gui_menu_closed(true); return; } free(temp); gui_form_select_menu->entries[i].data.indirected_text.text = text_convert; gui_form_select_menu->entries[i].data.indirected_text.size = strlen(gui_form_select_menu->entries[i]. data.indirected_text.text) + 1; } } gui_form_select_menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED; gui_form_select_menu->entries[i - 1].menu_flags |= wimp_MENU_LAST; error = xwimp_get_pointer_info(&pointer); if (error) { LOG(("xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); ro_gui_menu_closed(true); return; } gui_form_select_control = control; ro_gui_menu_create(gui_form_select_menu, pointer.pos.x, pointer.pos.y, bw->window->window); } /** * Creates a wimp_menu and adds it to the list to handle actions for. * * \param menu the data to create the menu with * \return the menu created, or NULL on failure */ wimp_menu *ro_gui_menu_define_menu(struct ns_menu *menu) { struct menu_definition *definition; int entry; definition = calloc(sizeof(struct menu_definition), 1); if (!definition) die("No memory to create menu definition."); /* link in the menu to our list */ definition->next = ro_gui_menu_definitions; ro_gui_menu_definitions = definition; /* count number of menu entries */ for (entry = 0; menu->entries[entry].text; entry++) /* do nothing */; /* create our definitions */ ro_gui_menu_define_menu_add(definition, menu, 0, NULL, 0, entry, NULL, 0); /* and translate menu into current encoding */ if (!ro_gui_menu_translate(definition)) die("No memory to translate menu."); return definition->menu; } /** * Create a wimp menu tree from ns_menu data. * This function does *not* deal with the menu textual content - it simply * creates and populates the appropriate structures. Textual content is * generated by ro_gui_menu_translate_menu() * * \param definition Top level menu definition * \param menu Menu declaration data * \param depth Depth of menu we're currently building * \param parent_entry Entry in parent menu, or NULL if root menu * \param first First index in declaration data that is used by this menu * \param last Last index in declaration data that is used by this menu * \param prefix Prefix pf menu declaration string already seen * \param prefix_length Length of prefix */ void ro_gui_menu_define_menu_add(struct menu_definition *definition, struct ns_menu *menu, int depth, wimp_menu_entry *parent_entry, int first, int last, const char *prefix, int prefix_length) { int entry, id, cur_depth; int entries = 0; int matches[last - first + 1]; const char *match; const char *text, *menu_text; wimp_menu *new_menu; struct menu_definition_entry *definition_entry; /* step 1: store the matches for depth and subset string */ for (entry = first; entry < last; entry++) { cur_depth = 0; match = menu->entries[entry].text; /* skip specials at start of string */ while (!isalnum(*match)) match++; /* attempt prefix match */ if ((prefix) && (strncmp(match, prefix, prefix_length))) continue; /* Find depth of this entry */ while (*match) if (*match++ == '.') cur_depth++; if (depth == cur_depth) matches[entries++] = entry; } matches[entries] = last; /* no entries, so exit */ if (entries == 0) return; /* step 2: build and link the menu. we must use realloc to stop * our memory fragmenting so we can test for sub-menus easily */ new_menu = (wimp_menu *)malloc(wimp_SIZEOF_MENU(entries)); if (!new_menu) die("No memory to create menu."); if (parent_entry) { /* Fix up sub menu pointer */ parent_entry->sub_menu = new_menu; } else { /* Root menu => fill in definition struct */ definition->title_key = menu->title; definition->current_encoding = 0; definition->menu = new_menu; } /* this is fixed up in ro_gui_menu_translate() */ new_menu->title_data.indirected_text.text = NULL; /* fill in menu flags */ ro_gui_menu_init_structure(new_menu, entries); /* and then create the entries */ for (entry = 0; entry < entries; entry++) { /* add the entry */ id = matches[entry]; text = menu->entries[id].text; /* fill in menu flags from specials at start of string */ new_menu->entries[entry].menu_flags = 0; while (!isalnum(*text)) { if (*text == '_') new_menu->entries[entry].menu_flags |= wimp_MENU_SEPARATE; text++; } /* get messages key for menu entry */ menu_text = strrchr(text, '.'); if (!menu_text) /* no '.' => top-level entry */ menu_text = text; else menu_text++; /* and move past the '.' */ /* fill in submenu data */ if (menu->entries[id].sub_window) new_menu->entries[entry].sub_menu = (wimp_menu *)menu->entries[id].sub_window; /* this is fixed up in ro_gui_menu_translate() */ new_menu->entries[entry].data.indirected_text.text = NULL; /* create definition entry */ definition_entry = malloc(sizeof(struct menu_definition_entry)); if (!definition_entry) die("Unable to create menu definition entry"); definition_entry->action = menu->entries[id].action; definition_entry->menu_entry = &new_menu->entries[entry]; definition_entry->entry_key = menu_text; definition_entry->next = definition->entries; definition->entries = definition_entry; /* recurse */ if (new_menu->entries[entry].sub_menu == wimp_NO_SUB_MENU) { ro_gui_menu_define_menu_add(definition, menu, depth + 1, &new_menu->entries[entry], matches[entry], matches[entry + 1], text, strlen(text)); } /* give menu warnings */ if (new_menu->entries[entry].sub_menu != wimp_NO_SUB_MENU) new_menu->entries[entry].menu_flags |= wimp_MENU_GIVE_WARNING; } new_menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED; new_menu->entries[entries - 1].menu_flags |= wimp_MENU_LAST; } /** * Initialise the basic state of a menu structure so all entries are * indirected text with no flags, no submenu. */ void ro_gui_menu_init_structure(wimp_menu *menu, int entries) { int i; menu->title_fg = wimp_COLOUR_BLACK; menu->title_bg = wimp_COLOUR_LIGHT_GREY; menu->work_fg = wimp_COLOUR_BLACK; menu->work_bg = wimp_COLOUR_WHITE; menu->width = 200; menu->height = wimp_MENU_ITEM_HEIGHT; menu->gap = wimp_MENU_ITEM_GAP; for (i = 0; i < entries; i++) { menu->entries[i].menu_flags = 0; menu->entries[i].sub_menu = wimp_NO_SUB_MENU; menu->entries[i].icon_flags = DEFAULT_FLAGS | wimp_ICON_INDIRECTED; menu->entries[i].data.indirected_text.validation = (char *)-1; } menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED; menu->entries[i - 1].menu_flags |= wimp_MENU_LAST; } /** * Finds the menu_definition corresponding to a wimp_menu. * * \param menu the menu to find the definition for * \return the associated definition, or NULL if one could not be found */ struct menu_definition *ro_gui_menu_find_menu(wimp_menu *menu) { struct menu_definition *definition; if (!menu) return NULL; for (definition = ro_gui_menu_definitions; definition; definition = definition->next) if (definition->menu == menu) return definition; return NULL; } /** * Finds the key associated with a menu entry translation. * * \param menu the menu to search * \param translated the translated text * \return the original message key, or NULL if one could not be found */ const char *ro_gui_menu_find_menu_entry_key(wimp_menu *menu, const char *translated) { struct menu_definition_entry *entry; struct menu_definition *definition = ro_gui_menu_find_menu(menu); if (!definition) return NULL; for (entry = definition->entries; entry; entry = entry->next) if (!strcmp(entry->menu_entry->data.indirected_text.text, translated)) return entry->entry_key; return NULL; } /** * Finds the menu_definition_entry corresponding to an action for a wimp_menu. * * \param menu the menu to search for an action within * \param action the action to find * \return the associated menu entry, or NULL if one could not be found */ struct menu_definition_entry *ro_gui_menu_find_entry(wimp_menu *menu, menu_action action) { struct menu_definition_entry *entry; struct menu_definition *definition = ro_gui_menu_find_menu(menu); if (!definition) return NULL; for (entry = definition->entries; entry; entry = entry->next) if (entry->action == action) return entry; return NULL; } /** * Finds the action corresponding to a wimp_menu_entry for a wimp_menu. * * \param menu the menu to search for an action within * \param menu_entry the menu_entry to find * \return the associated action, or 0 if one could not be found */ menu_action ro_gui_menu_find_action(wimp_menu *menu, wimp_menu_entry *menu_entry) { struct menu_definition_entry *entry; struct menu_definition *definition = ro_gui_menu_find_menu(menu); if (!definition) return NO_ACTION; for (entry = definition->entries; entry; entry = entry->next) { if (entry->menu_entry == menu_entry) return entry->action; } return NO_ACTION; } /** * Sets an action within a menu as having a specific ticked status. * * \param menu the menu containing the action * \param action the action to tick/untick * \param ticked whether to set the item as ticked */ void ro_gui_menu_set_entry_shaded(wimp_menu *menu, menu_action action, bool shaded) { struct menu_definition_entry *entry = ro_gui_menu_find_entry(menu, action); if (entry) { if (shaded) entry->menu_entry->icon_flags |= wimp_ICON_SHADED; else entry->menu_entry->icon_flags &= ~wimp_ICON_SHADED; } } /** * Sets an action within a menu as having a specific ticked status. * * \param menu the menu containing the action * \param action the action to tick/untick * \param ticked whether to set the item as ticked */ void ro_gui_menu_set_entry_ticked(wimp_menu *menu, menu_action action, bool ticked) { struct menu_definition_entry *entry = ro_gui_menu_find_entry(menu, action); if (entry) { if (ticked) entry->menu_entry->menu_flags |= wimp_MENU_TICKED; else entry->menu_entry->menu_flags &= ~wimp_MENU_TICKED; } } /** * Handles an action. * * \param owner the window to handle the action for * \param action the action to handle * \param windows_at_pointer whether to open any windows at the pointer location */ bool ro_gui_menu_handle_action(wimp_w owner, menu_action action, bool windows_at_pointer) { wimp_window_state state; struct gui_window *g = NULL; struct browser_window *bw = NULL; struct content *c = NULL; struct toolbar *t = NULL; struct tree *tree; struct node *node; os_error *error; char url[80]; struct url_content *data; ro_gui_menu_get_window_details(owner, &g, &bw, &c, &t, &tree); switch (action) { /* help actions */ case HELP_OPEN_CONTENTS: ro_gui_open_help_page("docs"); return true; case HELP_OPEN_GUIDE: ro_gui_open_help_page("guide"); return true; case HELP_OPEN_INFORMATION: ro_gui_open_help_page("info"); return true; case HELP_OPEN_ABOUT: browser_window_create( "file://Docs/about", 0, 0); return true; case HELP_LAUNCH_INTERACTIVE: ro_gui_interactive_help_start(); return true; /* history actions */ case HISTORY_SHOW_LOCAL: if ((!bw) || (!bw->history)) return false; ro_gui_history_open(bw, bw->history, windows_at_pointer); return true; case HISTORY_SHOW_GLOBAL: ro_gui_tree_show(global_history_tree); return true; /* hotlist actions */ case HOTLIST_ADD_URL: if ((!hotlist_tree) || (!c) || (!c->url)) return false; data = url_store_find(c->url); if (data) { node = tree_create_URL_node(hotlist_tree->root, data, NULL); if (node) { tree_redraw_area(hotlist_tree, node->box.x - NODE_INSTEP, 0, NODE_INSTEP, 16384); tree_handle_node_changed(hotlist_tree, node, false, true); ro_gui_tree_scroll_visible(hotlist_tree, &node->data); ro_gui_hotlist_save(); } } return true; case HOTLIST_SHOW: ro_gui_tree_show(hotlist_tree); return true; /* page actions */ case BROWSER_PAGE_INFO: if (!c) return false; ro_gui_menu_prepare_action(owner, action, true); ro_gui_dialog_open_persistent(g->window, dialog_pageinfo, windows_at_pointer); return true; case BROWSER_PRINT: if (!c) return false; ro_gui_menu_prepare_action(owner, action, true); ro_gui_dialog_open_persistent(g->window, dialog_print, windows_at_pointer); return true; case BROWSER_NEW_WINDOW: if (!c) return false; browser_window_create(c->url, bw, 0); return true; case BROWSER_VIEW_SOURCE: if (!c) return false; ro_gui_view_source(c); return true; /* object actions */ case BROWSER_OBJECT_INFO: if (!current_menu_object_box) return false; ro_gui_menu_prepare_action(owner, action, true); ro_gui_dialog_open_persistent(g->window, dialog_objinfo, windows_at_pointer); return true; case BROWSER_OBJECT_RELOAD: if (!current_menu_object_box) return false; current_menu_object_box->object->fresh = false; browser_window_reload(bw, false); return true; /* save actions */ case BROWSER_OBJECT_SAVE: case BROWSER_OBJECT_EXPORT_SPRITE: case BROWSER_OBJECT_SAVE_URL_URI: case BROWSER_OBJECT_SAVE_URL_URL: case BROWSER_OBJECT_SAVE_URL_TEXT: c = current_menu_object_box ? current_menu_object_box->object : NULL; case BROWSER_SAVE: case BROWSER_SAVE_COMPLETE: case BROWSER_EXPORT_DRAW: case BROWSER_EXPORT_TEXT: case BROWSER_SAVE_URL_URI: case BROWSER_SAVE_URL_URL: case BROWSER_SAVE_URL_TEXT: if (!c) return false; case HOTLIST_EXPORT: case HISTORY_EXPORT: ro_gui_menu_prepare_action(owner, action, true); ro_gui_dialog_open_persistent(owner, dialog_saveas, windows_at_pointer); return true; /* navigation actions */ case BROWSER_NAVIGATE_HOME: if (!bw) return false; if ((option_homepage_url) && (option_homepage_url[0])) { browser_window_go(g->bw, option_homepage_url, 0); } else { snprintf(url, sizeof url, "file://Docs/intro_%s", option_language); browser_window_go(g->bw, url, 0); } return true; case BROWSER_NAVIGATE_BACK: if ((!bw) || (!bw->history)) return false; history_back(bw, bw->history); return true; case BROWSER_NAVIGATE_FORWARD: if ((!bw) || (!bw->history)) return false; history_forward(bw, bw->history); return true; case BROWSER_NAVIGATE_RELOAD: case BROWSER_NAVIGATE_RELOAD_ALL: if (!bw) return false; browser_window_reload(bw, (action == BROWSER_NAVIGATE_RELOAD_ALL)); return true; case BROWSER_NAVIGATE_STOP: if (!bw) return false; browser_window_stop(bw); return true; case BROWSER_NAVIGATE_URL: ro_gui_menu_prepare_action(owner, action, true); ro_gui_dialog_open_persistent(NULL, dialog_openurl, windows_at_pointer); return true; /* browser window/display actions */ case BROWSER_SCALE_VIEW: if (!c) return false; ro_gui_menu_prepare_action(owner, action, true); ro_gui_dialog_open_persistent(g->window, dialog_zoom, windows_at_pointer); return true; case BROWSER_FIND_TEXT: if (!c || (c->type != CONTENT_HTML && c->type != CONTENT_TEXTPLAIN)) return false; ro_gui_menu_prepare_action(owner, action, true); ro_gui_menu_search_window_menu = false; ro_gui_dialog_open_persistent(g->window, dialog_search, windows_at_pointer); return true; case BROWSER_IMAGES_BACKGROUND: if (!g) return false; g->option.background_images = !g->option.background_images; gui_window_redraw_window(g); return true; case BROWSER_BUFFER_ANIMS: if (!g) return false; g->option.buffer_animations = !g->option.buffer_animations; break; case BROWSER_BUFFER_ALL: if (!g) return false; g->option.buffer_everything = !g->option.buffer_everything; break; case BROWSER_SAVE_VIEW: if (!bw) return false; ro_gui_window_default_options(bw); ro_gui_save_options(); return true; case BROWSER_WINDOW_DEFAULT: if (!g) return false; ro_gui_screen_size(&option_window_screen_width, &option_window_screen_height); state.w = current_menu_window; error = xwimp_get_window_state(&state); if (error) { LOG(("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); } option_window_x = state.visible.x0; option_window_y = state.visible.y0; option_window_width = state.visible.x1 - state.visible.x0; option_window_height = state.visible.y1 - state.visible.y0; ro_gui_save_options(); return true; case BROWSER_WINDOW_STAGGER: option_window_stagger = !option_window_stagger; ro_gui_save_options(); return true; case BROWSER_WINDOW_COPY: option_window_size_clone = !option_window_size_clone; ro_gui_save_options(); return true; case BROWSER_WINDOW_RESET: option_window_screen_width = 0; option_window_screen_height = 0; ro_gui_save_options(); return true; /* tree actions */ case TREE_NEW_FOLDER: ro_gui_menu_prepare_action(owner, action, true); ro_gui_dialog_open_persistent((wimp_w)tree->handle, dialog_folder, windows_at_pointer); return true; case TREE_NEW_LINK: ro_gui_menu_prepare_action(owner, action, true); ro_gui_dialog_open_persistent((wimp_w)tree->handle, dialog_entry, windows_at_pointer); return true; case TREE_EXPAND_ALL: case TREE_EXPAND_FOLDERS: case TREE_EXPAND_LINKS: tree_handle_expansion(tree, tree->root, true, (action != TREE_EXPAND_LINKS), (action != TREE_EXPAND_FOLDERS)); return true; case TREE_COLLAPSE_ALL: case TREE_COLLAPSE_FOLDERS: case TREE_COLLAPSE_LINKS: tree_handle_expansion(tree, tree->root, false, (action != TREE_COLLAPSE_LINKS), (action != TREE_COLLAPSE_FOLDERS)); return true; case TREE_SELECTION_EDIT: return true; case TREE_SELECTION_LAUNCH: ro_gui_tree_launch_selected(tree); return true; case TREE_SELECTION_DELETE: ro_gui_tree_stop_edit(tree); tree_delete_selected_nodes(tree, tree->root); if (tree == hotlist_tree) ro_gui_hotlist_save(); ro_gui_menu_prepare_action(owner, TREE_CLEAR_SELECTION, true); ro_gui_menu_prepare_action(owner, TREE_SELECTION, true); return true; case TREE_SELECT_ALL: ro_gui_tree_stop_edit(tree); if (tree->root->child) { tree->temp_selection = NULL; tree_set_node_selected(tree, tree->root, true); } ro_gui_menu_prepare_action(owner, TREE_CLEAR_SELECTION, true); ro_gui_menu_prepare_action(owner, TREE_SELECTION, true); return true; case TREE_CLEAR_SELECTION: tree->temp_selection = NULL; ro_gui_tree_stop_edit(tree); tree_set_node_selected(tree, tree->root, false); ro_gui_menu_prepare_action(owner, TREE_CLEAR_SELECTION, true); ro_gui_menu_prepare_action(owner, TREE_SELECTION, true); return true; /* toolbar actions */ case TOOLBAR_BUTTONS: assert(t); t->display_buttons = !t->display_buttons; ro_gui_menu_refresh_toolbar(t); return true; case TOOLBAR_ADDRESS_BAR: assert(t); t->display_url = !t->display_url; ro_gui_menu_refresh_toolbar(t); if (t->display_url) ro_gui_set_caret_first(t->toolbar_handle); return true; case TOOLBAR_THROBBER: assert(t); t->display_throbber = !t->display_throbber; ro_gui_menu_refresh_toolbar(t); return true; case TOOLBAR_STATUS_BAR: assert(t); t->display_status = !t->display_status; ro_gui_menu_refresh_toolbar(t); return true; case TOOLBAR_EDIT: assert(t); ro_gui_theme_toggle_edit(t); return true; /* misc actions */ case APPLICATION_QUIT: if (ro_gui_prequit()) { LOG(("QUIT in response to user request")); netsurf_quit = true; } return true; case CHOICES_SHOW: ro_gui_configure_show(); return true; /* unknown action */ default: return false; } return false; } /** * Prepares an action for use. * * \param owner the window to prepare the action for * \param action the action to prepare * \param windows whether to update sub-windows */ void ro_gui_menu_prepare_action(wimp_w owner, menu_action action, bool windows) { struct menu_definition_entry *entry; struct gui_window *g; struct browser_window *bw; struct content *c; struct toolbar *t; struct tree *tree; struct node *node; bool result = false; int checksum = 0; os_error *error; ro_gui_menu_get_window_details(owner, &g, &bw, &c, &t, &tree); if (current_menu_open) checksum = ro_gui_menu_get_checksum(); switch (action) { /* help actions */ case HELP_LAUNCH_INTERACTIVE: result = ro_gui_interactive_help_available(); ro_gui_menu_set_entry_shaded(current_menu, action, result); ro_gui_menu_set_entry_ticked(current_menu, action, result); break; /* history actions */ case HISTORY_SHOW_LOCAL: ro_gui_menu_set_entry_shaded(current_menu, action, (!bw || (!bw->history) || !(c || history_back_available(bw->history) || history_forward_available(bw->history)))); break; case HISTORY_SHOW_GLOBAL: ro_gui_menu_set_entry_shaded(current_menu, action, !global_history_tree); break; /* hotlist actions */ case HOTLIST_ADD_URL: ro_gui_menu_set_entry_shaded(current_menu, action, (!c || !hotlist_tree)); break; case HOTLIST_SHOW: ro_gui_menu_set_entry_shaded(current_menu, action, !hotlist_tree); if ((t) && (!t->editor) && (t->type == THEME_BROWSER_TOOLBAR)) ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_BOOKMARK, !hotlist_tree); break; /* page actions */ case BROWSER_PAGE_INFO: ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((windows) && (c)) ro_gui_menu_prepare_pageinfo(g); break; case BROWSER_PRINT: ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((t) && (t->type == THEME_BROWSER_TOOLBAR)) ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_PRINT, !c); if ((windows) && (c)) ro_gui_print_prepare(g); if ((t) && (!t->editor) && (t->type == THEME_BROWSER_TOOLBAR)) ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_PRINT, !c); break; case BROWSER_PAGE: case BROWSER_NEW_WINDOW: case BROWSER_VIEW_SOURCE: ro_gui_menu_set_entry_shaded(current_menu, action, !c); break; /* object actions */ case BROWSER_OBJECT: c = current_menu_object_box ? current_menu_object_box->object : NULL; ro_gui_menu_set_entry_shaded(current_menu, action, !c); break; case BROWSER_OBJECT_INFO: if ((windows) && (current_menu_object_box)) ro_gui_menu_prepare_objectinfo( current_menu_object_box); case BROWSER_OBJECT_RELOAD: ro_gui_menu_set_entry_shaded(current_menu, action, !current_menu_object_box); break; /* save actions (browser, hotlist, history) */ case BROWSER_OBJECT_SAVE: c = current_menu_object_box ? current_menu_object_box->object : NULL; ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((c) && (windows)) ro_gui_save_prepare(GUI_SAVE_OBJECT_ORIG, c); break; case BROWSER_OBJECT_EXPORT_SPRITE: c = current_menu_object_box ? current_menu_object_box->object : NULL; ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((c) && (windows)) ro_gui_save_prepare(GUI_SAVE_OBJECT_NATIVE, c); break; case BROWSER_SAVE: ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((c) && (windows)) ro_gui_save_prepare(GUI_SAVE_SOURCE, c); if ((t) && (!t->editor) && (t->type == THEME_BROWSER_TOOLBAR)) ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_SAVE, !c); break; case BROWSER_SAVE_COMPLETE: ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((c) && (windows)) ro_gui_save_prepare(GUI_SAVE_COMPLETE, c); break; case BROWSER_EXPORT_DRAW: ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((c) && (windows)) ro_gui_save_prepare(GUI_SAVE_DRAW, c); break; case BROWSER_EXPORT_TEXT: ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((c) && (windows)) ro_gui_save_prepare(GUI_SAVE_TEXT, c); break; case BROWSER_OBJECT_SAVE_URL_URI: c = current_menu_object_box ? current_menu_object_box->object : NULL; case BROWSER_SAVE_URL_URI: ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((c) && (windows)) ro_gui_save_prepare(GUI_SAVE_LINK_URI, c); break; case BROWSER_OBJECT_SAVE_URL_URL: c = current_menu_object_box ? current_menu_object_box->object : NULL; case BROWSER_SAVE_URL_URL: ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((c) && (windows)) ro_gui_save_prepare(GUI_SAVE_LINK_URL, c); break; case BROWSER_OBJECT_SAVE_URL_TEXT: c = current_menu_object_box ? current_menu_object_box->object : NULL; case BROWSER_SAVE_URL_TEXT: ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((c) && (windows)) ro_gui_save_prepare(GUI_SAVE_LINK_TEXT, c); break; case HOTLIST_EXPORT: if ((tree) && (windows)) ro_gui_save_prepare(GUI_SAVE_HOTLIST_EXPORT_HTML, NULL); break; case HISTORY_EXPORT: if ((tree) && (windows)) ro_gui_save_prepare(GUI_SAVE_HISTORY_EXPORT_HTML, NULL); break; /* navigation actions */ case BROWSER_NAVIGATE_BACK: result = (!bw || !bw->history || !history_back_available(bw->history)); ro_gui_menu_set_entry_shaded(current_menu, action, result); if ((t) && (!t->editor) && (t->type == THEME_BROWSER_TOOLBAR)) ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_BACK, result); break; case BROWSER_NAVIGATE_FORWARD: result = (!bw || !bw->history || !history_forward_available(bw->history)); ro_gui_menu_set_entry_shaded(current_menu, action, result); if ((t) && (!t->editor) && (t->type == THEME_BROWSER_TOOLBAR)) ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_FORWARD, result); break; case BROWSER_NAVIGATE_RELOAD: case BROWSER_NAVIGATE_RELOAD_ALL: result = (bw->current_content && !bw->loading_content); ro_gui_menu_set_entry_shaded(current_menu, action, !result); if ((t) && (!t->editor) && (t->type == THEME_BROWSER_TOOLBAR)) ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_RELOAD, !result); break; case BROWSER_NAVIGATE_STOP: result = (bw->loading_content || (bw->current_content && (bw->current_content->status != CONTENT_STATUS_DONE))); ro_gui_menu_set_entry_shaded(current_menu, action, !result); if ((t) && (!t->editor) && (t->type == THEME_BROWSER_TOOLBAR)) ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_STOP, !result); break; case BROWSER_NAVIGATE_URL: if (windows) ro_gui_dialog_prepare_open_url(); break; /* display actions */ case BROWSER_SCALE_VIEW: ro_gui_menu_set_entry_shaded(current_menu, action, !c); if ((c) && (windows)) ro_gui_dialog_prepare_zoom(g); if ((t) && (!t->editor) && (t->type == THEME_BROWSER_TOOLBAR)) ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_SCALE, !c); break; case BROWSER_FIND_TEXT: result = !c || (c->type != CONTENT_HTML && c->type != CONTENT_TEXTPLAIN); ro_gui_menu_set_entry_shaded(current_menu, action, result); if ((!result) && (windows)) { ro_gui_menu_search_window_menu = true; ro_gui_search_prepare(g); } if ((t) && (!t->editor) && (t->type == THEME_BROWSER_TOOLBAR)) ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_SEARCH, result); break; case BROWSER_IMAGES_FOREGROUND: ro_gui_menu_set_entry_shaded(current_menu, action, true); ro_gui_menu_set_entry_ticked(current_menu, action, true); break; case BROWSER_IMAGES_BACKGROUND: if (g) ro_gui_menu_set_entry_ticked(current_menu, action, g->option.background_images); break; case BROWSER_BUFFER_ANIMS: if (g) { ro_gui_menu_set_entry_shaded(current_menu, action, g->option.buffer_everything); ro_gui_menu_set_entry_ticked(current_menu, action, g->option.buffer_animations || g->option.buffer_everything); } break; case BROWSER_BUFFER_ALL: if (g) ro_gui_menu_set_entry_ticked(current_menu, action, g->option.buffer_everything); break; case BROWSER_WINDOW_STAGGER: ro_gui_menu_set_entry_shaded(current_menu, action, option_window_screen_width == 0); ro_gui_menu_set_entry_ticked(current_menu, action, ((option_window_screen_width == 0) || option_window_stagger)); break; case BROWSER_WINDOW_COPY: ro_gui_menu_set_entry_ticked(current_menu, action, option_window_size_clone); break; case BROWSER_WINDOW_RESET: ro_gui_menu_set_entry_shaded(current_menu, action, option_window_screen_width == 0); break; /* tree actions */ case TREE_NEW_FOLDER: ro_gui_hotlist_prepare_folder_dialog(NULL); break; case TREE_NEW_LINK: ro_gui_hotlist_prepare_entry_dialog(NULL); break; case TREE_EXPAND_ALL: case TREE_EXPAND_FOLDERS: case TREE_EXPAND_LINKS: case TREE_COLLAPSE_ALL: case TREE_COLLAPSE_FOLDERS: case TREE_COLLAPSE_LINKS: if ((tree) && (tree->root)) ro_gui_menu_set_entry_shaded(current_menu, action, !tree->root->child); break; case TREE_SELECTION: if ((!tree) || (!tree->root)) break; if (tree->root->child) result = tree_has_selection(tree->root->child); ro_gui_menu_set_entry_shaded(current_menu, action, !result); if ((t) && (!t->editor) && (t->type != THEME_BROWSER_TOOLBAR)) { ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_DELETE, !result); ro_gui_set_icon_shaded_state( t->toolbar_handle, ICON_TOOLBAR_LAUNCH, !result); } break; case TREE_SELECTION_EDIT: node = tree_get_selected_node(tree->root); entry = ro_gui_menu_find_entry(current_menu, action); if ((!node) || (!entry)) break; if (node->folder) { entry->menu_entry->sub_menu = (wimp_menu *)dialog_folder; if (windows) ro_gui_hotlist_prepare_folder_dialog(node); } else { entry->menu_entry->sub_menu = (wimp_menu *)dialog_entry; if (windows) ro_gui_hotlist_prepare_entry_dialog(node); } break; case TREE_SELECTION_LAUNCH: case TREE_SELECTION_DELETE: case TREE_CLEAR_SELECTION: if ((!tree) || (!tree->root)) break; if (tree->root->child) result = tree_has_selection(tree->root->child); ro_gui_menu_set_entry_shaded(current_menu, action, !result); break; case TREE_SELECT_ALL: ro_gui_menu_set_entry_shaded(current_menu, action, !tree->root->child); break; /* toolbar actions */ case TOOLBAR_BUTTONS: ro_gui_menu_set_entry_shaded(current_menu, action, (!t || (t->editor))); ro_gui_menu_set_entry_ticked(current_menu, action, (t && ((t->display_buttons) || (t->editor)))); break; case TOOLBAR_ADDRESS_BAR: ro_gui_menu_set_entry_shaded(current_menu, action, !t); ro_gui_menu_set_entry_ticked(current_menu, action, (t && t->display_url)); break; case TOOLBAR_THROBBER: ro_gui_menu_set_entry_shaded(current_menu, action, !t); ro_gui_menu_set_entry_ticked(current_menu, action, (t && t->display_throbber)); break; case TOOLBAR_STATUS_BAR: ro_gui_menu_set_entry_shaded(current_menu, action, !t); ro_gui_menu_set_entry_ticked(current_menu, action, (t && t->display_status)); break; case TOOLBAR_EDIT: ro_gui_menu_set_entry_shaded(current_menu, action, !t); ro_gui_menu_set_entry_ticked(current_menu, action, (t && t->editor)); break; /* unknown action */ default: return; } /* update open menus */ if ((current_menu_open) && (checksum != ro_gui_menu_get_checksum())) { error = xwimp_create_menu(current_menu, 0, 0); if (error) { LOG(("xwimp_create_menu: 0x%x: %s", error->errnum, error->errmess)); warn_user("MenuError", error->errmess); } } } /** * Gets various details relating to a window * * \param w the window to complete information for */ void ro_gui_menu_get_window_details(wimp_w w, struct gui_window **g, struct browser_window **bw, struct content **content, struct toolbar **toolbar, struct tree **tree) { *g = ro_gui_window_lookup(w); if (*g) { *bw = (*g)->bw; *toolbar = (*g)->toolbar; if (*bw) *content = (*bw)->current_content; *tree = NULL; } else { *bw = NULL; *content = NULL; if ((hotlist_tree) && (w == (wimp_w)hotlist_tree->handle)) *tree = hotlist_tree; else if ((global_history_tree) && (w == (wimp_w)global_history_tree->handle)) *tree = global_history_tree; else *tree = NULL; if (*tree) *toolbar = (*tree)->toolbar; else *toolbar = NULL; } } /** * Calculates a simple checksum for the current menu state */ int ro_gui_menu_get_checksum(void) { wimp_selection menu_tree; int i = 0, j, checksum = 0; os_error *error; wimp_menu *menu; if (!current_menu_open) return 0; error = xwimp_get_menu_state((wimp_menu_state_flags)0, &menu_tree, 0, 0); if (error) { LOG(("xwimp_get_menu_state: 0x%x: %s", error->errnum, error->errmess)); warn_user("MenuError", error->errmess); return 0; } menu = current_menu; do { j = 0; do { if (menu->entries[j].icon_flags & wimp_ICON_SHADED) checksum ^= (1 << (i + j * 2)); if (menu->entries[j].menu_flags & wimp_MENU_TICKED) checksum ^= (2 << (i + j * 2)); } while (!(menu->entries[j++].menu_flags & wimp_MENU_LAST)); j = menu_tree.items[i++]; if (j != -1) { menu = menu->entries[j].sub_menu; if ((!menu) || (menu == wimp_NO_SUB_MENU) || (!IS_MENU(menu))) break; } } while (j != -1); return checksum; } /** * Translate a menu's textual content into the system local encoding * * \param menu The menu to translate * \return false if out of memory, true otherwise */ bool ro_gui_menu_translate(struct menu_definition *menu) { os_error *error; int alphabet; struct menu_definition_entry *entry; char *translated; utf8_convert_ret err; /* read current alphabet */ error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &alphabet); if (error) { LOG(("failed reading alphabet: 0x%x: %s", error->errnum, error->errmess)); /* assume Latin1 */ alphabet = territory_ALPHABET_LATIN1; } if (menu->current_encoding == alphabet) /* menu text is already in the correct encoding */ return true; /* translate root menu title text */ free(menu->menu->title_data.indirected_text.text); err = utf8_to_local_encoding(messages_get(menu->title_key), 0, &translated); if (err != UTF8_CONVERT_OK) { assert(err != UTF8_CONVERT_BADENC); LOG(("utf8_to_enc failed")); return false; } /* and fill in WIMP menu field */ menu->menu->title_data.indirected_text.text = translated; /* now the menu entries */ for (entry = menu->entries; entry; entry = entry->next) { wimp_menu *submenu = entry->menu_entry->sub_menu; /* tranlate menu entry text */ free(entry->menu_entry->data.indirected_text.text); err = utf8_to_local_encoding(messages_get(entry->entry_key), 0, &translated); if (err != UTF8_CONVERT_OK) { assert(err != UTF8_CONVERT_BADENC); LOG(("utf8_to_enc failed")); return false; } /* fill in WIMP menu fields */ entry->menu_entry->data.indirected_text.text = translated; entry->menu_entry->data.indirected_text.validation = (char *) -1; entry->menu_entry->data.indirected_text.size = strlen(translated); /* child menu title - this is the same as the text of * the parent menu entry, so just copy the pointer */ if (submenu != wimp_NO_SUB_MENU && IS_MENU(submenu)) { submenu->title_data.indirected_text.text = translated; } } /* finally, set the current encoding of the menu */ menu->current_encoding = alphabet; return true; }