/* * Copyright 2003 Phil Mellor * Copyright 2005 James Bursa * Copyright 2003 John M Bell * Copyright 2005 Richard Wilson * * 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 * Menu creation and handling implementation. */ #include #include #include #include #include "oslib/os.h" #include "oslib/osbyte.h" #include "oslib/osgbpb.h" #include "oslib/territory.h" #include "oslib/wimp.h" #include "utils/log.h" #include "utils/messages.h" #include "utils/utf8.h" #include "desktop/cookie_manager.h" #include "riscos/dialog.h" #include "riscos/configure.h" #include "riscos/cookies.h" #include "riscos/gui.h" #include "riscos/global_history.h" #include "riscos/help.h" #include "riscos/hotlist.h" #include "riscos/menus.h" #include "utils/nsoption.h" #include "riscos/save.h" #include "riscos/tinct.h" #include "riscos/toolbar.h" #include "riscos/url_suggest.h" #include "riscos/wimp.h" #include "riscos/wimp_event.h" #include "riscos/ucstables.h" 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 void ro_gui_menu_closed(void); static void ro_gui_menu_define_menu_add(struct menu_definition *definition, const 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 int ro_gui_menu_get_checksum(void); 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)) /** 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; /** 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 available menus */ wimp_menu *image_quality_menu, *proxy_type_menu, *languages_menu; /* 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) { /* image quality menu */ static const struct ns_menu 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(&images_definition); /* proxy menu */ static const struct ns_menu proxy_type_definition = { "ProxyType", { { "ProxyNone", NO_ACTION, 0 }, { "ProxyNoAuth", NO_ACTION, 0 }, { "ProxyBasic", NO_ACTION, 0 }, { "ProxyNTLM", NO_ACTION, 0 }, {NULL, 0, 0} } }; proxy_type_menu = ro_gui_menu_define_menu(&proxy_type_definition); /* special case menus */ ro_gui_url_suggest_init(); /* Note: This table *must* be kept in sync with the LangNames file */ static const struct ns_menu 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(&lang_definition); } /** * Display a menu. * * \param *menu Pointer to the menu to be displayed. * \param x The x position. * \param y The y position. * \param w The window that the menu belongs to. */ void ro_gui_menu_create(wimp_menu *menu, int x, int y, wimp_w w) { os_error *error; 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)) { ro_warn_user("NoMemory", 0); return; } } /* store the menu characteristics */ current_menu = menu; current_menu_window = w; current_menu_icon = wimp_ICON_WINDOW; /* create the menu */ current_menu_open = true; error = xwimp_create_menu(menu, x - 64, y); if (error) { NSLOG(netsurf, INFO, "xwimp_create_menu: 0x%x: %s", error->errnum, error->errmess); ro_warn_user("MenuError", error->errmess); ro_gui_menu_closed(); } } /** * 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) { NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess); ro_warn_user("MenuError", error->errmess); return; } error = xwimp_get_icon_state(&icon_state); if (error) { NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s", error->errnum, error->errmess); ro_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; } /** * Forcibly close any menu or transient dialogue box that is currently open. */ void ro_gui_menu_destroy(void) { os_error *error; if (current_menu == NULL) return; error = xwimp_create_menu(wimp_CLOSE_MENU, 0, 0); if (error) { NSLOG(netsurf, INFO, "xwimp_create_menu: 0x%x: %s", error->errnum, error->errmess); ro_warn_user("MenuError", error->errmess); } ro_gui_menu_closed(); } /** * Allow the current menu window to change, if the window is deleted and * recreated while a menu is active on an Adjust-click. * * \param from The original window handle. * \param to The new replacement window handle. */ void ro_gui_menu_window_changed(wimp_w from, wimp_w to) { if (from == current_menu_window) current_menu_window = to; } /** * 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; os_error *error; int previous_menu_icon = current_menu_icon; /* 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 and definition */ 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); /* Deal with the menu action. If this manages to re-prepare the * menu for re-opening, we test for and act on Adjust clicks. */ if (!ro_gui_wimp_event_menu_selection(current_menu_window, current_menu_icon, current_menu, selection, action)) return; /* re-open the menu for Adjust clicks */ error = xwimp_get_pointer_info(&pointer); if (error) { NSLOG(netsurf, INFO, "xwimp_get_pointer_info: 0x%x: %s", error->errnum, error->errmess); ro_warn_user("WimpError", error->errmess); ro_gui_menu_closed(); return; } if (pointer.buttons != wimp_CLICK_ADJUST) { ro_gui_menu_closed(); return; } 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; 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]]; action = ro_gui_menu_find_action(current_menu, menu_entry); /* Process the warning via Wimp_Event, then register the resulting * submenu with the module. */ ro_gui_wimp_event_submenu_warning(current_menu_window, current_menu_icon, current_menu, &(warning->selection), action); if (IS_MENU(menu_entry->sub_menu)) { ro_gui_wimp_event_register_submenu((wimp_w) 0); } else { ro_gui_wimp_event_register_submenu((wimp_w) menu_entry->sub_menu); /* If this is a dialogue box, remove the close and back icons. */ 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) { NSLOG(netsurf, INFO, "xwimp_create_sub_menu: 0x%x: %s", error->errnum, error->errmess); ro_warn_user("MenuError", error->errmess); } } /** * Handle Message_MenusDeleted, removing our current record of an open menu * if it matches the deleted menu handle. * * \param *deleted The message block. */ void ro_gui_menu_message_deleted(wimp_message_menus_deleted *deleted) { if (deleted != NULL && deleted->menu == current_menu) ro_gui_menu_closed(); } /** * Clean up after a menu has been closed, or forcibly close an open menu. */ static void ro_gui_menu_closed(void) { if (current_menu != NULL) ro_gui_wimp_event_menus_closed(current_menu_window, current_menu_icon, current_menu); current_menu = NULL; current_menu_window = NULL; current_menu_icon = 0; current_menu_open = false; } /** * Update the current menu by sending it a Menu Prepare event through wimp_event * and then reopening it if the contents has changed. * * \param *menu The menu to refresh: if 0, the current menu will be * refreshed regardless, otherwise it will be refreshed * only if it matches the supplied handle. */ void ro_gui_menu_refresh(wimp_menu *menu) { int checksum = 0; if (!current_menu_open) return; checksum = ro_gui_menu_get_checksum(); if (!ro_gui_wimp_event_prepare_menu(current_menu_window, current_menu_icon, current_menu)) return; /* \TODO -- Call the menu's event handler here. */ if (checksum != ro_gui_menu_get_checksum()) { os_error *error; error = xwimp_create_menu(current_menu, 0, 0); if (error) { NSLOG(netsurf, INFO, "xwimp_create_menu: 0x%x: %s", error->errnum, error->errmess); ro_warn_user("MenuError", error->errmess); } } } /** * 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(const 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."); return NULL; /* For the benefit of scan-build */ } /* 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, const struct ns_menu *menu, int depth, wimp_menu_entry *parent_entry, int first, int last, const char *prefix, int prefix_length) { int entry; int entries = 0; int matches[last - first + 1]; 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++) { const char *match; int 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 */ int 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 shaded whether to set the item as shaded */ void ro_gui_menu_set_entry_shaded(wimp_menu *menu, menu_action action, bool shaded) { struct menu_definition_entry *entry; struct menu_definition *definition = ro_gui_menu_find_menu(menu); if (!definition) return; /* we can't use find_entry as multiple actions may appear in one menu */ for (entry = definition->entries; entry; entry = entry->next) if (entry->action == action) { 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; } } /** * 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) { NSLOG(netsurf, INFO, "xwimp_get_menu_state: 0x%x: %s", error->errnum, error->errmess); ro_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; nserror err; /* read current alphabet */ error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &alphabet); if (error) { NSLOG(netsurf, INFO, "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 != NSERROR_OK) { assert(err != NSERROR_BAD_ENCODING); NSLOG(netsurf, INFO, "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 != NSERROR_OK) { assert(err != NSERROR_BAD_ENCODING); NSLOG(netsurf, INFO, "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; }