/* * 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 2004 Richard Wilson */ /** \file * Window themes and toolbars (implementation). */ #include #include #include #include #include #include "oslib/os.h" #include "oslib/osgbpb.h" #include "oslib/osgbpb.h" #include "oslib/osfile.h" #include "oslib/osfind.h" #include "oslib/osspriteop.h" #include "oslib/squash.h" #include "oslib/wimp.h" #include "oslib/wimpextend.h" #include "oslib/wimpspriteop.h" #include "netsurf/riscos/gui.h" #include "netsurf/riscos/theme.h" #include "netsurf/riscos/wimp.h" #include "netsurf/utils/log.h" #include "netsurf/utils/utils.h" #define THEME_URL_MEMORY 256 #define THEME_THROBBER_MEMORY 12 #define THEME_STATUS_MEMORY 256 struct toolbar_icon { int icon_number; /**< wimp icon number */ bool display; /**< whether to display the icon */ int width; /**< icon width */ int height; /**< icon height */ char name[12]; /**< icon name */ char validation[40]; /**< validation string */ struct toolbar_icon *next; /**< next toolbar icon, or NULL for no more */ }; struct theme_file_header { unsigned int magic_value; unsigned int parser_version; char name[32]; char author[64]; char browser_bg; char hotlist_bg; char status_bg; char status_fg; char throbber_left; char future_expansion_1; char future_expansion_2; char future_expansion_3; unsigned int compressed_sprite_size; unsigned int decompressed_sprite_size; }; static struct theme_descriptor *theme_current = NULL; static struct theme_descriptor *theme_descriptors = NULL; static void ro_gui_theme_free(struct theme_descriptor *descriptor, bool list); static void ro_gui_theme_add_toolbar_icon(struct toolbar *toolbar, const char *name, int icon_number); static void ro_gui_theme_update_toolbar_icon(struct toolbar *toolbar, struct toolbar_icon *icon); static void ro_gui_theme_destroy_toolbar_icon(struct toolbar_icon *icon); /* A basic window for the toolbar and status */ static wimp_window theme_toolbar_window = { {0, 0, 16384, 16384}, 0, 0, wimp_TOP, wimp_WINDOW_NEW_FORMAT | wimp_WINDOW_MOVEABLE | wimp_WINDOW_AUTO_REDRAW | wimp_WINDOW_FURNITURE_WINDOW, 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+ - greyed icons detected for interactive help */, {0, 0, 16384, 16384}, 0, 0, wimpspriteop_AREA, 12, 1, {""}, 0/*, { } */ }; /* Shared icon validation */ static char theme_url_validation[] = "Pptr_write\0"; static char theme_resize_validation[] = "R1;Pptr_lr,8,6\0"; static char theme_null_text_string[] = "\0"; /** * Initialise the theme handler */ void ro_gui_theme_initialise(void) { /* Get an initial theme list */ theme_descriptors = ro_gui_theme_get_available(); } /** * Finalise the theme handler */ void ro_gui_theme_finalise(void) { /* Free all closed descriptors */ ro_gui_theme_close(theme_current, false); ro_gui_theme_free(theme_descriptors, true); } /** * Finds a theme from the cached values. * * The returned theme is only guaranteed to be valid until the next call * to ro_gui_theme_get_available() unless it has been opened using * ro_gui_theme_open(). * * \param filename the filename of the theme_descriptor to return * \return the requested theme_descriptor, or NULL if not found */ struct theme_descriptor *ro_gui_theme_find(const char *filename) { struct theme_descriptor *descriptor; /* Check for bad filename */ if (!filename) return NULL; /* Work through until we find our required filename */ descriptor = theme_descriptors; while (descriptor) { if (!strcmp(filename, descriptor->filename)) return descriptor; descriptor = descriptor->next; } return NULL; } /** * Reads and caches the currently available themes. * * \return the requested theme_descriptor, or NULL if not found */ struct theme_descriptor *ro_gui_theme_get_available(void) { struct theme_file_header file_header; struct theme_descriptor *current; struct theme_descriptor *test; char pathname[256]; int context = 0; int read_count; osgbpb_INFO(100) info; int output_left; os_fw file_handle; os_error *error; /* Close any descriptors we've got so far */ ro_gui_theme_free(theme_descriptors, true); /* Create a new set */ while (context != -1) { /* Get the next entry */ error = xosgbpb_dir_entries_info(THEMES_DIR, (osgbpb_info_list *) &info, 1, context, sizeof(info), 0, &read_count, &context); if (error) { LOG(("xosgbpb_dir_entries_info: 0x%x: %s", error->errnum, error->errmess)); warn_user("MiscError", error->errmess); break; } /* Check if we've read anything */ if (read_count == 0) continue; /* Only process files */ if ((info.obj_type == fileswitch_IS_FILE) && (!ro_gui_theme_find(info.name))) { /* Get our full filename */ snprintf(pathname, sizeof pathname, "%s.%s", THEMES_DIR, info.name); pathname[sizeof pathname - 1] = 0; /* Get the header */ error = xosfind_openinw(osfind_NO_PATH, pathname, 0, &file_handle); if (error) { LOG(("xosfind_openinw: 0x%x: %s", error->errnum, error->errmess)); warn_user("FileError", error->errmess); continue; } if (file_handle == 0) continue; error = xosgbpb_read_atw(file_handle, (char *)&file_header, sizeof (struct theme_file_header), 0, &output_left); xosfind_closew(file_handle); if (error) { LOG(("xosbgpb_read_atw: 0x%x: %s", error->errnum, error->errmess)); warn_user("FileError", error->errmess); continue; } if (output_left > 0) continue; /* should try to read more? */ /* Check we are a valid theme */ if ((file_header.magic_value != 0x4d54534e) || (file_header.parser_version > 1)) continue; /* Create a new theme descriptor */ current = (struct theme_descriptor *)calloc(1, sizeof(struct theme_descriptor)); if (!current) { LOG(("calloc failed")); warn_user("NoMemory", 0); return theme_descriptors; } current->filename = malloc(strlen(info.name) + 1); if (!current->filename) { LOG(("malloc failed")); warn_user("NoMemory", 0); free(current); return theme_descriptors; } strcpy(current->filename, info.name); strcpy(current->name, file_header.name); strcpy(current->author, file_header.author); current->browser_background = file_header.browser_bg; current->hotlist_background = file_header.hotlist_bg; current->status_background = file_header.status_bg; current->status_foreground = file_header.status_fg; current->throbber_right = (file_header.throbber_left == 0x00); current->decompressed_size = file_header.decompressed_sprite_size; current->compressed_size = file_header.compressed_sprite_size; /* Link in our new descriptor alphabetically */ if (theme_descriptors) { current->next = theme_descriptors; theme_descriptors->previous = current; } theme_descriptors = current; } } /* Sort alphabetically in a very rubbish way */ if (theme_descriptors->next) { current = theme_descriptors; while ((test = current->next)) { if (strcmp(current->name, test->name) > 0) { current->next->previous = current->previous; if (current->previous) current->previous->next = current->next; current->next = test->next; test->next = current; current->previous = test; if (current->next) current->next->previous = current; current = test->previous; if (!current) current = test; } else { current = current->next; } } while (theme_descriptors->previous) theme_descriptors = theme_descriptors->previous; } return theme_descriptors; } /** * Opens a theme ready for use. * * \param descriptor the theme_descriptor to open * \param list whether to open all themes in the list * \return whether the operation was successful */ bool ro_gui_theme_open(struct theme_descriptor *descriptor, bool list) { fileswitch_object_type obj_type; squash_output_status status; os_coord dimensions; os_mode mode; os_error *error; char pathname[256]; bool result = true; int i, n; int workspace_size, file_size; char *raw_data, *workspace; osspriteop_area *decompressed; /* If we are freeing the whole of the list then we need to start at the first descriptor. */ if (list) { while (descriptor->previous) descriptor = descriptor->previous; } /* Open the themes */ while (descriptor) { /* If we are already loaded, increase the usage count */ if (descriptor->theme) { descriptor->theme->users = descriptor->theme->users + 1; } else if (descriptor->decompressed_size > 0) { /* Create a new theme */ descriptor->theme = (struct theme *)calloc(1, sizeof(struct theme)); if (!descriptor->theme) { LOG(("calloc failed")); warn_user("NoMemory", 0); return false; } descriptor->theme->users = 1; /* Get our full filename */ snprintf(pathname, sizeof pathname, "%s.%s", THEMES_DIR, descriptor->filename); pathname[sizeof pathname - 1] = 0; /* Load the file. We use a goto to exit from here on in as using a continue leaves us in an infinite loop - it's nasty, and really should be rewritten properly. */ error = xosfile_read_stamped_no_path(pathname, &obj_type, 0, 0, &file_size, 0, 0); if (error) { LOG(("xosfile_read_stamped_no_path: 0x%x: %s", error->errnum, error->errmess)); warn_user("FileError", error->errmess); goto ro_gui_theme_open_continue; } if (obj_type != fileswitch_IS_FILE) goto ro_gui_theme_open_continue; raw_data = malloc(file_size); if (!raw_data) { LOG(("No memory for malloc()")); warn_user("NoMemory", 0); return false; } error = xosfile_load_stamped_no_path(pathname, (byte *)raw_data, 0, 0, 0, 0, 0); if (error) { free(raw_data); LOG(("xosfile_load_stamped_no_path: 0x%x: %s", error->errnum, error->errmess)); warn_user("FileError", error->errmess); goto ro_gui_theme_open_continue; } /* Decompress the sprites */ error = xsquash_decompress_return_sizes(-1, &workspace_size, 0); if (error) { free(raw_data); LOG(("xsquash_decompress_return_sizes: 0x%x: %s", error->errnum, error->errmess)); warn_user("MiscError", error->errmess); goto ro_gui_theme_open_continue; } decompressed = (osspriteop_area *)malloc(descriptor->decompressed_size); workspace = malloc(workspace_size); if ((!decompressed) || (!workspace)) { if (decompressed) free(decompressed); if (workspace) free(raw_data); LOG(("No memory for malloc()")); warn_user("NoMemory", 0); return false; } error = xsquash_decompress(squash_INPUT_ALL_PRESENT, workspace, (byte *)(raw_data + sizeof(struct theme_file_header)), descriptor->compressed_size, (byte *)decompressed, descriptor->decompressed_size, &status, 0, 0, 0, 0); free(workspace); free(raw_data); if (error) { free(decompressed); LOG(("xsquash_decompress: 0x%x: %s", error->errnum, error->errmess)); warn_user("MiscError", error->errmess); goto ro_gui_theme_open_continue; } if (status != 0) { free(decompressed); goto ro_gui_theme_open_continue; } descriptor->theme->sprite_area = decompressed; /* Find the highest sprite called 'throbber%i', and get the maximum dimensions for all 'thobber%i' icons. */ for (i = 1; i <= descriptor->theme->sprite_area->sprite_count; i++) { osspriteop_return_name(osspriteop_USER_AREA, descriptor->theme->sprite_area, pathname, 12, i); if (strncmp(pathname, "throbber", 8) == 0) { /* Get the max sprite width/height */ xosspriteop_read_sprite_info(osspriteop_USER_AREA, descriptor->theme->sprite_area, (osspriteop_id)pathname, &dimensions.x, &dimensions.y, (osbool *)0, &mode); ro_convert_pixels_to_os_units(&dimensions, mode); if (dimensions.x > descriptor->theme->throbber_width) descriptor->theme->throbber_width = dimensions.x; if (dimensions.y > descriptor->theme->throbber_height) descriptor->theme->throbber_height = dimensions.y; /* Get the throbber number */ n = atoi(pathname + 8); if (descriptor->theme->throbber_frames < n) descriptor->theme->throbber_frames = n; } } } ro_gui_theme_open_continue: /* Loop or return depending on whether the entire list is to be processed. */ if (list) { descriptor = descriptor->next; } else { return result; } } return result; } /** * Applies the theme to all current windows and subsequent ones. * * \param descriptor the theme_descriptor to open * \return whether the operation was successful */ bool ro_gui_theme_apply(struct theme_descriptor *descriptor) { struct theme_descriptor *theme_previous; /* Check if the theme is already applied */ if (descriptor == theme_current) return true; /* Re-open the new-theme and release the current theme */ if (!ro_gui_theme_open(descriptor, false)) { /* The error has already been reported */ return false; } theme_previous = theme_current; theme_current = descriptor; /* Apply the theme to all the current windows */ ro_gui_window_update_theme(); /* Release the previous theme */ ro_gui_theme_close(theme_previous, false); return true; } /** * Closes a theme after use. * * \param descriptor the theme_descriptor to close * \param list whether to open all themes in the list * \return whether the operation was successful */ void ro_gui_theme_close(struct theme_descriptor *descriptor, bool list) { /* We might not have created any descriptors yet to close. */ if (!descriptor) return; /* If we are freeing the whole of the list then we need to start at the first descriptor. */ if (list) { while (descriptor->previous) descriptor = descriptor->previous; } /* Close the themes */ while (descriptor) { /* Lower the theme usage count */ if (descriptor->theme) { descriptor->theme->users = descriptor->theme->users - 1; if (descriptor->theme->users <= 0) { free(descriptor->theme->sprite_area); free(descriptor->theme); descriptor->theme = NULL; } } /* Loop or return depending on whether the entire list is to be processed. */ if (list) { descriptor = descriptor->next; } else { return; } } } /** * Frees any unused theme descriptors. * * \param descriptor the theme_descriptor to free * \param list whether to open all themes in the list * \return whether the operation was successful */ void ro_gui_theme_free(struct theme_descriptor *descriptor, bool list) { struct theme_descriptor *next_descriptor; /* We might not have created any descriptors yet to close. */ if (!descriptor) return; /* If we are freeing the whole of the list then we need to start at the first descriptor. */ if (list) { while (descriptor->previous) descriptor = descriptor->previous; } /* Close the themes */ while (descriptor) { /* Remember where we are going next */ next_descriptor = descriptor->next; /* If we have no loaded theme then we can kill the descriptor */ if (!descriptor->theme) { /* De-link the descriptor */ if (descriptor->previous) descriptor->previous->next = descriptor->next; if (descriptor->next) descriptor->next->previous = descriptor->previous; /* Keep the cached list in sync */ if (theme_descriptors == descriptor) theme_descriptors = next_descriptor; /* Release any memory */ free(descriptor->filename); free(descriptor); } /* Loop or return depending on whether the entire list is to be processed. */ if (list) { descriptor = next_descriptor; } else { return; } } } /** * Creates a toolbar. * * \param descriptor the theme to use, or NULL for current * \param type the toolbar type * \return a new toolbar, or NULL for failure */ struct toolbar *ro_gui_theme_create_toolbar(struct theme_descriptor *descriptor, toolbar_type type) { struct toolbar *toolbar; /* Create a new toolbar */ toolbar = calloc(sizeof(struct toolbar), 1); if (!toolbar) { LOG(("No memory for malloc()")); warn_user("NoMemory", 0); return NULL; } toolbar->type = type; /* Store the theme */ if (!descriptor) descriptor = theme_current; toolbar->descriptor = descriptor; /* Apply the default settings */ toolbar->display_buttons = true; toolbar->toolbar_current = 16384; switch (type) { case THEME_BROWSER_TOOLBAR: toolbar->display_url = true; toolbar->display_throbber = true; toolbar->display_status = true; ro_gui_theme_add_toolbar_icon(toolbar, "back", ICON_TOOLBAR_BACK); ro_gui_theme_add_toolbar_icon(toolbar, "forward", ICON_TOOLBAR_FORWARD); ro_gui_theme_add_toolbar_icon(toolbar, "stop", ICON_TOOLBAR_STOP); ro_gui_theme_add_toolbar_icon(toolbar, "reload", ICON_TOOLBAR_RELOAD); ro_gui_theme_add_toolbar_icon(toolbar, "home", ICON_TOOLBAR_HOME); ro_gui_theme_add_toolbar_icon(toolbar, NULL, -1); ro_gui_theme_add_toolbar_icon(toolbar, "search", ICON_TOOLBAR_SEARCH); ro_gui_theme_add_toolbar_icon(toolbar, "history", ICON_TOOLBAR_HISTORY); ro_gui_theme_add_toolbar_icon(toolbar, "scale", ICON_TOOLBAR_SCALE); ro_gui_theme_add_toolbar_icon(toolbar, NULL, -1); ro_gui_theme_add_toolbar_icon(toolbar, "hotlist", ICON_TOOLBAR_BOOKMARK); ro_gui_theme_add_toolbar_icon(toolbar, "save", ICON_TOOLBAR_SAVE); ro_gui_theme_add_toolbar_icon(toolbar, "print", ICON_TOOLBAR_PRINT); break; case THEME_HOTLIST_TOOLBAR: ro_gui_theme_add_toolbar_icon(toolbar, "create", ICON_TOOLBAR_CREATE); ro_gui_theme_add_toolbar_icon(toolbar, "delete", ICON_TOOLBAR_DELETE); ro_gui_theme_add_toolbar_icon(toolbar, "launch", ICON_TOOLBAR_LAUNCH); ro_gui_theme_add_toolbar_icon(toolbar, NULL, -1); ro_gui_theme_add_toolbar_icon(toolbar, "open", ICON_TOOLBAR_OPEN); ro_gui_theme_add_toolbar_icon(toolbar, "expand", ICON_TOOLBAR_EXPAND); break; } /* Claim the memory for our Wimp indirection */ if (type == THEME_BROWSER_TOOLBAR) { toolbar->url_buffer = calloc(1, THEME_URL_MEMORY + THEME_THROBBER_MEMORY + THEME_STATUS_MEMORY); if (!toolbar->url_buffer) { LOG(("No memory for calloc()")); ro_gui_theme_destroy_toolbar(toolbar); return NULL; } toolbar->throbber_buffer = toolbar->url_buffer + THEME_URL_MEMORY; toolbar->status_buffer = toolbar->throbber_buffer + THEME_THROBBER_MEMORY; sprintf(toolbar->throbber_buffer, "throbber0"); } /* Apply the desired theme to the toolbar */ if (!ro_gui_theme_update_toolbar(descriptor, toolbar)) { ro_gui_theme_destroy_toolbar(toolbar); return NULL; } return toolbar; } /** * Updates a toolbar to use a particular theme. * The toolbar may be unstable on failure and should be destroyed. * * \param descriptor the theme to use, or NULL for current * \param toolbar the toolbar to update * \return whether the operation was successful */ bool ro_gui_theme_update_toolbar(struct theme_descriptor *descriptor, struct toolbar *toolbar) { wimp_icon_create new_icon; os_error *error; osspriteop_area *sprite_area; struct toolbar_icon *toolbar_icon; int width; if (!toolbar) return false; /* Set the theme and window sprite area */ if (!descriptor) descriptor = theme_current; toolbar->descriptor = descriptor; if ((toolbar->descriptor) && (toolbar->descriptor->theme)) { sprite_area = toolbar->descriptor->theme->sprite_area; } else { sprite_area = (osspriteop_area *)1; } theme_toolbar_window.sprite_area = sprite_area; /* Update the icon sizes */ toolbar_icon = toolbar->icon; while (toolbar_icon) { ro_gui_theme_update_toolbar_icon(toolbar, toolbar_icon); toolbar_icon = toolbar_icon->next; } /* Recreate the toolbar window */ if (toolbar->descriptor) { if (toolbar->type == THEME_BROWSER_TOOLBAR) { theme_toolbar_window.work_bg = toolbar->descriptor->browser_background; } else { theme_toolbar_window.work_bg = toolbar->descriptor->hotlist_background; } } else { theme_toolbar_window.work_bg = wimp_COLOUR_VERY_LIGHT_GREY; } theme_toolbar_window.flags |= wimp_WINDOW_NO_BOUNDS; theme_toolbar_window.xmin = 1; theme_toolbar_window.ymin = 1; theme_toolbar_window.extent.x1 = 16384; theme_toolbar_window.extent.y1 = 16384; if (toolbar->toolbar_handle) { xwimp_delete_window(toolbar->toolbar_handle); toolbar->toolbar_handle = NULL; } error = xwimp_create_window(&theme_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; } LOG(("Created window 1")); /* Create the basic icons */ int max_icon = ICON_TOOLBAR_URL; if (toolbar->type == THEME_HOTLIST_TOOLBAR) max_icon = ICON_TOOLBAR_HOTLIST_LAST; new_icon.w = toolbar->toolbar_handle; new_icon.icon.data.indirected_text.size = 1; new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_INDIRECTED | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED | (wimp_BUTTON_CLICK << wimp_ICON_BUTTON_TYPE_SHIFT); if (toolbar->descriptor) { new_icon.icon.flags |= (toolbar->descriptor->browser_background << wimp_ICON_BG_COLOUR_SHIFT); } else { new_icon.icon.flags |= (wimp_COLOUR_VERY_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT); } for (int i = 0; i < max_icon; i++) { new_icon.icon.data.indirected_text.text = theme_null_text_string; toolbar_icon = toolbar->icon; while (toolbar_icon) { if (toolbar_icon->icon_number == i) { new_icon.icon.data.indirected_text.validation = toolbar_icon->validation; break; } else { toolbar_icon = toolbar_icon->next; } } error = xwimp_create_icon(&new_icon, 0); if (error) { LOG(("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return false; } } /* Create the URL/throbber icons */ if (toolbar->type == THEME_BROWSER_TOOLBAR) { new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED | wimp_ICON_VCENTRED | wimp_ICON_BORDER | wimp_ICON_FILLED | (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | (wimp_BUTTON_WRITE_CLICK_DRAG << wimp_ICON_BUTTON_TYPE_SHIFT); new_icon.icon.data.indirected_text.text = toolbar->url_buffer; new_icon.icon.data.indirected_text.validation = theme_url_validation; new_icon.icon.data.indirected_text.size = THEME_URL_MEMORY; error = xwimp_create_icon(&new_icon, 0); if (error) { LOG(("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return false; } /* Now the throbber */ new_icon.icon.flags = wimp_ICON_SPRITE | wimp_ICON_INDIRECTED | wimp_ICON_HCENTRED | wimp_ICON_VCENTRED; new_icon.icon.data.indirected_sprite.id = (osspriteop_id)toolbar->throbber_buffer; new_icon.icon.data.indirected_sprite.area = sprite_area; new_icon.icon.data.indirected_sprite.size = THEME_THROBBER_MEMORY; error = xwimp_create_icon(&new_icon, 0); if (error) { LOG(("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return false; } } if (toolbar->parent_handle) { ro_gui_theme_attach_toolbar(toolbar, toolbar->parent_handle); } /* Recreate the status window */ if (toolbar->type == THEME_BROWSER_TOOLBAR) { /* Delete the old window and create a new one */ if (toolbar->status_handle) { xwimp_delete_window(toolbar->status_handle); toolbar->status_handle = NULL; } if (toolbar->descriptor) { theme_toolbar_window.work_bg = toolbar->descriptor->status_background; } else { theme_toolbar_window.work_bg = wimp_COLOUR_VERY_LIGHT_GREY; } theme_toolbar_window.flags &= ~wimp_WINDOW_NO_BOUNDS; theme_toolbar_window.xmin = 12; theme_toolbar_window.ymin = ro_get_hscroll_height((wimp_w)0) - 4; theme_toolbar_window.extent.y1 = theme_toolbar_window.ymin; error = xwimp_create_window(&theme_toolbar_window, &toolbar->status_handle); if (error) { LOG(("xwimp_create_window: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return false; } /* Create the status resize icon */ new_icon.w = toolbar->status_handle; new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED | wimp_ICON_BORDER | wimp_ICON_FILLED | (wimp_COLOUR_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT) | (wimp_BUTTON_CLICK_DRAG << wimp_ICON_BUTTON_TYPE_SHIFT); new_icon.icon.data.indirected_text.text = theme_null_text_string; new_icon.icon.data.indirected_text.validation = theme_resize_validation; new_icon.icon.data.indirected_text.size = 1; error = xwimp_create_icon(&new_icon, 0); if (error) { LOG(("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return false; } /* And finally our status display icon */ new_icon.icon.flags = wimp_ICON_TEXT | wimp_ICON_INDIRECTED | wimp_ICON_VCENTRED; if (toolbar->descriptor) { new_icon.icon.flags |= (toolbar->descriptor->status_foreground << wimp_ICON_FG_COLOUR_SHIFT) | (toolbar->descriptor->status_background << wimp_ICON_BG_COLOUR_SHIFT); } else { new_icon.icon.flags |= (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | (wimp_COLOUR_VERY_LIGHT_GREY << wimp_ICON_BG_COLOUR_SHIFT); } new_icon.icon.data.indirected_text.text = toolbar->status_buffer; new_icon.icon.data.indirected_text.validation = theme_null_text_string; new_icon.icon.data.indirected_text.size = THEME_STATUS_MEMORY; error = xwimp_create_icon(&new_icon, 0); if (error) { LOG(("xwimp_create_icon: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return false; } } /* Force a re-processing of the toolbar */ width = toolbar->toolbar_current; toolbar->reformat_buttons = true; toolbar->toolbar_current = -1; toolbar->status_current = -1; ro_gui_theme_process_toolbar(toolbar, width); return true; } /** * Attaches a toolbar to a window. * * \param toolbar the toolbar to update * \param parent the window to contain the toolbar * \return whether the operation was successful */ bool ro_gui_theme_attach_toolbar(struct toolbar *toolbar, wimp_w parent) { wimp_outline outline; wimp_window_state state; if (!toolbar) return false; /* Attach/close the window */ toolbar->parent_handle = parent; if (toolbar->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 - toolbar->height + 2; state.xscroll = 0; state.yscroll = 0; xwimp_open_window_nested((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); } else { xwimp_close_window(toolbar->toolbar_handle); } return true; } /** * Updates the toolbars status bar settings to reflect the current size * * \param toolbar the toolbar to update */ void ro_gui_theme_resize_toolbar_status(struct toolbar *toolbar) { os_error *error; wimp_outline outline; wimp_window_state state; wimp_w parent = NULL; int parent_size, status_size; if ((!toolbar) || (!toolbar->parent_handle)) return; /* Get the width to scale to */ parent = toolbar->parent_handle; 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; } parent_size = outline.outline.x1 - outline.outline.x0 - ro_get_vscroll_width(parent) - 2; /* Get the current size */ state.w = toolbar->status_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; } status_size = state.visible.x1 - state.visible.x0; /* Store the new size */ toolbar->status_width = (10000 * status_size) / parent_size; if (toolbar->status_width > 10000) toolbar->status_width = 10000; ro_gui_theme_process_toolbar(toolbar, -1); } /** * Updates the toolbar to reflect changes to the icon flags and any reformatting * required due to the change in parent window size. * * \param toolbar the toolbar to update * \param width a specific width to resize to, or -1 to use parent width * \return whether the operation was successful */ bool ro_gui_theme_process_toolbar(struct toolbar *toolbar, int width) { wimp_caret caret; os_box extent = { 0, 0, 0, 0 }; os_error *error; wimp_w parent = NULL; wimp_outline outline; wimp_window_state state; int throbber_x = -1; int status_max; int left_edge, right_edge, bottom_edge; int status_size = 0; int status_height = 0; if (!toolbar) return false; int old_height = toolbar->height; int old_width = toolbar->toolbar_current; struct toolbar_icon *toolbar_icon; bool visible_icon = false; /* Find the parent window handle if we need to process the status window, or the caller has requested we calculate the width ourself. */ if ((width == -1) || ((toolbar->status_handle) && (toolbar->display_status))) { parent = toolbar->parent_handle; /* Get the window outline width */ if (width == -1) { if (!parent) return false; 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; } width = outline.outline.x1 - outline.outline.x0 - 2; } } /* Reformat the buttons starting with the throbber */ if ((width != old_width) || (toolbar->reformat_buttons)) { left_edge = 6; right_edge = width - 8; toolbar->height = 0; if ((toolbar->descriptor) && (toolbar->descriptor->theme) && (toolbar->type == THEME_BROWSER_TOOLBAR) && (toolbar->display_throbber)) { if (!toolbar->descriptor->throbber_right) { throbber_x = left_edge; left_edge += toolbar->descriptor->theme->throbber_width + 8; } toolbar->height = toolbar->descriptor->theme->throbber_height + 8; } if ((toolbar->type == THEME_BROWSER_TOOLBAR) && (toolbar->display_url)) { if (toolbar->height < 52 + 8) toolbar->height = 52 + 8; } /* Get the minimum height of the icons */ bottom_edge = left_edge; if ((toolbar->display_buttons) && (toolbar->descriptor) && (toolbar->descriptor->theme)) { toolbar_icon = toolbar->icon; while (toolbar_icon) { if (toolbar_icon->display) { bottom_edge += toolbar_icon->width; visible_icon = true; if ((toolbar_icon->height != 0) && (toolbar->height < toolbar_icon->height + 8)) { toolbar->height = toolbar_icon->height + 8; } } toolbar_icon = toolbar_icon->next; } if (visible_icon) bottom_edge += 8; } /* Check for minimum widths */ if (toolbar->type == THEME_BROWSER_TOOLBAR) { if (!toolbar->reformat_buttons) left_edge = bottom_edge; if (toolbar->display_url) bottom_edge += 32; if (bottom_edge > right_edge) right_edge = bottom_edge; if ((toolbar->descriptor) && (toolbar->descriptor->theme) && (toolbar->display_throbber) && (toolbar->descriptor->throbber_right)) { bottom_edge += toolbar->descriptor->theme->throbber_width; if (bottom_edge > right_edge) right_edge = bottom_edge; throbber_x = right_edge - toolbar->descriptor->theme->throbber_width; right_edge -= toolbar->descriptor->theme->throbber_width + 8; } } if (toolbar->reformat_buttons) { /* Hide the URL bar if we should */ if (!toolbar->display_url) { if (!xwimp_get_caret_position(&caret)) { if ((caret.w == toolbar->toolbar_handle) && (caret.i == ICON_TOOLBAR_URL)) xwimp_set_caret_position((wimp_w)-1, 0, 0, 0, 0, 0); } xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL, 0, -16384, 0, -16384); } ro_gui_set_icon_shaded_state(toolbar->toolbar_handle, ICON_TOOLBAR_URL, !toolbar->display_url); xwimp_force_redraw(toolbar->toolbar_handle, 0, 0, 16384, 16384); /* Move the buttons */ toolbar_icon = toolbar->icon; while (toolbar_icon) { if ((toolbar->display_buttons) && (toolbar_icon->display) && (toolbar_icon->width > 0)) { visible_icon = true; bottom_edge = (toolbar->height - toolbar_icon->height) / 2; xwimp_resize_icon(toolbar->toolbar_handle, toolbar_icon->icon_number, left_edge, bottom_edge, left_edge + toolbar_icon->width, bottom_edge + toolbar_icon->height); left_edge += toolbar_icon->width; } else { xwimp_resize_icon(toolbar->toolbar_handle, toolbar_icon->icon_number, 0, -16384, 0, -16384); } toolbar_icon = toolbar_icon->next; } if (visible_icon) left_edge += 8; } if (toolbar->height != 0) toolbar->height += 2; if (toolbar->type == THEME_BROWSER_TOOLBAR) { /* Move the URL bar */ if (toolbar->display_url) { xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL, left_edge, (toolbar->height / 2) - 26, right_edge, (toolbar->height / 2) + 26); xwimp_force_redraw(toolbar->toolbar_handle, right_edge, 0, 16384, 16384); if (!xwimp_get_caret_position(&caret)) { if ((caret.w == toolbar->toolbar_handle) && (caret.i == ICON_TOOLBAR_URL)) { xwimp_set_caret_position(toolbar->toolbar_handle, ICON_TOOLBAR_URL, caret.pos.x, caret.pos.y, -1, caret.index); } } ro_gui_redraw_icon(toolbar->toolbar_handle, ICON_TOOLBAR_URL); } /* Move the throbber */ if ((toolbar->descriptor) && (toolbar->descriptor->theme) && (throbber_x >= 0) && (toolbar->display_throbber)) { xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_THROBBER, throbber_x, 0, throbber_x + toolbar->descriptor->theme->throbber_width, toolbar->height); if (toolbar->descriptor->throbber_right) xwimp_force_redraw(toolbar->toolbar_handle, old_width - width + throbber_x, 0, 16384, 16384); xwimp_force_redraw(toolbar->toolbar_handle, throbber_x, 0, 16384, 16384); } else { xwimp_resize_icon(toolbar->toolbar_handle, ICON_TOOLBAR_THROBBER, 0, -16384, 0, -16384); } } /* Re-attach to the parent */ toolbar->toolbar_current = width; if ((toolbar->reformat_buttons) && (parent) && (old_height != toolbar->height)) { extent.x1 = 16384; extent.y0 = 0; extent.y1 = toolbar->height; xwimp_set_extent(toolbar->toolbar_handle, &extent); ro_gui_theme_attach_toolbar(toolbar, parent); } toolbar->reformat_buttons = false; } /* Reformat the status bar */ if ((toolbar->status_handle) && (parent)) { /* Get the current state */ state.w = toolbar->status_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; } /* Open or close the window */ if (!toolbar->display_status) { if (state.flags & wimp_WINDOW_OPEN) xwimp_close_window(toolbar->status_handle); } else { /* Get the status bar height/width */ status_max = width - ro_get_vscroll_width(parent); status_size = (status_max * toolbar->status_width) / 10000; if (status_size < 12) status_size = 12; status_height = ro_get_hscroll_height(parent) - 2; /* Update the extent */ extent.x1 = status_max; extent.y1 = status_height - 2; xwimp_set_extent(toolbar->status_handle, &extent); /* Re-open the window */ state.w = toolbar->status_handle; state.xscroll = 0; state.yscroll = 0; state.next = wimp_TOP; state.visible.x0 = outline.outline.x0; state.visible.x1 = outline.outline.x0 + status_size; state.visible.y0 = outline.outline.y0 - status_height; state.visible.y1 = outline.outline.y0 - 2; xwimp_open_window_nested((wimp_open *)&state, parent, wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT << wimp_CHILD_XORIGIN_SHIFT | wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT << wimp_CHILD_YORIGIN_SHIFT | wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT << wimp_CHILD_LS_EDGE_SHIFT | wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT << wimp_CHILD_BS_EDGE_SHIFT | wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT << wimp_CHILD_RS_EDGE_SHIFT | wimp_CHILD_LINKS_PARENT_VISIBLE_BOTTOM_OR_LEFT << wimp_CHILD_TS_EDGE_SHIFT); /* Resize and redraw the icons */ status_size = state.visible.x1 - state.visible.x0; if (status_size != toolbar->status_current) { xwimp_resize_icon(toolbar->status_handle, ICON_STATUS_TEXT, 0, 0, status_size - 12, status_height - 2); xwimp_resize_icon(toolbar->status_handle, ICON_STATUS_RESIZE, status_size - 12, 0, status_size, status_height - 2); xwimp_force_redraw(toolbar->status_handle, toolbar->status_current - 12, 0, status_size - 12, status_height - 2); xwimp_force_redraw(toolbar->status_handle, status_size - 12, 0, status_size, status_height - 2); toolbar->status_current = status_size; } } } return true; } /** * Destroys a toolbar and frees any associated memory. * * \param toolbar the toolbar to destroy */ void ro_gui_theme_destroy_toolbar(struct toolbar *toolbar) { struct toolbar_icon *icon; struct toolbar_icon *next_icon; if (!toolbar) return; /* Delete our windows */ if (toolbar->toolbar_handle) xwimp_delete_window(toolbar->toolbar_handle); if (toolbar->status_handle) xwimp_delete_window(toolbar->status_handle); /* Free the Wimp buffer (we only created one for them all) */ free(toolbar->url_buffer); /* Free all the icons */ next_icon = toolbar->icon; while ((icon = next_icon) != NULL) { next_icon = icon->next; ro_gui_theme_destroy_toolbar_icon(icon); } free(toolbar); } void ro_gui_theme_add_toolbar_icon(struct toolbar *toolbar, const char *name, int icon_number) { if (!toolbar) return; struct toolbar_icon *toolbar_icon; struct toolbar_icon *link_icon; /* Separators are really a sprite called "separator" */ if (name == NULL) name = "separator"; /* Create a new toolbar */ toolbar_icon = calloc(sizeof(struct toolbar_icon), 1); if (!toolbar_icon) { LOG(("No memory for malloc()")); warn_user("NoMemory", 0); return; } /* Set up and link in the icon */ sprintf(toolbar_icon->name, name); sprintf(toolbar_icon->validation, "R5;S%s,p%s", name, name); toolbar_icon->icon_number = icon_number; toolbar_icon->display = true; if (!toolbar->icon) { toolbar->icon = toolbar_icon; } else { link_icon = toolbar->icon; while (link_icon->next) link_icon = link_icon->next; link_icon->next = toolbar_icon; } } /** * Updates a toolbar icon with respect to the associated sprite. * * \param icon the toolbar icon to update */ void ro_gui_theme_update_toolbar_icon(struct toolbar *toolbar, struct toolbar_icon *icon) { os_coord dimensions; os_mode mode; os_error *error; int default_width = 0; /* Separators default to a width of 16 */ if (icon->icon_number == -1) default_width = 16; /* Handle no theme/no sprite area */ if (!toolbar) return; if ((!toolbar->descriptor) || (!toolbar->descriptor->theme) || (!toolbar->descriptor->theme->sprite_area)) { icon->width = 0; icon->height = 0; return; } /* Get the sprite details */ error = xosspriteop_read_sprite_info(osspriteop_USER_AREA, toolbar->descriptor->theme->sprite_area, (osspriteop_id)icon->name, &dimensions.x, &dimensions.y, 0, &mode); if (error) { icon->width = default_width; icon->height = 0; if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) { LOG(("xosspriteop_read_sprite_info: 0x%x: %s", error->errnum, error->errmess)); warn_user("MiscError", error->errmess); } return; } /* Store the details */ ro_convert_pixels_to_os_units(&dimensions, mode); icon->width = dimensions.x; icon->height = dimensions.y; } /** * Destroys a toolbar icon and frees any associated memory. * The icon is not removed from any linked list. * * \param icon the toolbar icon to destroy */ void ro_gui_theme_destroy_toolbar_icon(struct toolbar_icon *icon) { free(icon); }