Updated italian translation
[midnight-commander.git] / src / menu.c
blobe95fdc5a048597e309ccab884f67fe40aa25ae58
1 /* Pulldown menu code.
2 Copyright (C) 1994 Miguel de Icaza.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18 #include <config.h>
20 #include <ctype.h>
21 #include <stdarg.h>
22 #include <string.h>
24 #include <sys/types.h>
26 #include "global.h"
27 #include "tty.h"
28 #include "menu.h"
29 #include "help.h"
30 #include "dialog.h"
31 #include "color.h"
32 #include "main.h"
33 #include "mouse.h"
34 #include "win.h"
35 #include "key.h" /* For mi_getch() */
37 int menubar_visible = 1; /* This is the new default */
39 static void
40 menu_scan_hotkey (Menu *menu)
42 char *cp = strchr (menu->name, '&');
44 if (cp != NULL && cp[1] != '\0') {
45 g_strlcpy (cp, cp + 1, strlen (cp));
46 menu->hotkey = tolower ((unsigned char) *cp);
47 } else
48 menu->hotkey = 0;
51 Menu *
52 create_menu (const char *name, menu_entry *entries, int count, const char *help_node)
54 Menu *menu;
55 const char *cp;
57 menu = (Menu *) g_malloc (sizeof (*menu));
58 menu->count = count;
59 menu->max_entry_len = 20;
60 menu->entries = entries;
62 if (entries != (menu_entry*) NULL) {
63 register menu_entry* mp;
64 for (mp = entries; count--; mp++) {
65 if (mp->text[0] != '\0') {
66 #ifdef ENABLE_NLS
67 mp->text = _(mp->text);
68 #endif /* ENABLE_NLS */
69 cp = strchr (mp->text,'&');
71 if (cp != NULL && *(cp+1) != '\0') {
72 mp->hot_key = tolower ((unsigned char) *(cp+1));
73 menu->max_entry_len = max ((int) (strlen (mp->text) - 1),
74 menu->max_entry_len);
75 } else {
76 menu->max_entry_len = max ((int) strlen (mp->text),
77 menu->max_entry_len);
83 menu->name = g_strdup (name);
84 menu_scan_hotkey(menu);
85 menu->start_x = 0;
86 menu->help_node = g_strdup (help_node);
87 return menu;
90 static void menubar_drop_compute (WMenu *menubar)
92 menubar->max_entry_len = menubar->menu [menubar->selected]->max_entry_len;
95 static void menubar_paint_idx (WMenu *menubar, int idx, int color)
97 const Menu *menu = menubar->menu [menubar->selected];
98 const int y = 2 + idx;
99 int x = menubar-> menu[menubar->selected]->start_x;
101 if (x + menubar->max_entry_len + 3 > menubar->widget.cols)
102 x = menubar->widget.cols - menubar->max_entry_len - 3;
104 widget_move (&menubar->widget, y, x);
105 attrset (color);
106 hline (' ', menubar->max_entry_len+2);
107 if (!*menu->entries [idx].text) {
108 attrset (SELECTED_COLOR);
109 widget_move (&menubar->widget, y, x + 1);
110 hline (slow_terminal ? ' ' : ACS_HLINE, menubar->max_entry_len);
111 } else {
112 const char *text;
114 addch((unsigned char)menu->entries [idx].first_letter);
115 for (text = menu->entries [idx].text; *text; text++)
117 if (*text != '&')
118 addch(*text);
119 else {
120 attrset (color == MENU_SELECTED_COLOR ?
121 MENU_HOTSEL_COLOR : MENU_HOT_COLOR);
122 addch(*(++text));
123 attrset(color);
127 widget_move (&menubar->widget, y, x + 1);
130 static inline void menubar_draw_drop (WMenu *menubar)
132 const int count = (menubar->menu [menubar->selected])->count;
133 int i;
134 int sel = menubar->subsel;
135 int column = menubar-> menu[menubar->selected]->start_x - 1;
137 if (column + menubar->max_entry_len + 4 > menubar->widget.cols)
138 column = menubar->widget.cols - menubar->max_entry_len - 4;
140 attrset (SELECTED_COLOR);
141 draw_box (menubar->widget.parent,
142 menubar->widget.y+1, menubar->widget.x + column,
143 count+2, menubar->max_entry_len + 4);
145 column++;
146 for (i = 0; i < count; i++){
147 if (i == sel)
148 continue;
149 menubar_paint_idx (menubar, i, MENU_ENTRY_COLOR);
151 menubar_paint_idx (menubar, sel, MENU_SELECTED_COLOR);
154 static void menubar_draw (WMenu *menubar)
156 const int items = menubar->items;
157 int i;
159 /* First draw the complete menubar */
160 attrset (SELECTED_COLOR);
161 widget_move (&menubar->widget, 0, 0);
163 hline (' ', menubar->widget.cols);
165 attrset (SELECTED_COLOR);
166 /* Now each one of the entries */
167 for (i = 0; i < items; i++){
168 if (menubar->active)
169 attrset(i == menubar->selected?MENU_SELECTED_COLOR:SELECTED_COLOR);
170 widget_move (&menubar->widget, 0, menubar->menu [i]->start_x);
171 tty_printf ("%s", menubar->menu [i]->name);
174 if (menubar->dropped)
175 menubar_draw_drop (menubar);
176 else
177 widget_move (&menubar->widget, 0,
178 menubar-> menu[menubar->selected]->start_x);
181 static inline void menubar_remove (WMenu *menubar)
183 menubar->subsel = 0;
184 if (menubar->dropped){
185 menubar->dropped = 0;
186 do_refresh ();
187 menubar->dropped = 1;
191 static void menubar_left (WMenu *menu)
193 menubar_remove (menu);
194 menu->selected = (menu->selected - 1) % menu->items;
195 if (menu->selected < 0)
196 menu->selected = menu->items -1;
197 menubar_drop_compute (menu);
198 menubar_draw (menu);
201 static void menubar_right (WMenu *menu)
203 menubar_remove (menu);
204 menu->selected = (menu->selected + 1) % menu->items;
205 menubar_drop_compute (menu);
206 menubar_draw (menu);
209 static void
210 menubar_finish (WMenu *menubar)
212 menubar->dropped = 0;
213 menubar->active = 0;
214 menubar->widget.lines = 1;
215 widget_want_hotkey (menubar->widget, 0);
217 dlg_select_by_id (menubar->widget.parent, menubar->previous_widget);
218 do_refresh ();
221 static void menubar_drop (WMenu *menubar, int selected)
223 menubar->dropped = 1;
224 menubar->selected = selected;
225 menubar->subsel = 0;
226 menubar_drop_compute (menubar);
227 menubar_draw (menubar);
230 static void menubar_execute (WMenu *menubar, int entry)
232 const Menu *menu = menubar->menu [menubar->selected];
233 const callfn call_back = menu->entries [entry].call_back;
235 is_right = menubar->selected != 0;
237 /* This used to be the other way round, i.e. first callback and
238 then menubar_finish. The new order (hack?) is needed to make
239 change_panel () work which is used in quick_view_cmd () -- Norbert
241 menubar_finish (menubar);
242 (*call_back) ();
243 do_refresh ();
246 static void menubar_move (WMenu *menubar, int step)
248 const Menu *menu = menubar->menu [menubar->selected];
250 menubar_paint_idx (menubar, menubar->subsel, MENU_ENTRY_COLOR);
251 do {
252 menubar->subsel += step;
253 if (menubar->subsel < 0)
254 menubar->subsel = menu->count - 1;
256 menubar->subsel %= menu->count;
257 } while (!menu->entries [menubar->subsel].call_back);
258 menubar_paint_idx (menubar, menubar->subsel, MENU_SELECTED_COLOR);
261 static int menubar_handle_key (WMenu *menubar, int key)
263 int i;
265 /* Lowercase */
266 if (key < 256 && isalpha (key)) /* Linux libc.so.5.x.x bug fix */
267 key = tolower (key);
269 if (is_abort_char (key)){
270 menubar_finish (menubar);
271 return 1;
274 if (key == KEY_F(1)) {
275 if (menubar->dropped) {
276 interactive_display (NULL,
277 (menubar->menu [menubar->selected])->help_node);
278 } else {
279 interactive_display (NULL, "[Menu Bar]");
281 menubar_draw (menubar);
282 return 1;
285 if (key == KEY_LEFT || key == XCTRL('b')){
286 menubar_left (menubar);
287 return 1;
288 } else if (key == KEY_RIGHT || key == XCTRL ('f')){
289 menubar_right (menubar);
290 return 1;
293 if (!menubar->dropped){
294 const int items = menubar->items;
295 for (i = 0; i < items; i++){
296 const Menu *menu = menubar->menu [i];
298 if (menu->hotkey == key){
299 menubar_drop (menubar, i);
300 return 1;
303 if (key == KEY_ENTER || key == XCTRL ('n') || key == KEY_DOWN
304 || key == '\n'){
305 menubar_drop (menubar, menubar->selected);
306 return 1;
308 return 1;
309 } else {
310 const int selected = menubar->selected;
311 const Menu *menu = menubar->menu [selected];
312 const int items = menu->count;
314 for (i = 0; i < items; i++){
315 if (!menu->entries [i].call_back)
316 continue;
318 if (key != menu->entries [i].hot_key)
319 continue;
321 menubar_execute (menubar, i);
322 return 1;
325 if (key == KEY_ENTER || key == '\n'){
326 menubar_execute (menubar, menubar->subsel);
327 return 1;
331 if (key == KEY_DOWN || key == XCTRL ('n'))
332 menubar_move (menubar, 1);
334 if (key == KEY_UP || key == XCTRL ('p'))
335 menubar_move (menubar, -1);
337 return 0;
340 static cb_ret_t
341 menubar_callback (Widget *w, widget_msg_t msg, int parm)
343 WMenu *menubar = (WMenu *) w;
345 switch (msg) {
346 /* We do not want the focus unless we have been activated */
347 case WIDGET_FOCUS:
348 if (!menubar->active)
349 return MSG_NOT_HANDLED;
351 widget_want_cursor (menubar->widget, 1);
353 /* Trick to get all the mouse events */
354 menubar->widget.lines = LINES;
356 /* Trick to get all of the hotkeys */
357 widget_want_hotkey (menubar->widget, 1);
358 menubar->subsel = 0;
359 menubar_drop_compute (menubar);
360 menubar_draw (menubar);
361 return MSG_HANDLED;
363 /* We don't want the buttonbar to activate while using the menubar */
364 case WIDGET_HOTKEY:
365 case WIDGET_KEY:
366 if (menubar->active) {
367 menubar_handle_key (menubar, parm);
368 return MSG_HANDLED;
369 } else
370 return MSG_NOT_HANDLED;
372 case WIDGET_CURSOR:
373 /* Put the cursor in a suitable place */
374 return MSG_NOT_HANDLED;
376 case WIDGET_UNFOCUS:
377 if (menubar->active)
378 return MSG_NOT_HANDLED;
379 else {
380 widget_want_cursor (menubar->widget, 0);
381 return MSG_HANDLED;
384 case WIDGET_DRAW:
385 if (menubar_visible)
386 menubar_draw (menubar);
387 return MSG_HANDLED;
389 default:
390 return default_proc (msg, parm);
394 static int
395 menubar_event (Gpm_Event *event, void *data)
397 WMenu *menubar = data;
398 int was_active;
399 int new_selection;
400 int left_x, right_x, bottom_y;
402 if (!(event->type & (GPM_UP|GPM_DOWN|GPM_DRAG)))
403 return MOU_NORMAL;
405 if (!menubar->dropped){
406 menubar->previous_widget = menubar->widget.parent->current->dlg_id;
407 menubar->active = 1;
408 menubar->dropped = 1;
409 was_active = 0;
410 } else
411 was_active = 1;
413 /* Mouse operations on the menubar */
414 if (event->y == 1 || !was_active){
415 if (event->type & GPM_UP)
416 return MOU_NORMAL;
418 new_selection = 0;
419 while (new_selection < menubar->items
420 && event->x > menubar->menu[new_selection]->start_x
422 new_selection++;
424 if (new_selection) /* Don't set the invalid value -1 */
425 --new_selection;
427 if (!was_active){
428 menubar->selected = new_selection;
429 dlg_select_widget (menubar);
430 menubar_drop_compute (menubar);
431 menubar_draw (menubar);
432 return MOU_NORMAL;
435 menubar_remove (menubar);
437 menubar->selected = new_selection;
439 menubar_drop_compute (menubar);
440 menubar_draw (menubar);
441 return MOU_NORMAL;
444 if (!menubar->dropped)
445 return MOU_NORMAL;
447 /* Ignore the events on anything below the third line */
448 if (event->y <= 2)
449 return MOU_NORMAL;
451 /* Else, the mouse operation is on the menus or it is not */
452 left_x = menubar->menu[menubar->selected]->start_x;
453 right_x = left_x + menubar->max_entry_len + 4;
454 if (right_x > menubar->widget.cols)
456 left_x = menubar->widget.cols - menubar->max_entry_len - 3;
457 right_x = menubar->widget.cols - 1;
460 bottom_y = (menubar->menu [menubar->selected])->count + 3;
462 if ((event->x > left_x) && (event->x < right_x) && (event->y < bottom_y)){
463 int pos = event->y - 3;
465 if (!menubar->menu [menubar->selected]->entries [pos].call_back)
466 return MOU_NORMAL;
468 menubar_paint_idx (menubar, menubar->subsel, MENU_ENTRY_COLOR);
469 menubar->subsel = pos;
470 menubar_paint_idx (menubar, menubar->subsel, MENU_SELECTED_COLOR);
472 if (event->type & GPM_UP)
473 menubar_execute (menubar, pos);
474 } else
475 if (event->type & GPM_DOWN)
476 menubar_finish (menubar);
478 return MOU_NORMAL;
482 * Properly space menubar items. Should be called when menubar is created
483 * and also when widget width is changed (i.e. upon xterm resize).
485 void
486 menubar_arrange(WMenu* menubar)
488 register int i, start_x = 1;
489 int items = menubar->items;
491 #ifndef RESIZABLE_MENUBAR
492 int gap = 3;
494 for (i = 0; i < items; i++)
496 int len = strlen(menubar->menu[i]->name);
497 menubar->menu[i]->start_x = start_x;
498 start_x += len + gap;
501 #else /* RESIZABLE_MENUBAR */
503 int gap = menubar->widget.cols - 2;
505 /* First, calculate gap between items... */
506 for (i = 0; i < items; i++)
508 /* preserve length here, to be used below */
509 gap -= (menubar->menu[i]->start_x = strlen(menubar->menu[i]->name));
512 gap /= (items - 1);
514 if (gap <= 0)
516 /* We are out of luck - window is too narrow... */
517 gap = 1;
520 /* ...and now fix start positions of menubar items */
521 for (i = 0; i < items; i++)
523 int len = menubar->menu[i]->start_x;
524 menubar->menu[i]->start_x = start_x;
525 start_x += len + gap;
527 #endif /* RESIZABLE_MENUBAR */
530 void
531 destroy_menu (Menu *menu)
533 g_free (menu->name);
534 g_free (menu->help_node);
535 g_free (menu);
538 WMenu *
539 menubar_new (int y, int x, int cols, Menu *menu[], int items)
541 WMenu *menubar = g_new0 (WMenu, 1); /* FIXME: subsel used w/o being set */
543 init_widget (&menubar->widget, y, x, 1, cols,
544 menubar_callback, menubar_event);
545 menubar->menu = menu;
546 menubar->active = 0;
547 menubar->dropped = 0;
548 menubar->items = items;
549 menubar->selected = 0;
550 widget_want_cursor (menubar->widget, 0);
551 menubar_arrange (menubar);
553 return menubar;