Remove unneeded `struct` keyword for typedef'd structs
[midnight-commander.git] / lib / widget / menu.c
blob504d6c8e07762fdd2d98955bf25fa401e5d7c45c
1 /*
2 Pulldown menu code
4 Copyright (C) 1994-2016
5 Free Software Foundation, Inc.
7 Written by:
8 Andrew Borodin <aborodin@vmail.ru>, 2012, 2013
10 This file is part of the Midnight Commander.
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 /** \file menu.c
27 * \brief Source: pulldown menu code
30 #include <config.h>
32 #include <ctype.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <sys/types.h>
37 #include "lib/global.h"
39 #include "lib/tty/tty.h"
40 #include "lib/skin.h"
41 #include "lib/tty/mouse.h"
42 #include "lib/tty/key.h" /* key macros */
43 #include "lib/strutil.h"
44 #include "lib/widget.h"
45 #include "lib/event.h" /* mc_event_raise() */
47 /*** global variables ****************************************************************************/
49 /*** file scope macro definitions ****************************************************************/
51 #define MENUENTRY(x) ((menu_entry_t *)(x))
52 #define MENU(x) ((menu_t *)(x))
54 /*** file scope type declarations ****************************************************************/
56 struct menu_entry_t
58 unsigned char first_letter;
59 hotkey_t text;
60 long command;
61 char *shortcut;
64 struct menu_t
66 int start_x; /* position relative to menubar start */
67 hotkey_t text;
68 GList *entries;
69 size_t max_entry_len; /* cached max length of entry texts (text + shortcut) */
70 size_t max_hotkey_len; /* cached max length of shortcuts */
71 unsigned int selected; /* pointer to current menu entry */
72 char *help_node;
75 /*** file scope variables ************************************************************************/
77 /*** file scope functions ************************************************************************/
78 /* --------------------------------------------------------------------------------------------- */
80 static void
81 menu_arrange (menu_t * menu, dlg_shortcut_str get_shortcut)
83 if (menu != NULL)
85 GList *i;
86 size_t max_shortcut_len = 0;
88 menu->max_entry_len = 1;
89 menu->max_hotkey_len = 1;
91 for (i = menu->entries; i != NULL; i = g_list_next (i))
93 menu_entry_t *entry = MENUENTRY (i->data);
95 if (entry != NULL)
97 size_t len;
99 len = (size_t) hotkey_width (entry->text);
100 menu->max_hotkey_len = max (menu->max_hotkey_len, len);
102 if (get_shortcut != NULL)
103 entry->shortcut = get_shortcut (entry->command);
105 if (entry->shortcut != NULL)
107 len = (size_t) str_term_width1 (entry->shortcut);
108 max_shortcut_len = max (max_shortcut_len, len);
113 menu->max_entry_len = menu->max_hotkey_len + max_shortcut_len;
117 /* --------------------------------------------------------------------------------------------- */
119 static void
120 menubar_paint_idx (WMenuBar * menubar, unsigned int idx, int color)
122 Widget *w = WIDGET (menubar);
123 const menu_t *menu = MENU (g_list_nth_data (menubar->menu, menubar->selected));
124 const menu_entry_t *entry = MENUENTRY (g_list_nth_data (menu->entries, idx));
125 const int y = 2 + idx;
126 int x = menu->start_x;
128 if (x + menu->max_entry_len + 4 > (gsize) w->cols)
129 x = w->cols - menu->max_entry_len - 4;
131 if (entry == NULL)
133 /* menu separator */
134 tty_setcolor (MENU_ENTRY_COLOR);
136 widget_move (w, y, x - 1);
137 tty_print_alt_char (ACS_LTEE, FALSE);
138 tty_draw_hline (w->y + y, w->x + x, ACS_HLINE, menu->max_entry_len + 3);
139 widget_move (w, y, x + menu->max_entry_len + 3);
140 tty_print_alt_char (ACS_RTEE, FALSE);
142 else
144 int yt, xt;
146 /* menu text */
147 tty_setcolor (color);
148 widget_move (w, y, x);
149 tty_print_char ((unsigned char) entry->first_letter);
150 tty_getyx (&yt, &xt);
151 tty_draw_hline (yt, xt, ' ', menu->max_entry_len + 2); /* clear line */
152 tty_print_string (entry->text.start);
154 if (entry->text.hotkey != NULL)
156 tty_setcolor (color == MENU_SELECTED_COLOR ? MENU_HOTSEL_COLOR : MENU_HOT_COLOR);
157 tty_print_string (entry->text.hotkey);
158 tty_setcolor (color);
161 if (entry->text.end != NULL)
162 tty_print_string (entry->text.end);
164 if (entry->shortcut != NULL)
166 widget_move (w, y, x + menu->max_hotkey_len + 3);
167 tty_print_string (entry->shortcut);
170 /* move cursor to the start of entry text */
171 widget_move (w, y, x + 1);
175 /* --------------------------------------------------------------------------------------------- */
177 static void
178 menubar_draw_drop (WMenuBar * menubar)
180 Widget *w = WIDGET (menubar);
181 const menu_t *menu = MENU (g_list_nth_data (menubar->menu, menubar->selected));
182 const unsigned int count = g_list_length (menu->entries);
183 int column = menu->start_x - 1;
184 unsigned int i;
186 if (column + menu->max_entry_len + 5 > (gsize) w->cols)
187 column = w->cols - menu->max_entry_len - 5;
189 tty_setcolor (MENU_ENTRY_COLOR);
190 tty_draw_box (w->y + 1, w->x + column, count + 2, menu->max_entry_len + 5, FALSE);
192 for (i = 0; i < count; i++)
193 menubar_paint_idx (menubar, i,
194 i == menu->selected ? MENU_SELECTED_COLOR : MENU_ENTRY_COLOR);
197 /* --------------------------------------------------------------------------------------------- */
199 static void
200 menubar_set_color (WMenuBar * menubar, gboolean current, gboolean hotkey)
202 if (!menubar->is_active)
203 tty_setcolor (MENU_INACTIVE_COLOR);
204 else if (current)
205 tty_setcolor (hotkey ? MENU_HOTSEL_COLOR : MENU_SELECTED_COLOR);
206 else
207 tty_setcolor (hotkey ? MENU_HOT_COLOR : MENU_ENTRY_COLOR);
210 /* --------------------------------------------------------------------------------------------- */
212 static void
213 menubar_draw (WMenuBar * menubar)
215 Widget *w = WIDGET (menubar);
216 GList *i;
218 /* First draw the complete menubar */
219 tty_setcolor (menubar->is_active ? MENU_ENTRY_COLOR : MENU_INACTIVE_COLOR);
220 tty_draw_hline (w->y, w->x, ' ', w->cols);
222 /* Now each one of the entries */
223 for (i = menubar->menu; i != NULL; i = g_list_next (i))
225 menu_t *menu = MENU (i->data);
226 gboolean is_selected = (menubar->selected == (gsize) g_list_position (menubar->menu, i));
228 menubar_set_color (menubar, is_selected, FALSE);
229 widget_move (w, 0, menu->start_x);
231 tty_print_char (' ');
232 tty_print_string (menu->text.start);
234 if (menu->text.hotkey != NULL)
236 menubar_set_color (menubar, is_selected, TRUE);
237 tty_print_string (menu->text.hotkey);
238 menubar_set_color (menubar, is_selected, FALSE);
241 if (menu->text.end != NULL)
242 tty_print_string (menu->text.end);
244 tty_print_char (' ');
247 if (menubar->is_dropped)
248 menubar_draw_drop (menubar);
249 else
250 widget_move (w, 0, MENU (g_list_nth_data (menubar->menu, menubar->selected))->start_x);
253 /* --------------------------------------------------------------------------------------------- */
255 static void
256 menubar_remove (WMenuBar * menubar)
258 WDialog *h;
260 if (!menubar->is_dropped)
261 return;
263 /* HACK: before refresh the dialog, change the current widget to keep the order
264 of overlapped widgets. This is useful in multi-window editor.
265 In general, menubar should be a special object, not an ordinary widget
266 in the current dialog. */
267 h = WIDGET (menubar)->owner;
268 h->current = g_list_find (h->widgets, dlg_find_by_id (h, menubar->previous_widget));
270 menubar->is_dropped = FALSE;
271 do_refresh ();
272 menubar->is_dropped = TRUE;
274 /* restore current widget */
275 h->current = g_list_find (h->widgets, menubar);
278 /* --------------------------------------------------------------------------------------------- */
280 static void
281 menubar_left (WMenuBar * menubar)
283 menubar_remove (menubar);
284 if (menubar->selected == 0)
285 menubar->selected = g_list_length (menubar->menu) - 1;
286 else
287 menubar->selected--;
288 menubar_draw (menubar);
291 /* --------------------------------------------------------------------------------------------- */
293 static void
294 menubar_right (WMenuBar * menubar)
296 menubar_remove (menubar);
297 menubar->selected = (menubar->selected + 1) % g_list_length (menubar->menu);
298 menubar_draw (menubar);
301 /* --------------------------------------------------------------------------------------------- */
303 static void
304 menubar_finish (WMenuBar * menubar)
306 Widget *w = WIDGET (menubar);
308 menubar->is_dropped = FALSE;
309 menubar->is_active = FALSE;
310 w->lines = 1;
311 widget_want_hotkey (w, 0);
313 dlg_select_by_id (w->owner, menubar->previous_widget);
314 do_refresh ();
317 /* --------------------------------------------------------------------------------------------- */
319 static void
320 menubar_drop (WMenuBar * menubar, unsigned int selected)
322 menubar->is_dropped = TRUE;
323 menubar->selected = selected;
324 menubar_draw (menubar);
327 /* --------------------------------------------------------------------------------------------- */
329 static void
330 menubar_execute (WMenuBar * menubar)
332 const menu_t *menu = MENU (g_list_nth_data (menubar->menu, menubar->selected));
333 const menu_entry_t *entry = MENUENTRY (g_list_nth_data (menu->entries, menu->selected));
335 if ((entry != NULL) && (entry->command != CK_IgnoreKey))
337 Widget *w = WIDGET (menubar);
339 mc_global.widget.is_right = (menubar->selected != 0);
340 menubar_finish (menubar);
341 send_message (w->owner, w, MSG_ACTION, entry->command, NULL);
342 do_refresh ();
346 /* --------------------------------------------------------------------------------------------- */
348 static void
349 menubar_down (WMenuBar * menubar)
351 menu_t *menu = MENU (g_list_nth_data (menubar->menu, menubar->selected));
352 const unsigned int len = g_list_length (menu->entries);
353 menu_entry_t *entry;
355 menubar_paint_idx (menubar, menu->selected, MENU_ENTRY_COLOR);
359 menu->selected = (menu->selected + 1) % len;
360 entry = MENUENTRY (g_list_nth_data (menu->entries, menu->selected));
362 while ((entry == NULL) || (entry->command == CK_IgnoreKey));
364 menubar_paint_idx (menubar, menu->selected, MENU_SELECTED_COLOR);
367 /* --------------------------------------------------------------------------------------------- */
369 static void
370 menubar_up (WMenuBar * menubar)
372 menu_t *menu = MENU (g_list_nth_data (menubar->menu, menubar->selected));
373 const unsigned int len = g_list_length (menu->entries);
374 menu_entry_t *entry;
376 menubar_paint_idx (menubar, menu->selected, MENU_ENTRY_COLOR);
380 if (menu->selected == 0)
381 menu->selected = len - 1;
382 else
383 menu->selected--;
384 entry = MENUENTRY (g_list_nth_data (menu->entries, menu->selected));
386 while ((entry == NULL) || (entry->command == CK_IgnoreKey));
388 menubar_paint_idx (menubar, menu->selected, MENU_SELECTED_COLOR);
391 /* --------------------------------------------------------------------------------------------- */
393 static void
394 menubar_first (WMenuBar * menubar)
396 menu_t *menu = MENU (g_list_nth_data (menubar->menu, menubar->selected));
398 if (menu->selected == 0)
399 return;
401 menubar_paint_idx (menubar, menu->selected, MENU_ENTRY_COLOR);
403 menu->selected = 0;
405 while (TRUE)
407 menu_entry_t *entry;
409 entry = MENUENTRY (g_list_nth_data (menu->entries, menu->selected));
411 if ((entry == NULL) || (entry->command == CK_IgnoreKey))
412 menu->selected++;
413 else
414 break;
417 menubar_paint_idx (menubar, menu->selected, MENU_SELECTED_COLOR);
420 /* --------------------------------------------------------------------------------------------- */
422 static void
423 menubar_last (WMenuBar * menubar)
425 menu_t *menu = MENU (g_list_nth_data (menubar->menu, menubar->selected));
426 const unsigned int len = g_list_length (menu->entries);
427 menu_entry_t *entry;
429 if (menu->selected == len - 1)
430 return;
432 menubar_paint_idx (menubar, menu->selected, MENU_ENTRY_COLOR);
434 menu->selected = len;
438 menu->selected--;
439 entry = MENUENTRY (g_list_nth_data (menu->entries, menu->selected));
441 while ((entry == NULL) || (entry->command == CK_IgnoreKey));
443 menubar_paint_idx (menubar, menu->selected, MENU_SELECTED_COLOR);
446 /* --------------------------------------------------------------------------------------------- */
448 static int
449 menubar_handle_key (WMenuBar * menubar, int key)
451 /* Lowercase */
452 if (isascii (key))
453 key = g_ascii_tolower (key);
455 if (is_abort_char (key))
457 menubar_finish (menubar);
458 return 1;
461 /* menubar help or menubar navigation */
462 switch (key)
464 case KEY_F (1):
466 ev_help_t event_data = { NULL, NULL };
468 if (menubar->is_dropped)
469 event_data.node =
470 MENU (g_list_nth_data (menubar->menu, menubar->selected))->help_node;
471 else
472 event_data.node = "[Menu Bar]";
474 mc_event_raise (MCEVENT_GROUP_CORE, "help", &event_data);
475 menubar_draw (menubar);
476 return 1;
479 case KEY_LEFT:
480 case XCTRL ('b'):
481 menubar_left (menubar);
482 return 1;
484 case KEY_RIGHT:
485 case XCTRL ('f'):
486 menubar_right (menubar);
487 return 1;
489 default:
490 break;
493 if (!menubar->is_dropped)
495 GList *i;
497 /* drop menu by hotkey */
498 for (i = menubar->menu; i != NULL; i = g_list_next (i))
500 menu_t *menu = MENU (i->data);
502 if ((menu->text.hotkey != NULL) && (key == g_ascii_tolower (menu->text.hotkey[0])))
504 menubar_drop (menubar, g_list_position (menubar->menu, i));
505 return 1;
509 /* drop menu by Enter or Dowwn key */
510 if (key == KEY_ENTER || key == XCTRL ('n') || key == KEY_DOWN || key == '\n')
511 menubar_drop (menubar, menubar->selected);
513 return 1;
517 menu_t *menu = MENU (g_list_nth_data (menubar->menu, menubar->selected));
518 GList *i;
520 /* execute menu command by hotkey */
521 for (i = menu->entries; i != NULL; i = g_list_next (i))
523 const menu_entry_t *entry = MENUENTRY (i->data);
525 if ((entry != NULL) && (entry->command != CK_IgnoreKey)
526 && (entry->text.hotkey != NULL) && (key == g_ascii_tolower (entry->text.hotkey[0])))
528 menu->selected = g_list_position (menu->entries, i);
529 menubar_execute (menubar);
530 return 1;
534 /* menu execute by Enter or menu navigation */
535 switch (key)
537 case KEY_ENTER:
538 case '\n':
539 menubar_execute (menubar);
540 return 1;
542 case KEY_HOME:
543 case ALT ('<'):
544 menubar_first (menubar);
545 break;
547 case KEY_END:
548 case ALT ('>'):
549 menubar_last (menubar);
550 break;
552 case KEY_DOWN:
553 case XCTRL ('n'):
554 menubar_down (menubar);
555 break;
557 case KEY_UP:
558 case XCTRL ('p'):
559 menubar_up (menubar);
560 break;
562 default:
563 break;
567 return 0;
570 /* --------------------------------------------------------------------------------------------- */
572 static cb_ret_t
573 menubar_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
575 WMenuBar *menubar = MENUBAR (w);
577 switch (msg)
579 /* We do not want the focus unless we have been activated */
580 case MSG_FOCUS:
581 if (!menubar->is_active)
582 return MSG_NOT_HANDLED;
584 /* Trick to get all the mouse events */
585 w->lines = LINES;
587 /* Trick to get all of the hotkeys */
588 widget_want_hotkey (w, 1);
589 menubar_draw (menubar);
590 return MSG_HANDLED;
592 /* We don't want the buttonbar to activate while using the menubar */
593 case MSG_HOTKEY:
594 case MSG_KEY:
595 if (menubar->is_active)
597 menubar_handle_key (menubar, parm);
598 return MSG_HANDLED;
600 return MSG_NOT_HANDLED;
602 case MSG_CURSOR:
603 /* Put the cursor in a suitable place */
604 return MSG_NOT_HANDLED;
606 case MSG_UNFOCUS:
607 return menubar->is_active ? MSG_NOT_HANDLED : MSG_HANDLED;
609 case MSG_DRAW:
610 if (menubar->is_visible)
612 menubar_draw (menubar);
613 return MSG_HANDLED;
615 /* fall through */
617 case MSG_RESIZE:
618 /* try show menu after screen resize */
619 send_message (w, sender, MSG_FOCUS, 0, data);
620 return MSG_HANDLED;
623 case MSG_DESTROY:
624 menubar_set_menu (menubar, NULL);
625 return MSG_HANDLED;
627 default:
628 return widget_default_callback (w, sender, msg, parm, data);
632 /* --------------------------------------------------------------------------------------------- */
634 static int
635 menubar_event (Gpm_Event * event, void *data)
637 WMenuBar *menubar = MENUBAR (data);
638 Widget *w = WIDGET (data);
639 gboolean was_active = TRUE;
640 int left_x, right_x, bottom_y;
641 menu_t *menu;
642 Gpm_Event local;
644 if (!mouse_global_in_widget (event, w))
645 return MOU_UNHANDLED;
647 /* ignore unsupported events */
648 if ((event->type & (GPM_UP | GPM_DOWN | GPM_DRAG)) == 0)
649 return MOU_NORMAL;
651 /* ignore wheel events if menu is inactive */
652 if (!menubar->is_active && ((event->buttons & (GPM_B_MIDDLE | GPM_B_UP | GPM_B_DOWN)) != 0))
653 return MOU_NORMAL;
655 local = mouse_get_local (event, w);
657 if (local.y == 1 && (local.type & GPM_UP) != 0)
658 return MOU_NORMAL;
660 if (!menubar->is_dropped)
662 if (local.y > 1)
664 /* mouse click below menubar -- close menu and send focus to widget under mouse */
665 menubar_finish (menubar);
666 return MOU_UNHANDLED;
669 menubar->previous_widget = dlg_get_current_widget_id (w->owner);
670 menubar->is_active = TRUE;
671 menubar->is_dropped = TRUE;
672 was_active = FALSE;
675 /* Mouse operations on the menubar */
676 if (local.y == 1 || !was_active)
678 /* wheel events on menubar */
679 if ((local.buttons & GPM_B_UP) != 0)
680 menubar_left (menubar);
681 else if ((local.buttons & GPM_B_DOWN) != 0)
682 menubar_right (menubar);
683 else
685 const unsigned int len = g_list_length (menubar->menu);
686 unsigned int new_selection = 0;
688 while ((new_selection < len)
689 && (local.x > MENU (g_list_nth_data (menubar->menu, new_selection))->start_x))
690 new_selection++;
692 if (new_selection != 0) /* Don't set the invalid value -1 */
693 new_selection--;
695 if (!was_active)
697 menubar->selected = new_selection;
698 dlg_select_widget (menubar);
700 else
702 menubar_remove (menubar);
703 menubar->selected = new_selection;
705 menubar_draw (menubar);
707 return MOU_NORMAL;
710 if (!menubar->is_dropped || (local.y < 2))
711 return MOU_NORMAL;
713 /* middle click -- everywhere */
714 if (((local.buttons & GPM_B_MIDDLE) != 0) && ((local.type & GPM_DOWN) != 0))
716 menubar_execute (menubar);
717 return MOU_NORMAL;
720 /* the mouse operation is on the menus or it is not */
721 menu = MENU (g_list_nth_data (menubar->menu, menubar->selected));
722 left_x = menu->start_x;
723 right_x = left_x + menu->max_entry_len + 3;
724 if (right_x > w->cols)
726 left_x = w->cols - menu->max_entry_len - 3;
727 right_x = w->cols;
730 bottom_y = g_list_length (menu->entries) + 3;
732 if ((local.x >= left_x) && (local.x <= right_x) && (local.y <= bottom_y))
734 int pos = local.y - 3;
735 const menu_entry_t *entry = MENUENTRY (g_list_nth_data (menu->entries, pos));
737 /* mouse wheel */
738 if ((local.buttons & GPM_B_UP) != 0 && (local.type & GPM_DOWN) != 0)
740 menubar_up (menubar);
741 return MOU_NORMAL;
743 if ((local.buttons & GPM_B_DOWN) != 0 && (local.type & GPM_DOWN) != 0)
745 menubar_down (menubar);
746 return MOU_NORMAL;
749 /* ignore events above and below dropped down menu */
750 if ((pos < 0) || (pos >= bottom_y - 3))
751 return MOU_NORMAL;
753 if ((entry != NULL) && (entry->command != CK_IgnoreKey))
755 menubar_paint_idx (menubar, menu->selected, MENU_ENTRY_COLOR);
756 menu->selected = pos;
757 menubar_paint_idx (menubar, menu->selected, MENU_SELECTED_COLOR);
759 if ((event->type & GPM_UP) != 0)
760 menubar_execute (menubar);
763 else if (((local.type & GPM_DOWN) != 0) && ((local.buttons & (GPM_B_UP | GPM_B_DOWN)) == 0))
765 /* use click not wheel to close menu */
766 menubar_finish (menubar);
769 return MOU_NORMAL;
772 /* --------------------------------------------------------------------------------------------- */
773 /*** public functions ****************************************************************************/
774 /* --------------------------------------------------------------------------------------------- */
776 menu_entry_t *
777 menu_entry_create (const char *name, long command)
779 menu_entry_t *entry;
781 entry = g_new (menu_entry_t, 1);
782 entry->first_letter = ' ';
783 entry->text = parse_hotkey (name);
784 entry->command = command;
785 entry->shortcut = NULL;
787 return entry;
790 /* --------------------------------------------------------------------------------------------- */
792 void
793 menu_entry_free (menu_entry_t * entry)
795 if (entry != NULL)
797 release_hotkey (entry->text);
798 g_free (entry->shortcut);
799 g_free (entry);
803 /* --------------------------------------------------------------------------------------------- */
805 menu_t *
806 create_menu (const char *name, GList * entries, const char *help_node)
808 menu_t *menu;
810 menu = g_new (menu_t, 1);
811 menu->start_x = 0;
812 menu->text = parse_hotkey (name);
813 menu->entries = entries;
814 menu->max_entry_len = 1;
815 menu->max_hotkey_len = 0;
816 menu->selected = 0;
817 menu->help_node = g_strdup (help_node);
819 return menu;
822 /* --------------------------------------------------------------------------------------------- */
824 void
825 menu_set_name (menu_t * menu, const char *name)
827 release_hotkey (menu->text);
828 menu->text = parse_hotkey (name);
831 /* --------------------------------------------------------------------------------------------- */
833 void
834 destroy_menu (menu_t * menu)
836 release_hotkey (menu->text);
837 g_list_free_full (menu->entries, (GDestroyNotify) menu_entry_free);
838 g_free (menu->help_node);
839 g_free (menu);
842 /* --------------------------------------------------------------------------------------------- */
844 WMenuBar *
845 menubar_new (int y, int x, int cols, GList * menu, gboolean visible)
847 WMenuBar *menubar;
848 Widget *w;
850 menubar = g_new0 (WMenuBar, 1);
851 w = WIDGET (menubar);
852 widget_init (w, y, x, 1, cols, menubar_callback, menubar_event);
854 menubar->is_visible = visible;
855 widget_want_cursor (w, FALSE);
856 menubar_set_menu (menubar, menu);
858 return menubar;
861 /* --------------------------------------------------------------------------------------------- */
863 void
864 menubar_set_menu (WMenuBar * menubar, GList * menu)
866 /* delete previous menu */
867 if (menubar->menu != NULL)
868 g_list_free_full (menubar->menu, (GDestroyNotify) destroy_menu);
870 /* add new menu */
871 menubar->is_active = FALSE;
872 menubar->is_dropped = FALSE;
873 menubar->menu = menu;
874 menubar->selected = 0;
875 menubar_arrange (menubar);
878 /* --------------------------------------------------------------------------------------------- */
880 void
881 menubar_add_menu (WMenuBar * menubar, menu_t * menu)
883 if (menu != NULL)
885 menu_arrange (menu, WIDGET (menubar)->owner->get_shortcut);
886 menubar->menu = g_list_append (menubar->menu, menu);
889 menubar_arrange (menubar);
892 /* --------------------------------------------------------------------------------------------- */
894 * Properly space menubar items. Should be called when menubar is created
895 * and also when widget width is changed (i.e. upon xterm resize).
898 void
899 menubar_arrange (WMenuBar * menubar)
901 int start_x = 1;
902 GList *i;
903 int gap;
905 if (menubar->menu == NULL)
906 return;
908 gap = WIDGET (menubar)->cols - 2;
910 /* First, calculate gap between items... */
911 for (i = menubar->menu; i != NULL; i = g_list_next (i))
913 menu_t *menu = MENU (i->data);
915 /* preserve length here, to be used below */
916 menu->start_x = hotkey_width (menu->text) + 2;
917 gap -= menu->start_x;
920 if (g_list_next (menubar->menu) == NULL)
921 gap = 1;
922 else
923 gap /= (g_list_length (menubar->menu) - 1);
925 if (gap <= 0)
927 /* We are out of luck - window is too narrow... */
928 gap = 1;
930 else if (gap >= 3)
931 gap = 3;
933 /* ...and now fix start positions of menubar items */
934 for (i = menubar->menu; i != NULL; i = g_list_next (i))
936 menu_t *menu = MENU (i->data);
937 int len = menu->start_x;
939 menu->start_x = start_x;
940 start_x += len + gap;
944 /* --------------------------------------------------------------------------------------------- */
945 /** Find MenuBar widget in the dialog */
947 WMenuBar *
948 find_menubar (const WDialog * h)
950 return MENUBAR (find_widget_type (h, menubar_callback));
953 /* --------------------------------------------------------------------------------------------- */