1 /*
2 * Copyright 2003 Phil Mellor <monkeyson@users.sourceforge.net>
3 * Copyright 2005 James Bursa <bursa@users.sourceforge.net>
4 * Copyright 2003 John M Bell <jmb202@ecs.soton.ac.uk>
5 * Copyright 2005 Richard Wilson <info@tinct.net>
6 *
7 * This file is part of NetSurf, http://www.netsurf-browser.org/
8 *
9 * NetSurf is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License.
12 *
13 * NetSurf is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /**
23 * \file
24 * Menu creation and handling implementation.
25 */
26
27 #include <ctype.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stddef.h>
31 #include "oslib/os.h"
32 #include "oslib/osbyte.h"
33 #include "oslib/osgbpb.h"
34 #include "oslib/territory.h"
35 #include "oslib/wimp.h"
36
37 #include "utils/log.h"
38 #include "utils/messages.h"
39 #include "utils/utf8.h"
40 #include "desktop/cookie_manager.h"
41
42 #include "riscos/dialog.h"
43 #include "riscos/configure.h"
44 #include "riscos/cookies.h"
45 #include "riscos/gui.h"
46 #include "riscos/global_history.h"
47 #include "riscos/help.h"
48 #include "riscos/hotlist.h"
49 #include "riscos/menus.h"
50 #include "utils/nsoption.h"
51 #include "riscos/save.h"
52 #include "riscos/tinct.h"
53 #include "riscos/toolbar.h"
54 #include "riscos/url_suggest.h"
55 #include "riscos/wimp.h"
56 #include "riscos/wimp_event.h"
57 #include "riscos/ucstables.h"
58
59 struct menu_definition_entry {
60 menu_action action; /**< menu action */
61 wimp_menu_entry *menu_entry; /**< corresponding menu entry */
62 const char *entry_key; /**< Messages key for entry text */
63 struct menu_definition_entry *next; /**< next menu entry */
64 };
65
66 struct menu_definition {
67 wimp_menu *menu; /**< corresponding menu */
68 const char *title_key; /**< Messages key for title text */
69 int current_encoding; /**< Identifier for current text encoding of menu text (as per OS_Byte,71,127) */
70 struct menu_definition_entry *entries; /**< menu entries */
71 struct menu_definition *next; /**< next menu */
72 };
73
74 static void ro_gui_menu_closed(void);
75 static void ro_gui_menu_define_menu_add(struct menu_definition *definition,
76 const struct ns_menu *menu, int depth,
77 wimp_menu_entry *parent_entry,
78 int first, int last, const char *prefix, int prefix_length);
79 static struct menu_definition *ro_gui_menu_find_menu(wimp_menu *menu);
80 static struct menu_definition_entry *ro_gui_menu_find_entry(wimp_menu *menu,
81 menu_action action);
82 static menu_action ro_gui_menu_find_action(wimp_menu *menu,
83 wimp_menu_entry *menu_entry);
84 static int ro_gui_menu_get_checksum(void);
85 static bool ro_gui_menu_translate(struct menu_definition *menu);
86
87
88 /* default menu item flags */
89 #define DEFAULT_FLAGS (wimp_ICON_TEXT | wimp_ICON_FILLED | \
90 (wimp_COLOUR_BLACK << wimp_ICON_FG_COLOUR_SHIFT) | \
91 (wimp_COLOUR_WHITE << wimp_ICON_BG_COLOUR_SHIFT))
92
93 /** The currently defined menus to perform actions for */
94 static struct menu_definition *ro_gui_menu_definitions;
95 /** The current menu being worked with (may not be open) */
96 wimp_menu *current_menu;
97 /** Whether a menu is currently open */
98 bool current_menu_open = false;
99 /** Window that owns the current menu */
100 wimp_w current_menu_window;
101 /** Icon that owns the current menu (only valid for popup menus) */
102 static wimp_i current_menu_icon;
103 /** The available menus */
104 wimp_menu *image_quality_menu, *proxy_type_menu, *languages_menu;
105
106 /* the values given in PRM 3-157 for how to check menus/windows are
107 * incorrect so we use a hack of checking if the sub-menu has bit 0
108 * set which is undocumented but true of window handles on
109 * all target OS versions */
110 #define IS_MENU(menu) !((int)(menu) & 1)
111
112 /**
113 * Create menu structures.
114 */
115
ro_gui_menu_init(void)116 void ro_gui_menu_init(void)
117 {
118 /* image quality menu */
119 static const struct ns_menu images_definition = {
120 "Display", {
121 { "ImgStyle0", NO_ACTION, 0 },
122 { "ImgStyle1", NO_ACTION, 0 },
123 { "ImgStyle2", NO_ACTION, 0 },
124 { "ImgStyle3", NO_ACTION, 0 },
125 {NULL, 0, 0}
126 }
127 };
128 image_quality_menu = ro_gui_menu_define_menu(&images_definition);
129
130 /* proxy menu */
131 static const struct ns_menu proxy_type_definition = {
132 "ProxyType", {
133 { "ProxyNone", NO_ACTION, 0 },
134 { "ProxyNoAuth", NO_ACTION, 0 },
135 { "ProxyBasic", NO_ACTION, 0 },
136 { "ProxyNTLM", NO_ACTION, 0 },
137 {NULL, 0, 0}
138 }
139 };
140 proxy_type_menu = ro_gui_menu_define_menu(&proxy_type_definition);
141
142 /* special case menus */
143 ro_gui_url_suggest_init();
144
145 /* Note: This table *must* be kept in sync with the LangNames file */
146 static const struct ns_menu lang_definition = {
147 "Languages", {
148 { "lang_af", NO_ACTION, 0 },
149 { "lang_bm", NO_ACTION, 0 },
150 { "lang_ca", NO_ACTION, 0 },
151 { "lang_cs", NO_ACTION, 0 },
152 { "lang_cy", NO_ACTION, 0 },
153 { "lang_da", NO_ACTION, 0 },
154 { "lang_de", NO_ACTION, 0 },
155 { "lang_en", NO_ACTION, 0 },
156 { "lang_es", NO_ACTION, 0 },
157 { "lang_et", NO_ACTION, 0 },
158 { "lang_eu", NO_ACTION, 0 },
159 { "lang_ff", NO_ACTION, 0 },
160 { "lang_fi", NO_ACTION, 0 },
161 { "lang_fr", NO_ACTION, 0 },
162 { "lang_ga", NO_ACTION, 0 },
163 { "lang_gl", NO_ACTION, 0 },
164 { "lang_ha", NO_ACTION, 0 },
165 { "lang_hr", NO_ACTION, 0 },
166 { "lang_hu", NO_ACTION, 0 },
167 { "lang_id", NO_ACTION, 0 },
168 { "lang_is", NO_ACTION, 0 },
169 { "lang_it", NO_ACTION, 0 },
170 { "lang_lt", NO_ACTION, 0 },
171 { "lang_lv", NO_ACTION, 0 },
172 { "lang_ms", NO_ACTION, 0 },
173 { "lang_mt", NO_ACTION, 0 },
174 { "lang_nl", NO_ACTION, 0 },
175 { "lang_no", NO_ACTION, 0 },
176 { "lang_pl", NO_ACTION, 0 },
177 { "lang_pt", NO_ACTION, 0 },
178 { "lang_rn", NO_ACTION, 0 },
179 { "lang_ro", NO_ACTION, 0 },
180 { "lang_rw", NO_ACTION, 0 },
181 { "lang_sk", NO_ACTION, 0 },
182 { "lang_sl", NO_ACTION, 0 },
183 { "lang_so", NO_ACTION, 0 },
184 { "lang_sq", NO_ACTION, 0 },
185 { "lang_sr", NO_ACTION, 0 },
186 { "lang_sv", NO_ACTION, 0 },
187 { "lang_sw", NO_ACTION, 0 },
188 { "lang_tr", NO_ACTION, 0 },
189 { "lang_uz", NO_ACTION, 0 },
190 { "lang_vi", NO_ACTION, 0 },
191 { "lang_wo", NO_ACTION, 0 },
192 { "lang_xs", NO_ACTION, 0 },
193 { "lang_yo", NO_ACTION, 0 },
194 { "lang_zu", NO_ACTION, 0 },
195 { NULL, 0, 0 }
196 }
197 };
198 languages_menu = ro_gui_menu_define_menu(&lang_definition);
199 }
200
201
202 /**
203 * Display a menu.
204 *
205 * \param *menu Pointer to the menu to be displayed.
206 * \param x The x position.
207 * \param y The y position.
208 * \param w The window that the menu belongs to.
209 */
210
ro_gui_menu_create(wimp_menu * menu,int x,int y,wimp_w w)211 void ro_gui_menu_create(wimp_menu *menu, int x, int y, wimp_w w)
212 {
213 os_error *error;
214 struct menu_definition *definition;
215
216 /* translate menu, if necessary (this returns quickly
217 * if there's nothing to be done) */
218 definition = ro_gui_menu_find_menu(menu);
219 if (definition) {
220 if (!ro_gui_menu_translate(definition)) {
221 ro_warn_user("NoMemory", 0);
222 return;
223 }
224 }
225
226 /* store the menu characteristics */
227 current_menu = menu;
228 current_menu_window = w;
229 current_menu_icon = wimp_ICON_WINDOW;
230
231 /* create the menu */
232 current_menu_open = true;
233 error = xwimp_create_menu(menu, x - 64, y);
234 if (error) {
235 NSLOG(netsurf, INFO, "xwimp_create_menu: 0x%x: %s",
236 error->errnum, error->errmess);
237 ro_warn_user("MenuError", error->errmess);
238 ro_gui_menu_closed();
239 }
240 }
241
242
243 /**
244 * Display a pop-up menu next to the specified icon.
245 *
246 * \param menu menu to open
247 * \param w window handle
248 * \param i icon handle
249 */
250
ro_gui_popup_menu(wimp_menu * menu,wimp_w w,wimp_i i)251 void ro_gui_popup_menu(wimp_menu *menu, wimp_w w, wimp_i i)
252 {
253 wimp_window_state state;
254 wimp_icon_state icon_state;
255 os_error *error;
256
257 state.w = w;
258 icon_state.w = w;
259 icon_state.i = i;
260 error = xwimp_get_window_state(&state);
261 if (error) {
262 NSLOG(netsurf, INFO, "xwimp_get_window_state: 0x%x: %s",
263 error->errnum, error->errmess);
264 ro_warn_user("MenuError", error->errmess);
265 return;
266 }
267
268 error = xwimp_get_icon_state(&icon_state);
269 if (error) {
270 NSLOG(netsurf, INFO, "xwimp_get_icon_state: 0x%x: %s",
271 error->errnum, error->errmess);
272 ro_warn_user("MenuError", error->errmess);
273 return;
274 }
275
276 ro_gui_menu_create(menu,
277 state.visible.x0 + icon_state.icon.extent.x1 + 64,
278 state.visible.y1 + icon_state.icon.extent.y1 -
279 state.yscroll, w);
280 current_menu_icon = i;
281 }
282
283
284 /**
285 * Forcibly close any menu or transient dialogue box that is currently open.
286 */
287
ro_gui_menu_destroy(void)288 void ro_gui_menu_destroy(void)
289 {
290 os_error *error;
291
292 if (current_menu == NULL)
293 return;
294
295 error = xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
296 if (error) {
297 NSLOG(netsurf, INFO, "xwimp_create_menu: 0x%x: %s",
298 error->errnum, error->errmess);
299 ro_warn_user("MenuError", error->errmess);
300 }
301
302 ro_gui_menu_closed();
303 }
304
305
306 /**
307 * Allow the current menu window to change, if the window is deleted and
308 * recreated while a menu is active on an Adjust-click.
309 *
310 * \param from The original window handle.
311 * \param to The new replacement window handle.
312 */
313
ro_gui_menu_window_changed(wimp_w from,wimp_w to)314 void ro_gui_menu_window_changed(wimp_w from, wimp_w to)
315 {
316
317 if (from == current_menu_window)
318 current_menu_window = to;
319 }
320
321
322 /**
323 * Handle menu selection.
324 */
325
ro_gui_menu_selection(wimp_selection * selection)326 void ro_gui_menu_selection(wimp_selection *selection)
327 {
328 int i; //, j;
329 wimp_menu_entry *menu_entry;
330 menu_action action;
331 wimp_pointer pointer;
332 os_error *error;
333 int previous_menu_icon = current_menu_icon;
334
335 /* if we are using gui_multitask then menu selection events
336 * may be delivered after the menu has been closed. As such,
337 * we simply ignore these events. */
338 if (!current_menu)
339 return;
340
341 assert(current_menu_window);
342
343 /* get the menu entry and associated action and definition */
344 menu_entry = ¤t_menu->entries[selection->items[0]];
345 for (i = 1; selection->items[i] != -1; i++)
346 menu_entry = &menu_entry->sub_menu->
347 entries[selection->items[i]];
348 action = ro_gui_menu_find_action(current_menu, menu_entry);
349
350 /* Deal with the menu action. If this manages to re-prepare the
351 * menu for re-opening, we test for and act on Adjust clicks.
352 */
353
354 if (!ro_gui_wimp_event_menu_selection(current_menu_window,
355 current_menu_icon, current_menu, selection, action))
356 return;
357
358 /* re-open the menu for Adjust clicks */
359 error = xwimp_get_pointer_info(&pointer);
360 if (error) {
361 NSLOG(netsurf, INFO, "xwimp_get_pointer_info: 0x%x: %s",
362 error->errnum, error->errmess);
363 ro_warn_user("WimpError", error->errmess);
364 ro_gui_menu_closed();
365 return;
366 }
367
368 if (pointer.buttons != wimp_CLICK_ADJUST) {
369 ro_gui_menu_closed();
370 return;
371 }
372
373 ro_gui_menu_create(current_menu, 0, 0, current_menu_window);
374 current_menu_icon = previous_menu_icon;
375 }
376
377
378 /**
379 * Handle Message_MenuWarning.
380 */
ro_gui_menu_warning(wimp_message_menu_warning * warning)381 void ro_gui_menu_warning(wimp_message_menu_warning *warning)
382 {
383 int i;
384 menu_action action;
385 wimp_menu_entry *menu_entry;
386 os_error *error;
387
388 assert(current_menu);
389 assert(current_menu_window);
390
391 /* get the sub-menu of the warning */
392 if (warning->selection.items[0] == -1)
393 return;
394 menu_entry = ¤t_menu->entries[warning->selection.items[0]];
395 for (i = 1; warning->selection.items[i] != -1; i++)
396 menu_entry = &menu_entry->sub_menu->
397 entries[warning->selection.items[i]];
398 action = ro_gui_menu_find_action(current_menu, menu_entry);
399
400 /* Process the warning via Wimp_Event, then register the resulting
401 * submenu with the module.
402 */
403
404 ro_gui_wimp_event_submenu_warning(current_menu_window,
405 current_menu_icon, current_menu, &(warning->selection),
406 action);
407
408 if (IS_MENU(menu_entry->sub_menu)) {
409 ro_gui_wimp_event_register_submenu((wimp_w) 0);
410 } else {
411 ro_gui_wimp_event_register_submenu((wimp_w)
412 menu_entry->sub_menu);
413
414 /* If this is a dialogue box, remove the close and back icons.
415 */
416
417 ro_gui_wimp_update_window_furniture((wimp_w)
418 menu_entry->sub_menu,
419 wimp_WINDOW_CLOSE_ICON | wimp_WINDOW_BACK_ICON,
420 0);
421 }
422
423 /* open the sub-menu */
424
425 error = xwimp_create_sub_menu(menu_entry->sub_menu,
426 warning->pos.x, warning->pos.y);
427 if (error) {
428 NSLOG(netsurf, INFO, "xwimp_create_sub_menu: 0x%x: %s",
429 error->errnum, error->errmess);
430 ro_warn_user("MenuError", error->errmess);
431 }
432 }
433
434
435 /**
436 * Handle Message_MenusDeleted, removing our current record of an open menu
437 * if it matches the deleted menu handle.
438 *
439 * \param *deleted The message block.
440 */
441
ro_gui_menu_message_deleted(wimp_message_menus_deleted * deleted)442 void ro_gui_menu_message_deleted(wimp_message_menus_deleted *deleted)
443 {
444 if (deleted != NULL && deleted->menu == current_menu)
445 ro_gui_menu_closed();
446 }
447
448
449 /**
450 * Clean up after a menu has been closed, or forcibly close an open menu.
451 */
452
ro_gui_menu_closed(void)453 static void ro_gui_menu_closed(void)
454 {
455 if (current_menu != NULL)
456 ro_gui_wimp_event_menus_closed(current_menu_window,
457 current_menu_icon, current_menu);
458
459 current_menu = NULL;
460 current_menu_window = NULL;
461 current_menu_icon = 0;
462 current_menu_open = false;
463 }
464
465
466 /**
467 * Update the current menu by sending it a Menu Prepare event through wimp_event
468 * and then reopening it if the contents has changed.
469 *
470 * \param *menu The menu to refresh: if 0, the current menu will be
471 * refreshed regardless, otherwise it will be refreshed
472 * only if it matches the supplied handle.
473 */
474
ro_gui_menu_refresh(wimp_menu * menu)475 void ro_gui_menu_refresh(wimp_menu *menu)
476 {
477 int checksum = 0;
478
479 if (!current_menu_open)
480 return;
481
482 checksum = ro_gui_menu_get_checksum();
483
484 if (!ro_gui_wimp_event_prepare_menu(current_menu_window,
485 current_menu_icon, current_menu))
486 return;
487
488 /* \TODO -- Call the menu's event handler here. */
489
490 if (checksum != ro_gui_menu_get_checksum()) {
491 os_error *error;
492 error = xwimp_create_menu(current_menu, 0, 0);
493 if (error) {
494 NSLOG(netsurf, INFO, "xwimp_create_menu: 0x%x: %s",
495 error->errnum, error->errmess);
496 ro_warn_user("MenuError", error->errmess);
497 }
498 }
499 }
500
501
502
503
504 /**
505 * Creates a wimp_menu and adds it to the list to handle actions for.
506 *
507 * \param menu The data to create the menu with
508 * \return The menu created, or NULL on failure
509 */
ro_gui_menu_define_menu(const struct ns_menu * menu)510 wimp_menu *ro_gui_menu_define_menu(const struct ns_menu *menu)
511 {
512 struct menu_definition *definition;
513 int entry;
514
515 definition = calloc(sizeof(struct menu_definition), 1);
516 if (!definition) {
517 die("No memory to create menu definition.");
518 return NULL; /* For the benefit of scan-build */
519 }
520
521 /* link in the menu to our list */
522 definition->next = ro_gui_menu_definitions;
523 ro_gui_menu_definitions = definition;
524
525 /* count number of menu entries */
526 for (entry = 0; menu->entries[entry].text; entry++)
527 /* do nothing */;
528
529 /* create our definitions */
530 ro_gui_menu_define_menu_add(definition, menu, 0, NULL,
531 0, entry, NULL, 0);
532
533 /* and translate menu into current encoding */
534 if (!ro_gui_menu_translate(definition))
535 die("No memory to translate menu.");
536
537 return definition->menu;
538 }
539
540 /**
541 * Create a wimp menu tree from ns_menu data.
542 * This function does *not* deal with the menu textual content - it simply
543 * creates and populates the appropriate structures. Textual content is
544 * generated by ro_gui_menu_translate_menu()
545 *
546 * \param definition Top level menu definition
547 * \param menu Menu declaration data
548 * \param depth Depth of menu we're currently building
549 * \param parent_entry Entry in parent menu, or NULL if root menu
550 * \param first First index in declaration data that is used by this menu
551 * \param last Last index in declaration data that is used by this menu
552 * \param prefix Prefix pf menu declaration string already seen
553 * \param prefix_length Length of prefix
554 */
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)555 void ro_gui_menu_define_menu_add(struct menu_definition *definition,
556 const struct ns_menu *menu, int depth,
557 wimp_menu_entry *parent_entry, int first, int last,
558 const char *prefix, int prefix_length)
559 {
560 int entry;
561 int entries = 0;
562 int matches[last - first + 1];
563 const char *text, *menu_text;
564 wimp_menu *new_menu;
565 struct menu_definition_entry *definition_entry;
566
567 /* step 1: store the matches for depth and subset string */
568 for (entry = first; entry < last; entry++) {
569 const char *match;
570 int cur_depth = 0;
571 match = menu->entries[entry].text;
572
573 /* skip specials at start of string */
574 while (!isalnum(*match))
575 match++;
576
577 /* attempt prefix match */
578 if ((prefix) && (strncmp(match, prefix, prefix_length)))
579 continue;
580
581 /* Find depth of this entry */
582 while (*match)
583 if (*match++ == '.')
584 cur_depth++;
585
586 if (depth == cur_depth)
587 matches[entries++] = entry;
588 }
589 matches[entries] = last;
590
591 /* no entries, so exit */
592 if (entries == 0)
593 return;
594
595 /* step 2: build and link the menu. we must use realloc to stop
596 * our memory fragmenting so we can test for sub-menus easily */
597 new_menu = (wimp_menu *)malloc(wimp_SIZEOF_MENU(entries));
598 if (!new_menu)
599 die("No memory to create menu.");
600
601 if (parent_entry) {
602 /* Fix up sub menu pointer */
603 parent_entry->sub_menu = new_menu;
604 } else {
605 /* Root menu => fill in definition struct */
606 definition->title_key = menu->title;
607 definition->current_encoding = 0;
608 definition->menu = new_menu;
609 }
610
611 /* this is fixed up in ro_gui_menu_translate() */
612 new_menu->title_data.indirected_text.text = NULL;
613
614 /* fill in menu flags */
615 ro_gui_menu_init_structure(new_menu, entries);
616
617 /* and then create the entries */
618 for (entry = 0; entry < entries; entry++) {
619 /* add the entry */
620 int id = matches[entry];
621
622 text = menu->entries[id].text;
623
624 /* fill in menu flags from specials at start of string */
625 new_menu->entries[entry].menu_flags = 0;
626 while (!isalnum(*text)) {
627 if (*text == '_')
628 new_menu->entries[entry].menu_flags |=
629 wimp_MENU_SEPARATE;
630 text++;
631 }
632
633 /* get messages key for menu entry */
634 menu_text = strrchr(text, '.');
635 if (!menu_text)
636 /* no '.' => top-level entry */
637 menu_text = text;
638 else
639 menu_text++; /* and move past the '.' */
640
641 /* fill in submenu data */
642 if (menu->entries[id].sub_window)
643 new_menu->entries[entry].sub_menu =
644 (wimp_menu *) (*menu->entries[id].sub_window);
645
646 /* this is fixed up in ro_gui_menu_translate() */
647 new_menu->entries[entry].data.indirected_text.text = NULL;
648
649 /* create definition entry */
650 definition_entry =
651 malloc(sizeof(struct menu_definition_entry));
652 if (!definition_entry)
653 die("Unable to create menu definition entry");
654 definition_entry->action = menu->entries[id].action;
655 definition_entry->menu_entry = &new_menu->entries[entry];
656 definition_entry->entry_key = menu_text;
657 definition_entry->next = definition->entries;
658 definition->entries = definition_entry;
659
660 /* recurse */
661 if (new_menu->entries[entry].sub_menu == wimp_NO_SUB_MENU) {
662 ro_gui_menu_define_menu_add(definition, menu,
663 depth + 1, &new_menu->entries[entry],
664 matches[entry], matches[entry + 1],
665 text, strlen(text));
666 }
667
668 /* give menu warnings */
669 if (new_menu->entries[entry].sub_menu != wimp_NO_SUB_MENU)
670 new_menu->entries[entry].menu_flags |=
671 wimp_MENU_GIVE_WARNING;
672 }
673 new_menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED;
674 new_menu->entries[entries - 1].menu_flags |= wimp_MENU_LAST;
675 }
676
677
678 /**
679 * Initialise the basic state of a menu structure so all entries are
680 * indirected text with no flags, no submenu.
681 */
ro_gui_menu_init_structure(wimp_menu * menu,int entries)682 void ro_gui_menu_init_structure(wimp_menu *menu, int entries)
683 {
684 int i;
685
686 menu->title_fg = wimp_COLOUR_BLACK;
687 menu->title_bg = wimp_COLOUR_LIGHT_GREY;
688 menu->work_fg = wimp_COLOUR_BLACK;
689 menu->work_bg = wimp_COLOUR_WHITE;
690 menu->width = 200;
691 menu->height = wimp_MENU_ITEM_HEIGHT;
692 menu->gap = wimp_MENU_ITEM_GAP;
693
694 for (i = 0; i < entries; i++) {
695 menu->entries[i].menu_flags = 0;
696 menu->entries[i].sub_menu = wimp_NO_SUB_MENU;
697 menu->entries[i].icon_flags =
698 DEFAULT_FLAGS | wimp_ICON_INDIRECTED;
699 menu->entries[i].data.indirected_text.validation =
700 (char *)-1;
701 }
702 menu->entries[0].menu_flags |= wimp_MENU_TITLE_INDIRECTED;
703 menu->entries[i - 1].menu_flags |= wimp_MENU_LAST;
704 }
705
706
707 /**
708 * Finds the menu_definition corresponding to a wimp_menu.
709 *
710 * \param menu the menu to find the definition for
711 * \return the associated definition, or NULL if one could not be found
712 */
ro_gui_menu_find_menu(wimp_menu * menu)713 struct menu_definition *ro_gui_menu_find_menu(wimp_menu *menu)
714 {
715 struct menu_definition *definition;
716
717 if (!menu)
718 return NULL;
719
720 for (definition = ro_gui_menu_definitions; definition;
721 definition = definition->next)
722 if (definition->menu == menu)
723 return definition;
724 return NULL;
725 }
726
727
728 /**
729 * Finds the key associated with a menu entry translation.
730 *
731 * \param menu the menu to search
732 * \param translated the translated text
733 * \return the original message key, or NULL if one could not be found
734 */
ro_gui_menu_find_menu_entry_key(wimp_menu * menu,const char * translated)735 const char *ro_gui_menu_find_menu_entry_key(wimp_menu *menu,
736 const char *translated)
737 {
738 struct menu_definition_entry *entry;
739 struct menu_definition *definition = ro_gui_menu_find_menu(menu);
740
741 if (!definition)
742 return NULL;
743
744 for (entry = definition->entries; entry; entry = entry->next)
745 if (!strcmp(entry->menu_entry->data.indirected_text.text, translated))
746 return entry->entry_key;
747 return NULL;
748 }
749
750
751 /**
752 * Finds the menu_definition_entry corresponding to an action for a wimp_menu.
753 *
754 * \param menu the menu to search for an action within
755 * \param action the action to find
756 * \return the associated menu entry, or NULL if one could not be found
757 */
ro_gui_menu_find_entry(wimp_menu * menu,menu_action action)758 struct menu_definition_entry *ro_gui_menu_find_entry(wimp_menu *menu,
759 menu_action action)
760 {
761 struct menu_definition_entry *entry;
762 struct menu_definition *definition = ro_gui_menu_find_menu(menu);
763
764 if (!definition)
765 return NULL;
766
767 for (entry = definition->entries; entry; entry = entry->next)
768 if (entry->action == action)
769 return entry;
770 return NULL;
771 }
772
773
774 /**
775 * Finds the action corresponding to a wimp_menu_entry for a wimp_menu.
776 *
777 * \param menu the menu to search for an action within
778 * \param menu_entry the menu_entry to find
779 * \return the associated action, or 0 if one could not be found
780 */
ro_gui_menu_find_action(wimp_menu * menu,wimp_menu_entry * menu_entry)781 menu_action ro_gui_menu_find_action(wimp_menu *menu, wimp_menu_entry *menu_entry)
782 {
783 struct menu_definition_entry *entry;
784 struct menu_definition *definition = ro_gui_menu_find_menu(menu);
785
786 if (!definition)
787 return NO_ACTION;
788
789 for (entry = definition->entries; entry; entry = entry->next) {
790 if (entry->menu_entry == menu_entry)
791 return entry->action;
792 }
793 return NO_ACTION;
794 }
795
796
797 /**
798 * Sets an action within a menu as having a specific ticked status.
799 *
800 * \param menu the menu containing the action
801 * \param action the action to tick/untick
802 * \param shaded whether to set the item as shaded
803 */
ro_gui_menu_set_entry_shaded(wimp_menu * menu,menu_action action,bool shaded)804 void ro_gui_menu_set_entry_shaded(wimp_menu *menu, menu_action action,
805 bool shaded)
806 {
807 struct menu_definition_entry *entry;
808 struct menu_definition *definition = ro_gui_menu_find_menu(menu);
809
810 if (!definition)
811 return;
812
813 /* we can't use find_entry as multiple actions may appear in one menu */
814 for (entry = definition->entries; entry; entry = entry->next)
815 if (entry->action == action) {
816 if (shaded)
817 entry->menu_entry->icon_flags |= wimp_ICON_SHADED;
818 else
819 entry->menu_entry->icon_flags &= ~wimp_ICON_SHADED;
820 }
821 }
822
823
824 /**
825 * Sets an action within a menu as having a specific ticked status.
826 *
827 * \param menu the menu containing the action
828 * \param action the action to tick/untick
829 * \param ticked whether to set the item as ticked
830 */
ro_gui_menu_set_entry_ticked(wimp_menu * menu,menu_action action,bool ticked)831 void ro_gui_menu_set_entry_ticked(wimp_menu *menu, menu_action action,
832 bool ticked)
833 {
834 struct menu_definition_entry *entry =
835 ro_gui_menu_find_entry(menu, action);
836 if (entry) {
837 if (ticked)
838 entry->menu_entry->menu_flags |= wimp_MENU_TICKED;
839 else
840 entry->menu_entry->menu_flags &= ~wimp_MENU_TICKED;
841 }
842 }
843
844
845 /**
846 * Calculates a simple checksum for the current menu state
847 */
ro_gui_menu_get_checksum(void)848 int ro_gui_menu_get_checksum(void)
849 {
850 wimp_selection menu_tree;
851 int i = 0, j, checksum = 0;
852 os_error *error;
853 wimp_menu *menu;
854
855 if (!current_menu_open)
856 return 0;
857
858 error = xwimp_get_menu_state((wimp_menu_state_flags)0,
859 &menu_tree, 0, 0);
860 if (error) {
861 NSLOG(netsurf, INFO, "xwimp_get_menu_state: 0x%x: %s",
862 error->errnum, error->errmess);
863 ro_warn_user("MenuError", error->errmess);
864 return 0;
865 }
866
867 menu = current_menu;
868 do {
869 j = 0;
870 do {
871 if (menu->entries[j].icon_flags & wimp_ICON_SHADED)
872 checksum ^= (1 << (i + j * 2));
873 if (menu->entries[j].menu_flags & wimp_MENU_TICKED)
874 checksum ^= (2 << (i + j * 2));
875 } while (!(menu->entries[j++].menu_flags & wimp_MENU_LAST));
876
877 j = menu_tree.items[i++];
878 if (j != -1) {
879 menu = menu->entries[j].sub_menu;
880 if ((!menu) || (menu == wimp_NO_SUB_MENU) || (!IS_MENU(menu)))
881 break;
882 }
883 } while (j != -1);
884
885 return checksum;
886 }
887
888 /**
889 * Translate a menu's textual content into the system local encoding
890 *
891 * \param menu The menu to translate
892 * \return false if out of memory, true otherwise
893 */
ro_gui_menu_translate(struct menu_definition * menu)894 bool ro_gui_menu_translate(struct menu_definition *menu)
895 {
896 os_error *error;
897 int alphabet;
898 struct menu_definition_entry *entry;
899 char *translated;
900 nserror err;
901
902 /* read current alphabet */
903 error = xosbyte1(osbyte_ALPHABET_NUMBER, 127, 0, &alphabet);
904 if (error) {
905 NSLOG(netsurf, INFO, "failed reading alphabet: 0x%x: %s",
906 error->errnum, error->errmess);
907 /* assume Latin1 */
908 alphabet = territory_ALPHABET_LATIN1;
909 }
910
911 if (menu->current_encoding == alphabet)
912 /* menu text is already in the correct encoding */
913 return true;
914
915 /* translate root menu title text */
916 free(menu->menu->title_data.indirected_text.text);
917 err = utf8_to_local_encoding(messages_get(menu->title_key),
918 0, &translated);
919 if (err != NSERROR_OK) {
920 assert(err != NSERROR_BAD_ENCODING);
921 NSLOG(netsurf, INFO, "utf8_to_enc failed");
922 return false;
923 }
924
925 /* and fill in WIMP menu field */
926 menu->menu->title_data.indirected_text.text = translated;
927
928 /* now the menu entries */
929 for (entry = menu->entries; entry; entry = entry->next) {
930 wimp_menu *submenu = entry->menu_entry->sub_menu;
931
932 /* tranlate menu entry text */
933 free(entry->menu_entry->data.indirected_text.text);
934 err = utf8_to_local_encoding(messages_get(entry->entry_key),
935 0, &translated);
936 if (err != NSERROR_OK) {
937 assert(err != NSERROR_BAD_ENCODING);
938 NSLOG(netsurf, INFO, "utf8_to_enc failed");
939 return false;
940 }
941
942 /* fill in WIMP menu fields */
943 entry->menu_entry->data.indirected_text.text = translated;
944 entry->menu_entry->data.indirected_text.validation =
945 (char *) -1;
946 entry->menu_entry->data.indirected_text.size =
947 strlen(translated);
948
949 /* child menu title - this is the same as the text of
950 * the parent menu entry, so just copy the pointer */
951 if (submenu != wimp_NO_SUB_MENU && IS_MENU(submenu)) {
952 submenu->title_data.indirected_text.text =
953 translated;
954 }
955 }
956
957 /* finally, set the current encoding of the menu */
958 menu->current_encoding = alphabet;
959
960 return true;
961 }
962
963