/* * Copyright 2004, 2005 Richard Wilson * Copyright 2010, 2011 Stephen Fryatt * * This file is part of NetSurf, http://www.netsurf-browser.org/ * * NetSurf is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * NetSurf is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** \file * Window toolbars (implementation). */ #include #include #include #include #include #include "oslib/dragasprite.h" #include "oslib/os.h" #include "oslib/osgbpb.h" #include "oslib/osfile.h" #include "oslib/osfind.h" #include "oslib/osspriteop.h" #include "oslib/wimpspriteop.h" #include "oslib/squash.h" #include "oslib/wimp.h" #include "oslib/wimpextend.h" #include "oslib/wimpspriteop.h" #include "utils/log.h" #include "utils/utils.h" #include "utils/nsoption.h" #include "content/content.h" #include "desktop/plotters.h" #include "riscos/cookies.h" #include "riscos/dialog.h" #include "riscos/global_history.h" #include "riscos/gui.h" #include "riscos/gui/button_bar.h" #include "riscos/gui/throbber.h" #include "riscos/gui/url_bar.h" #include "riscos/hotlist.h" #include "riscos/menus.h" #include "riscos/save.h" #include "riscos/theme.h" #include "riscos/toolbar.h" #include "riscos/treeview.h" #include "riscos/url_complete.h" #include "riscos/wimp.h" #include "riscos/wimp_event.h" #include "riscos/wimputils.h" #include "riscos/window.h" #define TOOLBAR_WIDGET_GUTTER 8 #define TOOLBAR_DEFAULT_WIDTH 16384 /* Toolbar rows used to index into the arrays of row-specific data. */ #define TOOLBAR_ROW_TOP 0 #define TOOLBAR_ROW_DIV1 1 #define TOOLBAR_ROW_EDIT 2 #define TOOLBAR_MAX_ROWS 3 /* The toolbar data structure. */ struct toolbar { /** Bar details. */ struct theme_descriptor *theme; theme_style style; toolbar_flags flags; int current_width, current_height; int full_width, full_height; int clip_width, clip_height; /** Toolbar and parent window handles. */ wimp_w toolbar_handle; wimp_w parent_handle; /** Row locations and sizes. */ int row_y0[TOOLBAR_MAX_ROWS]; int row_y1[TOOLBAR_MAX_ROWS]; /** Details for the button bar. */ struct button_bar *buttons; bool buttons_display; os_coord buttons_size; /** Details for the URL bar. */ struct url_bar *url; bool url_display; os_coord url_size; /** Details for the throbber. */ struct throbber *throbber; bool throbber_display; bool throbber_right; os_coord throbber_size; /** Client callback data. */ const struct toolbar_callbacks *callbacks; void *client_data; /** Details for the toolbar editor. */ wimp_i editor_div1; struct button_bar *editor; os_coord editor_size; bool editing; /** Interactive help data. */ const char *help_prefix; /** The next bar in the toolbar list. */ struct toolbar *next; }; /* Global variables for the toolbar module. */ /** The list of defined toolbars. */ static struct toolbar *ro_toolbar_bars = NULL; /** The Toolber Menu */ wimp_menu *toolbar_menu; /* A basic window definition for the toolbar and status bar. */ static wimp_window ro_toolbar_window = { {0, 0, 1, 1}, 0, 0, wimp_TOP, wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_NO_BOUNDS | wimp_WINDOW_FURNITURE_WINDOW | wimp_WINDOW_IGNORE_XEXTENT | wimp_WINDOW_IGNORE_YEXTENT, wimp_COLOUR_BLACK, wimp_COLOUR_LIGHT_GREY, wimp_COLOUR_LIGHT_GREY, wimp_COLOUR_VERY_LIGHT_GREY, wimp_COLOUR_DARK_GREY, wimp_COLOUR_MID_LIGHT_GREY, wimp_COLOUR_CREAM, wimp_WINDOW_NEVER3D | 0x16u /* RISC OS 5.03+ */, {0, 0, TOOLBAR_DEFAULT_WIDTH, 16384}, 0, wimp_BUTTON_DOUBLE_CLICK_DRAG << wimp_ICON_BUTTON_TYPE_SHIFT, wimpspriteop_AREA, 1, 1, {""}, 0, { } }; static char ro_toolbar_null_string[] = ""; static char ro_toolbar_line_validation[] = "R2"; /* * Private function prototypes. */ static void ro_toolbar_update_current_widgets(struct toolbar *toolbar); static void ro_toolbar_refresh_widget_dimensions(struct toolbar *toolbar); static void ro_toolbar_reformat_widgets(struct toolbar *toolbar); static void ro_toolbar_redraw(wimp_draw *redraw); static bool ro_toolbar_click(wimp_pointer *pointer); static bool ro_toolbar_keypress(wimp_key *key); static bool ro_toolbar_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu, wimp_pointer *pointer); static void ro_toolbar_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu, wimp_selection *selection, menu_action action); static bool ro_toolbar_menu_select(wimp_w w, wimp_i i, wimp_menu *menu, wimp_selection *selection, menu_action action); static const char *ro_toolbar_get_help_suffix(wimp_w w, wimp_i i, os_coord *pos, wimp_mouse_state buttons); static void ro_toolbar_update_buttons(struct toolbar *toolbar); /* This is an exported interface documented in toolbar.h */ void ro_toolbar_init(void) { /* browser toolbar menu */ static const struct ns_menu toolbar_definition = { "Toolbar", { { "Toolbars", NO_ACTION, 0 }, { "Toolbars.ToolButtons", TOOLBAR_BUTTONS, 0 }, { "Toolbars.ToolAddress", TOOLBAR_ADDRESS_BAR, 0 }, { "Toolbars.ToolThrob", TOOLBAR_THROBBER, 0 }, { "EditToolbar", TOOLBAR_EDIT, 0 }, {NULL, 0, 0} } }; toolbar_menu = ro_gui_menu_define_menu( &toolbar_definition); } /* This is an exported interface documented in toolbar.h */ struct toolbar *ro_toolbar_create(struct theme_descriptor *descriptor, wimp_w parent, theme_style style, toolbar_flags bar_flags, const struct toolbar_callbacks *callbacks, void *client_data, const char *help) { struct toolbar *toolbar; /* Allocate memory for the bar and link it into the list of bars. */ toolbar = calloc(sizeof(struct toolbar), 1); if (toolbar == NULL) { LOG(("No memory for malloc()")); warn_user("NoMemory", 0); return NULL; } toolbar->next = ro_toolbar_bars; ro_toolbar_bars = toolbar; /* Store the supplied settings. */ toolbar->flags = bar_flags; toolbar->theme = descriptor; toolbar->style = style; toolbar->parent_handle = parent; toolbar->callbacks = callbacks; toolbar->client_data = client_data; /* Set up the internal widgets: initially, there are none. */ toolbar->buttons = NULL; toolbar->buttons_display = false; toolbar->url = NULL; toolbar->url_display = false; toolbar->throbber = NULL; toolbar->throbber_display = false; /* Set up the bar editor. */ toolbar->editor = NULL; toolbar->editor_div1 = -1; toolbar->editing = false; toolbar->help_prefix = help; return toolbar; } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_add_buttons(struct toolbar *toolbar, const struct button_bar_buttons buttons[], char *button_order) { if (toolbar == NULL) return false; if (toolbar->buttons != NULL) return false; toolbar->buttons = ro_gui_button_bar_create(toolbar->theme, buttons); if (toolbar->buttons != NULL) { toolbar->buttons_display = true; ro_gui_button_bar_arrange_buttons(toolbar->buttons, button_order); } toolbar->editor = ro_gui_button_bar_create(toolbar->theme, buttons); if (toolbar->editor != NULL) ro_gui_button_bar_hide(toolbar->editor, !toolbar->editing); if (toolbar->buttons != NULL && toolbar->editor != NULL) if (!ro_gui_button_bar_link_editor(toolbar->buttons, toolbar->editor, (void (*)(void *)) ro_toolbar_update_current_widgets, toolbar)) return false; return (toolbar->buttons == NULL || toolbar->editor == NULL) ? false : true; } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_add_throbber(struct toolbar *toolbar) { if (toolbar == NULL) return false; if (toolbar->throbber != NULL) return false; toolbar->throbber = ro_gui_throbber_create(toolbar->theme); if (toolbar->throbber != NULL) toolbar->throbber_display = true; return (toolbar->throbber == NULL) ? false : true; } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_add_url(struct toolbar *toolbar) { if (toolbar == NULL) return false; if (toolbar->url != NULL) return false; toolbar->url = ro_gui_url_bar_create(toolbar->theme); if (toolbar->url != NULL) toolbar->url_display = true; return (toolbar->url == NULL) ? false : true; } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_rebuild(struct toolbar *toolbar) { os_error *error; wimp_icon_create icon; wimp_w old_window = NULL; if (toolbar == NULL) return false; /* Start to set up the toolbar window. */ ro_toolbar_window.sprite_area = ro_gui_theme_get_sprites(toolbar->theme); ro_toolbar_window.work_bg = ro_gui_theme_get_style_element(toolbar->theme, toolbar->style, THEME_ELEMENT_BACKGROUND); /* Delete any existing toolbar window... */ if (toolbar->toolbar_handle != NULL) { old_window = toolbar->toolbar_handle; error = xwimp_delete_window(toolbar->toolbar_handle); if (error) LOG(("xwimp_delete_window: 0x%x: %s", error->errnum, error->errmess)); toolbar->toolbar_handle = NULL; } /* ...and create a new window. */ error = xwimp_create_window(&ro_toolbar_window, &toolbar->toolbar_handle); if (error) { LOG(("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return false; } /* Set up the toolbar's event handlers. Only set the user activity- * related callbacks if the bar isn't for display purposes. If the * toolbar is being recreated, simply transfer the handlers across * from the old, now-deleted window. */ if (old_window == NULL) { ro_gui_wimp_event_register_redraw_window( toolbar->toolbar_handle, ro_toolbar_redraw); ro_gui_wimp_event_set_user_data(toolbar->toolbar_handle, toolbar); if (!(toolbar->flags & TOOLBAR_FLAGS_DISPLAY)) { ro_gui_wimp_event_register_mouse_click( toolbar->toolbar_handle, ro_toolbar_click); ro_gui_wimp_event_register_keypress( toolbar->toolbar_handle, ro_toolbar_keypress); ro_gui_wimp_event_register_menu_prepare( toolbar->toolbar_handle, ro_toolbar_menu_prepare); ro_gui_wimp_event_register_menu_warning( toolbar->toolbar_handle, ro_toolbar_menu_warning); ro_gui_wimp_event_register_menu_selection( toolbar->toolbar_handle, ro_toolbar_menu_select); ro_gui_wimp_event_register_menu(toolbar->toolbar_handle, toolbar_menu, true, false); ro_gui_wimp_event_register_help_suffix( toolbar->toolbar_handle, ro_toolbar_get_help_suffix); } } else { ro_gui_wimp_event_transfer(old_window, toolbar->toolbar_handle); } /* The help prefix changes from edit to non-edit more. */ ro_gui_wimp_event_set_help_prefix(toolbar->toolbar_handle, (toolbar->editing) ? "HelpEditToolbar" : toolbar->help_prefix); /* Place the widgets into the new bar, using the new theme. * * \TODO -- If any widgets fail to rebuild, then we currently just * carry on without them. Not sure if the whole bar * rebuild should fail here? */ if (toolbar->throbber != NULL) { if (!ro_gui_throbber_rebuild(toolbar->throbber, toolbar->theme, toolbar->style, toolbar->toolbar_handle, toolbar->editing)) { ro_gui_throbber_destroy(toolbar->throbber); toolbar->throbber = NULL; } ro_gui_theme_get_throbber_data(toolbar->theme, NULL, NULL, NULL, &toolbar->throbber_right, NULL); } if (toolbar->buttons != NULL) { if (!ro_gui_button_bar_rebuild(toolbar->buttons, toolbar->theme, toolbar->style, toolbar->toolbar_handle, toolbar->editing)) { ro_gui_button_bar_destroy(toolbar->buttons); toolbar->buttons = NULL; } } if (toolbar->editor != NULL) { if (!ro_gui_button_bar_rebuild(toolbar->editor, toolbar->theme, toolbar->style, toolbar->toolbar_handle, toolbar->editing)) { ro_gui_button_bar_destroy(toolbar->editor); toolbar->editor = NULL; } } if (toolbar->url != NULL) { if (!ro_gui_url_bar_rebuild(toolbar->url, toolbar->theme, toolbar->style, toolbar->toolbar_handle, toolbar->flags & TOOLBAR_FLAGS_DISPLAY, toolbar->editing)) { ro_gui_url_bar_destroy(toolbar->url); toolbar->url = NULL; } } /* If this is an editor, add in a divider icon and the editor * button bar. */ if (toolbar->editing) { icon.w = toolbar->toolbar_handle; icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED | wimp_ICON_VCENTRED | wimp_ICON_BORDER | (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | (wimp_COLOUR_VERY_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT); icon.icon.extent.x0 = 0; icon.icon.extent.x1 = 0; icon.icon.extent.y1 = 0; icon.icon.extent.y0 = 0; icon.icon.data.indirected_text.text = ro_toolbar_null_string; icon.icon.data.indirected_text.validation = ro_toolbar_line_validation; icon.icon.data.indirected_text.size = 1; error = xwimp_create_icon(&icon, &toolbar->editor_div1); if (error) { LOG(("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); toolbar->editor_div1 = -1; } } /* Establish the required dimensions to fit the widgets, then * reflow the bar contents. */ ro_toolbar_refresh_widget_dimensions(toolbar); ro_toolbar_process(toolbar, -1, true); if (toolbar->parent_handle != NULL) ro_toolbar_attach(toolbar, toolbar->parent_handle); ro_toolbar_update_buttons(toolbar); return true; } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_attach(struct toolbar *toolbar, wimp_w parent) { wimp_outline outline; wimp_window_state state; os_error *error; if (toolbar == NULL || toolbar->toolbar_handle == NULL) return false; toolbar->parent_handle = parent; /* Only try to attach the toolbar if there's any of it visible to * matter. */ if (toolbar->current_height > 0) { outline.w = parent; xwimp_get_window_outline(&outline); state.w = parent; xwimp_get_window_state(&state); state.w = toolbar->toolbar_handle; state.visible.x1 = outline.outline.x1 - 2; state.visible.y0 = state.visible.y1 + 2 - toolbar->current_height; state.xscroll = 0; state.yscroll = 0; error = xwimp_open_window_nested(PTR_WIMP_OPEN(&state), parent, wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT << wimp_CHILD_XORIGIN_SHIFT | wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT << wimp_CHILD_YORIGIN_SHIFT | wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT << wimp_CHILD_LS_EDGE_SHIFT | wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT << wimp_CHILD_BS_EDGE_SHIFT | wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT << wimp_CHILD_RS_EDGE_SHIFT | wimp_CHILD_LINKS_PARENT_VISIBLE_TOP_OR_RIGHT << wimp_CHILD_TS_EDGE_SHIFT); if (error) { LOG(("xwimp_open_window_nested: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return false; } return true; } error = xwimp_close_window(toolbar->toolbar_handle); if (error) { LOG(("xwimp_close_window: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return false; } return true; } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_process(struct toolbar *toolbar, int width, bool reformat) { os_error *error; wimp_outline outline; wimp_window_state state; os_box extent; int old_height, old_width; if (!toolbar) return false; old_height = toolbar->current_height; old_width = toolbar->current_width; /* Measure the parent window width if the caller has asked us to * calculate the clip width ourselves. Otherwise, if a clip width * has been specified, set the clip to that. */ if ((toolbar->parent_handle != NULL) && (width == -1)) { outline.w = toolbar->parent_handle; error = xwimp_get_window_outline(&outline); if (error) { LOG(("xwimp_get_window_outline: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return false; } toolbar->clip_width = outline.outline.x1 - outline.outline.x0 - 2; toolbar->current_width = toolbar->clip_width; } else if (width != -1) { toolbar->clip_width = width; toolbar->current_width = toolbar->clip_width; } /* Find the parent visible height to clip our toolbar height to */ if (toolbar->parent_handle != NULL) { state.w = toolbar->parent_handle; 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 false; } toolbar->clip_height = state.visible.y1 - state.visible.y0 + 2; /* We can't obscure the height of the scroll bar as we * lose the resize icon if we do. */ if (toolbar->clip_height >= toolbar->full_height) toolbar->current_height = toolbar->full_height; else toolbar->current_height = toolbar->clip_height; /* Resize the work area extent and update our position. */ if (old_height != toolbar->current_height) { extent.x0 = 0; extent.y0 = 0; extent.x1 = TOOLBAR_DEFAULT_WIDTH; extent.y1 = toolbar->current_height - 2; error = xwimp_set_extent(toolbar->toolbar_handle, &extent); if (error) { LOG(("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); } ro_toolbar_attach(toolbar, toolbar->parent_handle); } } else { toolbar->clip_height = toolbar->full_height; toolbar->current_height = toolbar->full_height; } /* Reflow the widgets into the toolbar if the dimensions have * changed or we have been asked to anyway. */ if (toolbar->current_width != old_width || reformat) ro_toolbar_reformat_widgets(toolbar); return true; } /** * Update the widgets currently on view in a toolbar. This can be used * generally, but is primarily offered to widgets as a way for them * to force an update. * * \param *toolbar The toolbar to update. */ void ro_toolbar_update_current_widgets(struct toolbar *toolbar) { int old_height; if (toolbar == NULL) return; old_height = toolbar->full_height; ro_toolbar_refresh_widget_dimensions(toolbar); ro_toolbar_reformat_widgets(toolbar); /* If the toolbar height has changed, we need to tell the client. */ if (toolbar->full_height != old_height) ro_toolbar_refresh(toolbar); } /** * Get the minimum dimenstions required by the toolbar widgets after * these have changed. The minimum dimensions are assumed not to change * unless we change theme (ie. we rebuild the bar) or we knowingly * alter a widget (eg. we add or remove button-bar buttons). * * * \param *toolbar The toolbar to refresh. */ void ro_toolbar_refresh_widget_dimensions(struct toolbar *toolbar) { int width, height; int row_width, row_height; if (toolbar == NULL) return; /* Process the toolbar editor and any associated divider rows. */ if (toolbar->editor != NULL && toolbar->editing) { width = 0; height = 0; ro_gui_button_bar_get_dims(toolbar->editor, &width, &height); toolbar->editor_size.x = width; toolbar->editor_size.y = height; toolbar->row_y0[TOOLBAR_ROW_EDIT] = TOOLBAR_WIDGET_GUTTER; toolbar->row_y1[TOOLBAR_ROW_EDIT] = TOOLBAR_WIDGET_GUTTER + height; toolbar->row_y0[TOOLBAR_ROW_DIV1] = TOOLBAR_WIDGET_GUTTER + toolbar->row_y1[TOOLBAR_ROW_EDIT]; toolbar->row_y1[TOOLBAR_ROW_DIV1] = 8 + toolbar->row_y0[TOOLBAR_ROW_DIV1]; } else { toolbar->editor_size.x = 0; toolbar->editor_size.y = 0; toolbar->row_y0[TOOLBAR_ROW_EDIT] = 0; toolbar->row_y1[TOOLBAR_ROW_EDIT] = 0; toolbar->row_y0[TOOLBAR_ROW_DIV1] = 0; toolbar->row_y1[TOOLBAR_ROW_DIV1] = 0; } /* Process the top row icons. */ row_width = 0; row_height = 0; /* If the editor is active, any button bar if forced into view. */ if (toolbar->buttons != NULL && (toolbar->buttons_display || toolbar->editing)) { width = 0; height = 0; ro_gui_button_bar_get_dims(toolbar->buttons, &width, &height); row_width += width; toolbar->buttons_size.x = width; toolbar->buttons_size.y = height; if (height > row_height) row_height = height; } else { toolbar->buttons_size.x = 0; toolbar->buttons_size.y = 0; } if (toolbar->url != NULL && toolbar->url_display) { width = 0; height = 0; ro_gui_url_bar_get_dims(toolbar->url, &width, &height); if (row_width > 0) row_width += TOOLBAR_WIDGET_GUTTER; row_width += width; toolbar->url_size.x = width; toolbar->url_size.y = height; if (height > row_height) row_height = height; } else { toolbar->url_size.x = 0; toolbar->url_size.y = 0; } if (toolbar->throbber != NULL && toolbar->throbber_display) { width = 0; height = 0; ro_gui_throbber_get_dims(toolbar->throbber, &width, &height); if (row_width > 0) row_width += TOOLBAR_WIDGET_GUTTER; row_width += width; toolbar->throbber_size.x = width; toolbar->throbber_size.y = height; if (height > row_height) row_height = height; } else { toolbar->throbber_size.x = 0; toolbar->throbber_size.y = 0; } if (row_height > 0) { toolbar->row_y0[TOOLBAR_ROW_TOP] = TOOLBAR_WIDGET_GUTTER + toolbar->row_y1[TOOLBAR_ROW_DIV1]; toolbar->row_y1[TOOLBAR_ROW_TOP] = row_height + toolbar->row_y0[TOOLBAR_ROW_TOP]; } else { toolbar->row_y0[TOOLBAR_ROW_TOP] = 0; toolbar->row_y1[TOOLBAR_ROW_TOP] = 0; } /* Establish the full dimensions of the bar. * * \TODO -- This currently assumes an "all or nothing" approach to * the editor bar, and will need reworking once we have to * worry about tab bars. */ if (toolbar->row_y1[TOOLBAR_ROW_TOP] > 0) { toolbar->full_height = toolbar->row_y1[TOOLBAR_ROW_TOP] + TOOLBAR_WIDGET_GUTTER; } else { toolbar->full_height = 0; } toolbar->full_width = 2 * TOOLBAR_WIDGET_GUTTER + ((row_width > toolbar->editor_size.x) ? row_width : toolbar->editor_size.x); } /** * Reformat (reflow) the widgets into the toolbar, based on the toolbar size * and the previously calculated widget dimensions. * * \param *toolbar The toolbar to reformat. */ void ro_toolbar_reformat_widgets(struct toolbar *toolbar) { int left_margin, right_margin; left_margin = TOOLBAR_WIDGET_GUTTER; right_margin = toolbar->clip_width - TOOLBAR_WIDGET_GUTTER; /* Flow the toolbar editor row, which will be a fixed with and * may alter the right margin. */ if (toolbar->editor != NULL && toolbar->editing) { if (right_margin < left_margin + toolbar->editor_size.x) right_margin = left_margin + toolbar->editor_size.x; ro_gui_button_bar_set_extent(toolbar->editor, left_margin, toolbar->row_y0[TOOLBAR_ROW_EDIT], left_margin + toolbar->editor_size.x, toolbar->row_y1[TOOLBAR_ROW_EDIT]); if (toolbar->editor_div1 != -1) xwimp_resize_icon(toolbar->toolbar_handle, toolbar->editor_div1, -8, toolbar->row_y0[TOOLBAR_ROW_DIV1], toolbar->clip_width + 8, toolbar->row_y1[TOOLBAR_ROW_DIV1]); } /* Flow the top row. */ if (toolbar->throbber != NULL && toolbar->throbber_display) { if (toolbar->throbber_right) { right_margin -= (toolbar->throbber_size.x + TOOLBAR_WIDGET_GUTTER); } else { ro_gui_throbber_set_extent(toolbar->throbber, left_margin, toolbar->row_y0[TOOLBAR_ROW_TOP], left_margin + toolbar->throbber_size.x, toolbar->row_y1[TOOLBAR_ROW_TOP]); left_margin += (toolbar->throbber_size.x + TOOLBAR_WIDGET_GUTTER); } } if (toolbar->buttons != NULL && (toolbar->buttons_display || toolbar->editing)) { if (right_margin < left_margin + toolbar->buttons_size.x) right_margin = left_margin + toolbar->buttons_size.x; ro_gui_button_bar_set_extent(toolbar->buttons, left_margin, toolbar->row_y0[TOOLBAR_ROW_TOP], left_margin + toolbar->buttons_size.x, toolbar->row_y1[TOOLBAR_ROW_TOP]); left_margin += (toolbar->buttons_size.x + TOOLBAR_WIDGET_GUTTER); } if (toolbar->url != NULL && toolbar->url_display) { if (right_margin < left_margin + toolbar->url_size.x) right_margin = left_margin + toolbar->url_size.x; ro_gui_url_bar_set_extent(toolbar->url, left_margin, toolbar->row_y0[TOOLBAR_ROW_TOP], right_margin, toolbar->row_y1[TOOLBAR_ROW_TOP]); left_margin = right_margin + TOOLBAR_WIDGET_GUTTER; } if (toolbar->throbber != NULL && toolbar->throbber_display && toolbar->throbber_right) { left_margin = right_margin + TOOLBAR_WIDGET_GUTTER; ro_gui_throbber_set_extent(toolbar->throbber, left_margin, toolbar->row_y0[TOOLBAR_ROW_TOP], left_margin + toolbar->throbber_size.x, toolbar->row_y1[TOOLBAR_ROW_TOP]); } } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_destroy(struct toolbar *toolbar) { struct toolbar *bar; if (toolbar == NULL) return; LOG(("Destroying toolbar 0x%x", (unsigned int) toolbar)); /* Destroy the widgets. */ if (toolbar->buttons != NULL) ro_gui_button_bar_destroy(toolbar->buttons); if (toolbar->editor != NULL) ro_gui_button_bar_destroy(toolbar->editor); if (toolbar->url != NULL) ro_gui_url_bar_destroy(toolbar->url); if (toolbar->throbber != NULL) ro_gui_throbber_destroy(toolbar->throbber); /* Delete the toolbar window. */ if (toolbar->toolbar_handle != NULL) { xwimp_delete_window(toolbar->toolbar_handle); ro_gui_wimp_event_finalise(toolbar->toolbar_handle); } /* Remove the bar from the list and free the memory. */ if (ro_toolbar_bars == toolbar) { ro_toolbar_bars = toolbar->next; } else { for (bar = ro_toolbar_bars; bar != NULL && bar->next != toolbar; bar = bar->next); if (bar->next == toolbar) bar->next = toolbar->next; } free(toolbar); } /** * Handle redraw request events for a toolbar workarea. * * \param *redraw The redraw block for the event. */ void ro_toolbar_redraw(wimp_draw *redraw) { struct toolbar *toolbar; osbool more; os_error *error; toolbar = (struct toolbar *)ro_gui_wimp_event_get_user_data(redraw->w); assert(toolbar != NULL); error = xwimp_redraw_window(redraw, &more); if (error) { LOG(("xwimp_redraw_window: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return; } while (more) { ro_plot_origin_x = redraw->box.x0 - redraw->xscroll; ro_plot_origin_y = redraw->box.y1 - redraw->yscroll; if (toolbar->buttons != NULL && toolbar->buttons_display) ro_gui_button_bar_redraw(toolbar->buttons, redraw); if (toolbar->editor != NULL && toolbar->editing) ro_gui_button_bar_redraw(toolbar->editor, redraw); if (toolbar->url != NULL && toolbar->url_display) ro_gui_url_bar_redraw(toolbar->url, redraw); error = xwimp_get_rectangle(redraw, &more); if (error) { LOG(("xwimp_get_rectangle: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return; } } } /** * Process clicks on a toolbar, passing details on to clients where necessary. * * \param *pointer The wimp mouse click event. * \return True if the event was handled; else false. */ bool ro_toolbar_click(wimp_pointer *pointer) { struct toolbar *toolbar; union toolbar_action action; wimp_window_state state; os_error *error; toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(pointer->w); if (toolbar == NULL) return false; assert(pointer->w == toolbar->toolbar_handle); state.w = toolbar->toolbar_handle; 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 false; } /* If the click wasn't in the URL Bar's text field, then it will * need to close any URL Complete window that is open. * * \TODO -- This should really move into the URL Bar module, as * URL Complete is really an extension to that. */ if (toolbar->url != NULL && toolbar->url_display && !ro_gui_url_bar_test_for_text_field_click(toolbar->url, pointer)) ro_gui_url_complete_close(); /* Pass the click around the toolbar widgets. */ if (toolbar->buttons != NULL && (toolbar->buttons_display || toolbar->editing) && ro_gui_button_bar_click(toolbar->buttons, pointer, &state, &action.button)) { if (action.button != TOOLBAR_BUTTON_NONE && !toolbar->editing && toolbar->callbacks != NULL && toolbar->callbacks->user_action != NULL) toolbar->callbacks->user_action(toolbar->client_data, TOOLBAR_ACTION_BUTTON, action); return true; } if (toolbar->url != NULL && toolbar->url_display && ro_gui_url_bar_click(toolbar->url, pointer, &state, &action.url)) { if (action.url != TOOLBAR_URL_NONE && !toolbar->editing && toolbar->callbacks != NULL && toolbar->callbacks->user_action != NULL) toolbar->callbacks->user_action(toolbar->client_data, TOOLBAR_ACTION_URL, action); return true; } if (toolbar->editor != NULL && toolbar->editing && ro_gui_button_bar_click(toolbar->editor, pointer, &state, &action.button)) { return true; } /* Nothing else has handled this, so try passing it to the * URL Complete module. * * \TODO -- This should really move into the URL Bar module, as * URL Complete is really an extension to that. */ if (toolbar->url != NULL && toolbar->url_display && ro_gui_url_bar_test_for_text_field_click(toolbar->url, pointer)) { ro_gui_url_complete_start(toolbar); return true; } return false; } /** * Process keypresses in a toolbar, passing details on to clients where * necessary. * * \param *key The wimp key press event. * \return True if the event was handled; else false. */ bool ro_toolbar_keypress(wimp_key *key) { struct toolbar *toolbar; toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(key->w); if (toolbar == NULL) return false; /* Pass the keypress on to the client and stop if they handle it. */ if (toolbar->callbacks->key_press != NULL && toolbar->callbacks->key_press(toolbar->client_data, key)) return true; /* If the caret is in the URL bar, ask the URL Complete module if it * wants to handle the keypress. * * \TODO -- This should really move into the URL Bar module, as * URL Complete is really an extension to that. */ if (toolbar->url != NULL && toolbar->url_display && ro_gui_url_bar_test_for_text_field_keypress( toolbar->url, key) && ro_gui_url_complete_keypress(toolbar, key->c)) return true; return false; } /** * Prepare the toolbar menu for (re-)opening * * \param w The window owning the menu. * \param i The icon owning the menu. * \param *menu The menu about to be opened. * \param *pointer Pointer to the relevant wimp event block, or * NULL for an Adjust click. * \return true if the event was handled; else false. */ bool ro_toolbar_menu_prepare(wimp_w w, wimp_i i, wimp_menu *menu, wimp_pointer *pointer) { struct toolbar *toolbar; toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(w); if (toolbar == NULL) return false; /* Pass the event on to potentially interested widgets. */ if (toolbar->url != NULL && ro_gui_url_bar_menu_prepare(toolbar->url, i, menu, pointer)) return true; /* Try to process the event as a toolbar menu. */ if (menu != toolbar_menu) return false; /* Shade menu entries according to the state of the window and object * under the pointer. */ /* Toolbar (Sub)Menu */ ro_gui_menu_set_entry_shaded(menu, TOOLBAR_EDIT, ro_toolbar_menu_edit_shade(toolbar)); ro_gui_menu_set_entry_ticked(menu, TOOLBAR_EDIT, ro_toolbar_menu_edit_tick(toolbar)); ro_gui_menu_set_entry_shaded(menu, TOOLBAR_BUTTONS, ro_toolbar_menu_option_shade(toolbar) || toolbar->buttons == NULL); ro_gui_menu_set_entry_ticked(menu, TOOLBAR_BUTTONS, ro_toolbar_menu_buttons_tick(toolbar) && toolbar->buttons != NULL); ro_gui_menu_set_entry_shaded(menu, TOOLBAR_ADDRESS_BAR, ro_toolbar_menu_edit_shade(toolbar) || toolbar->url == NULL); ro_gui_menu_set_entry_ticked(menu, TOOLBAR_ADDRESS_BAR, ro_toolbar_menu_url_tick(toolbar) && toolbar->url != NULL); ro_gui_menu_set_entry_shaded(menu, TOOLBAR_THROBBER, ro_toolbar_menu_edit_shade(toolbar) || toolbar->throbber == NULL); ro_gui_menu_set_entry_ticked(menu, TOOLBAR_THROBBER, ro_toolbar_menu_throbber_tick(toolbar) && toolbar->throbber != NULL); return true; } /** * Handle submenu warnings for the toolbar menu * * \param w The window owning the menu. * \param i The icon owning the menu. * \param *menu The menu to which the warning applies. * \param *selection The wimp menu selection data. * \param action The selected menu action. */ void ro_toolbar_menu_warning(wimp_w w, wimp_i i, wimp_menu *menu, wimp_selection *selection, menu_action action) { /* Do nothing */ } /** * Handle selections from the toolbar menu * * \param w The window owning the menu. * \param i The icon owning the menu. * \param *menu The menu from which the selection was made. * \param *selection The wimp menu selection data. * \param action The selected menu action. * \return true if action accepted; else false. */ bool ro_toolbar_menu_select(wimp_w w, wimp_i i, wimp_menu *menu, wimp_selection *selection, menu_action action) { struct toolbar *toolbar; toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(w); if (toolbar == NULL) return false; /* Pass the event on to potentially interested widgets. */ if (toolbar->url != NULL && ro_gui_url_bar_menu_select(toolbar->url, i, menu, selection, action)) return true; /* Try to process the event as a toolbar menu. */ if (menu != toolbar_menu) return false; switch (action) { case TOOLBAR_BUTTONS: ro_toolbar_set_display_buttons(toolbar, !ro_toolbar_get_display_buttons(toolbar)); break; case TOOLBAR_ADDRESS_BAR: ro_toolbar_set_display_url(toolbar, !ro_toolbar_get_display_url(toolbar)); if (ro_toolbar_get_display_url(toolbar)) ro_toolbar_take_caret(toolbar); break; case TOOLBAR_THROBBER: ro_toolbar_set_display_throbber(toolbar, !ro_toolbar_get_display_throbber(toolbar)); break; case TOOLBAR_EDIT: ro_toolbar_toggle_edit(toolbar); break; default: return false; } return true; } /** * Translate the contents of a message_HELP_REQUEST into a suffix for a * NetSurf message token. The help system will then add this to whatever * prefix the current toolbar has registered with WimpEvent. * * \param w The window handle under the mouse. * \param i The icon handle under the mouse. * \param *pos The mouse position. * \param buttons The mouse button state. * \return The required help token suffix. */ const char *ro_toolbar_get_help_suffix(wimp_w w, wimp_i i, os_coord *pos, wimp_mouse_state buttons) { struct toolbar *toolbar; wimp_window_state state; os_error *error; const char *suffix; toolbar = (struct toolbar *) ro_gui_wimp_event_get_user_data(w); if (toolbar == NULL || toolbar->toolbar_handle != w) return NULL; state.w = toolbar->toolbar_handle; 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 NULL; } /* Pass the help request around the toolbar widgets. */ if (toolbar->throbber != NULL && toolbar->throbber_display && ro_gui_throbber_help_suffix(toolbar->throbber, i, pos, &state, buttons, &suffix)) return suffix; if (toolbar->url != NULL && toolbar->url_display && ro_gui_url_bar_help_suffix(toolbar->url, i, pos, &state, buttons, &suffix)) return suffix; if (toolbar->buttons != NULL && toolbar->buttons_display && ro_gui_button_bar_help_suffix(toolbar->buttons, i, pos, &state, buttons, &suffix)) return suffix; return ""; } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_update_client_data(struct toolbar *toolbar, void *client_data) { if (toolbar != NULL) toolbar->client_data = client_data; } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_update_all_buttons(void) { struct toolbar *bar; bar = ro_toolbar_bars; while (bar != NULL) { ro_toolbar_update_buttons(bar); bar = bar->next; } } /** * Update the state of a toolbar's buttons. * * \param toolbar the toolbar to update */ void ro_toolbar_update_buttons(struct toolbar *toolbar) { assert(toolbar != NULL); if (toolbar->callbacks != NULL && toolbar->callbacks->update_buttons != NULL) toolbar->callbacks->update_buttons(toolbar->client_data); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_refresh(struct toolbar *toolbar) { assert(toolbar != NULL); ro_toolbar_process(toolbar, -1, true); if (toolbar->callbacks != NULL && toolbar->callbacks->change_size != NULL) toolbar->callbacks->change_size(toolbar->client_data); if (toolbar->toolbar_handle != NULL) xwimp_force_redraw(toolbar->toolbar_handle, 0, 0, toolbar->current_width, toolbar->current_height); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_theme_update(void) { struct toolbar *bar, *next; bool ok; bar = ro_toolbar_bars; while (bar != NULL) { /* Take the next bar address now, as *bar may become invalid * during the update process (if an update fails and * ro_toolbar_destroy() is called) and we don't want to lose * the link to the rest of the chain. */ next = bar->next; /* Only process the bar if the theme is set to the default. * Otherwise, it's up to the owner to do whatever they need * to do for themselves. */ if (bar->theme == NULL) { ok = ro_toolbar_rebuild(bar); if (!ok) ro_toolbar_destroy(bar); } else { ok = true; } if (bar->callbacks != NULL && bar->callbacks->theme_update != NULL) bar->callbacks->theme_update(bar->client_data, ok); bar = next; } } /* This is an exported interface documented in toolbar.h */ struct toolbar *ro_toolbar_parent_window_lookup(wimp_w w) { struct toolbar *toolbar; toolbar = ro_toolbar_bars; while (toolbar != NULL && toolbar->parent_handle != w) toolbar = toolbar->next; return toolbar; } /* This is an exported interface documented in toolbar.h */ struct toolbar *ro_toolbar_window_lookup(wimp_w w) { struct toolbar *toolbar; toolbar = ro_toolbar_bars; while (toolbar != NULL && toolbar->toolbar_handle != w) toolbar = toolbar->next; return toolbar; } /* This is an exported interface documented in toolbar.h */ wimp_w ro_toolbar_get_parent_window(struct toolbar *toolbar) { return (toolbar != NULL) ? toolbar->parent_handle : 0; } /* This is an exported interface documented in toolbar.h */ wimp_w ro_toolbar_get_window(struct toolbar *toolbar) { return (toolbar != NULL) ? toolbar->toolbar_handle : 0; } /* This is an exported interface documented in toolbar.h */ int ro_toolbar_height(struct toolbar *toolbar) { return (toolbar == NULL) ? 0 : toolbar->current_height; } /* This is an exported interface documented in toolbar.h */ int ro_toolbar_full_height(struct toolbar *toolbar) { return (toolbar == NULL) ? 0 : toolbar->full_height; } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_start_throbbing(struct toolbar *toolbar) { if (toolbar != NULL && toolbar->throbber != NULL) ro_gui_throbber_animate(toolbar->throbber); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_stop_throbbing(struct toolbar *toolbar) { if (toolbar != NULL && toolbar->throbber != NULL) ro_gui_throbber_stop(toolbar->throbber); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_throb(struct toolbar *toolbar) { if (toolbar != NULL && toolbar->throbber != NULL) ro_gui_throbber_animate(toolbar->throbber); } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_set_button_order(struct toolbar *toolbar, char order[]) { if (toolbar == NULL || toolbar->buttons == NULL) return false; if (!ro_gui_button_bar_arrange_buttons(toolbar->buttons, order)) return false; ro_toolbar_refresh_widget_dimensions(toolbar); return ro_toolbar_process(toolbar, -1, true); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_set_button_shaded_state(struct toolbar *toolbar, button_bar_action action, bool shaded) { if (toolbar == NULL || toolbar->buttons == NULL) return; ro_gui_button_bar_shade_button(toolbar->buttons, action, shaded); } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_take_caret(struct toolbar *toolbar) { if (toolbar == NULL || toolbar->url == NULL || !toolbar->url_display) return false; return ro_gui_url_bar_take_caret(toolbar->url); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_set_url(struct toolbar *toolbar, const char *url, bool is_utf8, bool set_caret) { if (toolbar != NULL && toolbar->url != NULL) ro_gui_url_bar_set_url(toolbar->url, url, is_utf8, set_caret); } /* This is an exported interface documented in toolbar.h */ const char *ro_toolbar_get_url(struct toolbar *toolbar) { if (toolbar == NULL || toolbar->url == NULL) return NULL; return ro_gui_url_bar_get_url(toolbar->url); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_update_all_hotlists(void) { struct toolbar *bar; bar = ro_toolbar_bars; while (bar != NULL) { ro_toolbar_update_hotlist(bar); bar = bar->next; } } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_update_hotlist(struct toolbar *toolbar) { if (toolbar == NULL || toolbar->url == NULL) return; ro_gui_url_bar_update_hotlist(toolbar->url); } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_get_url_field_extent(struct toolbar *toolbar, os_box *extent) { if (toolbar == NULL || toolbar->url == NULL) return false; if (extent == NULL) return true; return ro_gui_url_bar_get_url_extent(toolbar->url, extent); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_set_site_favicon(struct toolbar *toolbar, struct hlcache_handle *h) { if (toolbar == NULL || toolbar->url == NULL) return; ro_gui_url_bar_set_site_favicon(toolbar->url, h); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_set_content_favicon(struct toolbar *toolbar, struct gui_window *g) { if (toolbar == NULL || toolbar->url == NULL) return; ro_gui_url_bar_set_content_favicon(toolbar->url, g); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_update_urlsuggest(struct toolbar *toolbar) { if (toolbar == NULL || toolbar->url == NULL) return; ro_gui_url_bar_update_urlsuggest(toolbar->url); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_set_display_buttons(struct toolbar *toolbar, bool display) { if (toolbar == NULL || toolbar->buttons == NULL) return; toolbar->buttons_display = display; ro_gui_button_bar_hide(toolbar->buttons, !display); ro_toolbar_refresh_widget_dimensions(toolbar); ro_toolbar_refresh(toolbar); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_set_display_url(struct toolbar *toolbar, bool display) { if (toolbar == NULL || toolbar->url == NULL) return; toolbar->url_display = display; ro_gui_url_bar_hide(toolbar->url, !display); ro_toolbar_refresh_widget_dimensions(toolbar); ro_toolbar_refresh(toolbar); } /* This is an exported interface documented in toolbar.h */ void ro_toolbar_set_display_throbber(struct toolbar *toolbar, bool display) { if (toolbar == NULL || toolbar->throbber == NULL) return; toolbar->throbber_display = display; ro_gui_throbber_hide(toolbar->throbber, !display); ro_toolbar_refresh_widget_dimensions(toolbar); ro_toolbar_refresh(toolbar); } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_get_display_buttons(struct toolbar *toolbar) { return (toolbar == NULL || toolbar->buttons == NULL) ? false : toolbar->buttons_display; } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_get_display_url(struct toolbar *toolbar) { return (toolbar == NULL || toolbar->url == NULL) ? false : toolbar->url_display; } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_get_display_throbber(struct toolbar *toolbar) { return (toolbar == NULL || toolbar->throbber == NULL) ? false : toolbar->throbber_display; } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_get_editing(struct toolbar *toolbar) { return (toolbar == NULL || !toolbar->editing) ? false : true; } /* This is an exported interface documented in toolbar.h */ bool ro_toolbar_toggle_edit(struct toolbar *toolbar) { if (toolbar == NULL || toolbar->editor == NULL) return false; toolbar->editing = !toolbar->editing; ro_gui_button_bar_hide(toolbar->editor, !toolbar->editing); ro_gui_button_bar_hide(toolbar->buttons, !toolbar->buttons_display && !toolbar->editing); if (!ro_toolbar_rebuild(toolbar)) { ro_toolbar_destroy(toolbar); return false; } ro_toolbar_refresh(toolbar); /* If there's a callback registered and an edit has finished, * tell out client what the new button state is. */ if (!toolbar->editing && toolbar->buttons != NULL && toolbar->callbacks != NULL && toolbar->callbacks->save_buttons != NULL) { char *new_buttons; new_buttons = ro_gui_button_bar_get_config(toolbar->buttons); toolbar->callbacks->save_buttons(toolbar->client_data, new_buttons); } return true; }