Just a little correction at the it.po file.
[midnight-commander.git] / src / menu.c
blob64aa27dc46b4dc831d0b94e7fedc5b81d51e7e32
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 #include <config.h>
19 #include <string.h>
20 #include <stdarg.h>
21 #include <sys/types.h>
22 #include <ctype.h>
23 #include "global.h"
24 #include "tty.h"
25 #include "menu.h"
26 #include "help.h"
27 #include "dialog.h"
28 #include "color.h"
29 #include "main.h"
30 #include "mouse.h"
31 #include "win.h"
32 #include "key.h" /* For mi_getch() */
34 int menubar_visible = 1; /* This is the new default */
36 static void
37 menu_scan_hotkey(Menu *menu)
39 char* cp = strchr (menu->name, '&');
41 if (cp != NULL && cp[1] != '\0'){
42 strcpy (cp, cp+1);
43 menu->hotkey = tolower(*cp);
45 else
46 menu->hotkey = 0;
49 Menu *
50 create_menu (char *name, menu_entry *entries, int count, char *help_node)
52 Menu *menu;
53 char *cp;
55 menu = (Menu *) g_malloc (sizeof (*menu));
56 menu->count = count;
57 menu->max_entry_len = 20;
58 menu->entries = entries;
60 if (entries != (menu_entry*) NULL) {
61 register menu_entry* mp;
62 for (mp = entries; count--; mp++) {
63 if (mp->text[0] != '\0') {
64 #ifdef ENABLE_NLS
65 mp->text = _(mp->text);
66 #endif /* ENABLE_NLS */
67 cp = strchr (mp->text,'&');
69 if (cp != NULL && *(cp+1) != '\0') {
70 mp->hot_key = tolower (*(cp+1));
71 menu->max_entry_len = max (strlen (mp->text) - 1,
72 menu->max_entry_len);
73 } else {
74 menu->max_entry_len = max (strlen (mp->text),
75 menu->max_entry_len);
81 menu->name = g_strdup (name);
82 menu_scan_hotkey(menu);
83 menu->start_x = 0;
84 menu->help_node = g_strdup (help_node);
85 return menu;
88 static void menubar_drop_compute (WMenu *menubar)
90 menubar->max_entry_len = menubar->menu [menubar->selected]->max_entry_len;
93 static void menubar_paint_idx (WMenu *menubar, int idx, int color)
95 const Menu *menu = menubar->menu [menubar->selected];
96 const int y = 2 + idx;
97 int x = menubar-> menu[menubar->selected]->start_x;
99 if (x + menubar->max_entry_len + 3 > menubar->widget.cols)
100 x = menubar->widget.cols - menubar->max_entry_len - 3;
102 widget_move (&menubar->widget, y, x);
103 attrset (color);
104 hline (' ', menubar->max_entry_len+2);
105 if (!*menu->entries [idx].text) {
106 attrset (SELECTED_COLOR);
107 widget_move (&menubar->widget, y, x + 1);
108 hline (slow_terminal ? ' ' : ACS_HLINE, menubar->max_entry_len);
109 } else {
110 unsigned char *text;
112 addch((unsigned char)menu->entries [idx].first_letter);
113 for (text = menu->entries [idx].text; *text; text++)
115 if (*text != '&')
116 addch(*text);
117 else {
118 attrset (color == MENU_SELECTED_COLOR ?
119 MENU_HOTSEL_COLOR : MENU_HOT_COLOR);
120 addch(*(++text));
121 attrset(color);
125 widget_move (&menubar->widget, y, x + 1);
128 static inline void menubar_draw_drop (WMenu *menubar)
130 const int count = (menubar->menu [menubar->selected])->count;
131 int i;
132 int sel = menubar->subsel;
133 int column = menubar-> menu[menubar->selected]->start_x - 1;
135 if (column + menubar->max_entry_len + 4 > menubar->widget.cols)
136 column = menubar->widget.cols - menubar->max_entry_len - 4;
138 attrset (SELECTED_COLOR);
139 draw_box (menubar->widget.parent,
140 menubar->widget.y+1, menubar->widget.x + column,
141 count+2, menubar->max_entry_len + 4);
143 column++;
144 for (i = 0; i < count; i++){
145 if (i == sel)
146 continue;
147 menubar_paint_idx (menubar, i, MENU_ENTRY_COLOR);
149 menubar_paint_idx (menubar, sel, MENU_SELECTED_COLOR);
152 static void menubar_draw (WMenu *menubar)
154 const int items = menubar->items;
155 int i;
157 /* First draw the complete menubar */
158 attrset (SELECTED_COLOR);
159 widget_move (&menubar->widget, 0, 0);
161 /* ncurses bug: it should work with hline but it does not */
162 for (i = menubar->widget.cols; i; i--)
163 addch (' ');
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 printw ("%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 menubar_finish (WMenu *menubar)
211 menubar->dropped = 0;
212 menubar->active = 0;
213 menubar->widget.lines = 1;
214 widget_want_hotkey (menubar->widget, 0);
215 dlg_select_nth_widget (menubar->widget.parent,
216 menubar->previous_selection);
217 do_refresh ();
220 static void menubar_drop (WMenu *menubar, int selected)
222 menubar->dropped = 1;
223 menubar->selected = selected;
224 menubar->subsel = 0;
225 menubar_drop_compute (menubar);
226 menubar_draw (menubar);
229 static void menubar_execute (WMenu *menubar, int entry)
231 const Menu *menu = menubar->menu [menubar->selected];
232 const callfn call_back = menu->entries [entry].call_back;
234 is_right = menubar->selected != 0;
236 /* This used to be the other way round, i.e. first callback and
237 then menubar_finish. The new order (hack?) is needed to make
238 change_panel () work which is used in quick_view_cmd () -- Norbert
240 menubar_finish (menubar);
241 (*call_back) ();
242 do_refresh ();
245 static void menubar_move (WMenu *menubar, int step)
247 const Menu *menu = menubar->menu [menubar->selected];
249 menubar_paint_idx (menubar, menubar->subsel, MENU_ENTRY_COLOR);
250 do {
251 menubar->subsel += step;
252 if (menubar->subsel < 0)
253 menubar->subsel = menu->count - 1;
255 menubar->subsel %= menu->count;
256 } while (!menu->entries [menubar->subsel].call_back);
257 menubar_paint_idx (menubar, menubar->subsel, MENU_SELECTED_COLOR);
260 static int menubar_handle_key (WMenu *menubar, int key)
262 int i;
264 /* Lowercase */
265 if (key < 256 && isalpha (key)) /* Linux libc.so.5.x.x bug fix */
266 key = tolower (key);
268 if (is_abort_char (key)){
269 menubar_finish (menubar);
270 return 1;
273 if (key == KEY_F(1)) {
274 if (menubar->dropped) {
275 interactive_display (NULL,
276 (menubar->menu [menubar->selected])->help_node);
277 } else {
278 interactive_display (NULL, "[Menu Bar]");
280 menubar_draw (menubar);
281 return 1;
284 if (key == KEY_LEFT || key == XCTRL('b')){
285 menubar_left (menubar);
286 return 1;
287 } else if (key == KEY_RIGHT || key == XCTRL ('f')){
288 menubar_right (menubar);
289 return 1;
292 /* .ado: NT Alpha cannot allow CTRL in Menubar */
293 #if defined(NATIVE_WIN32)
294 if (!key)
295 return 0;
296 #endif
298 if (!menubar->dropped){
299 const int items = menubar->items;
300 for (i = 0; i < items; i++){
301 const Menu *menu = menubar->menu [i];
303 if (menu->hotkey == key){
304 menubar_drop (menubar, i);
305 return 1;
308 if (key == KEY_ENTER || key == XCTRL ('n') || key == KEY_DOWN
309 || key == '\n'){
310 menubar_drop (menubar, menubar->selected);
311 return 1;
313 return 1;
314 } else {
315 const int selected = menubar->selected;
316 const Menu *menu = menubar->menu [selected];
317 const int items = menu->count;
319 for (i = 0; i < items; i++){
320 if (!menu->entries [i].call_back)
321 continue;
323 if (key != menu->entries [i].hot_key)
324 continue;
326 menubar_execute (menubar, i);
327 return 1;
330 if (key == KEY_ENTER || key == '\n'){
331 menubar_execute (menubar, menubar->subsel);
332 return 1;
336 if (key == KEY_DOWN || key == XCTRL ('n'))
337 menubar_move (menubar, 1);
339 if (key == KEY_UP || key == XCTRL ('p'))
340 menubar_move (menubar, -1);
342 return 0;
345 static int menubar_callback (WMenu *menubar, int msg, int par)
347 switch (msg){
348 /* We do not want the focus unless we have been activated */
349 case WIDGET_FOCUS:
350 if (menubar->active){
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 1;
362 } else
363 return 0;
365 /* We don't want the buttonbar to activate while using the menubar */
366 case WIDGET_HOTKEY:
367 case WIDGET_KEY:
368 if (menubar->active){
369 menubar_handle_key (menubar, par);
370 return 1;
371 } else
372 return 0;
374 case WIDGET_CURSOR:
375 /* Put the cursor in a suitable place */
376 return 0;
378 case WIDGET_UNFOCUS:
379 if (menubar->active)
380 return 0;
381 else {
382 widget_want_cursor (menubar->widget, 0);
383 return 1;
386 case WIDGET_DRAW:
387 if (menubar_visible)
388 menubar_draw (menubar);
390 return default_proc (msg, par);
393 static int
394 menubar_event (Gpm_Event *event, WMenu *menubar)
396 int was_active;
397 int new_selection;
398 int left_x, right_x, bottom_y;
400 if (!(event->type & (GPM_UP|GPM_DOWN|GPM_DRAG)))
401 return MOU_NORMAL;
403 if (!menubar->dropped){
404 menubar->previous_selection = dlg_item_number(menubar->widget.parent);
405 menubar->active = 1;
406 menubar->dropped = 1;
407 was_active = 0;
408 } else
409 was_active = 1;
411 /* Mouse operations on the menubar */
412 if (event->y == 1 || !was_active){
413 if (event->type & GPM_UP)
414 return MOU_NORMAL;
416 new_selection = 0;
417 while (new_selection < menubar->items
418 && event->x > menubar->menu[new_selection]->start_x
420 new_selection++;
422 if (new_selection) /* Don't set the invalid value -1 */
423 --new_selection;
425 if (!was_active){
426 menubar->selected = new_selection;
427 dlg_select_widget (menubar->widget.parent, menubar);
428 menubar_drop_compute (menubar);
429 menubar_draw (menubar);
430 return MOU_NORMAL;
433 menubar_remove (menubar);
435 menubar->selected = new_selection;
437 menubar_drop_compute (menubar);
438 menubar_draw (menubar);
439 return MOU_NORMAL;
442 if (!menubar->dropped)
443 return MOU_NORMAL;
445 /* Ignore the events on anything below the third line */
446 if (event->y <= 2)
447 return MOU_NORMAL;
449 /* Else, the mouse operation is on the menus or it is not */
450 left_x = menubar->menu[menubar->selected]->start_x;
451 right_x = left_x + menubar->max_entry_len + 4;
452 if (right_x > menubar->widget.cols)
454 left_x = menubar->widget.cols - menubar->max_entry_len - 3;
455 right_x = menubar->widget.cols - 1;
458 bottom_y = (menubar->menu [menubar->selected])->count + 3;
460 if ((event->x > left_x) && (event->x < right_x) && (event->y < bottom_y)){
461 int pos = event->y - 3;
463 if (!menubar->menu [menubar->selected]->entries [pos].call_back)
464 return MOU_NORMAL;
466 menubar_paint_idx (menubar, menubar->subsel, MENU_ENTRY_COLOR);
467 menubar->subsel = pos;
468 menubar_paint_idx (menubar, menubar->subsel, MENU_SELECTED_COLOR);
470 if (event->type & GPM_UP)
471 menubar_execute (menubar, pos);
472 } else
473 if (event->type & GPM_DOWN)
474 menubar_finish (menubar);
476 return MOU_NORMAL;
479 static void menubar_destroy (WMenu *menubar)
484 * Properly space menubar items. Should be called when menubar is created
485 * and also when widget width is changed (i.e. upon xterm resize).
487 void
488 menubar_arrange(WMenu* menubar)
490 register int i, start_x = 1;
491 int items = menubar->items;
493 #ifndef RESIZABLE_MENUBAR
494 int gap = 3;
496 for (i = 0; i < items; i++)
498 int len = strlen(menubar->menu[i]->name);
499 menubar->menu[i]->start_x = start_x;
500 start_x += len + gap;
503 #else /* RESIZABLE_MENUBAR */
505 int gap = menubar->widget.cols - 2;
507 /* First, calculate gap between items... */
508 for (i = 0; i < items; i++)
510 /* preserve length here, to be used below */
511 gap -= (menubar->menu[i]->start_x = strlen(menubar->menu[i]->name));
514 gap /= (items - 1);
516 if (gap <= 0)
518 /* We are out of luck - window is too narrow... */
519 gap = 1;
522 /* ...and now fix start positions of menubar items */
523 for (i = 0; i < items; i++)
525 int len = menubar->menu[i]->start_x;
526 menubar->menu[i]->start_x = start_x;
527 start_x += len + gap;
529 #endif /* RESIZABLE_MENUBAR */
532 void
533 destroy_menu (Menu *menu)
535 g_free (menu->name);
536 g_free (menu->help_node);
537 g_free (menu);
540 WMenu *menubar_new (int y, int x, int cols, Menu *menu [], int items)
542 WMenu *menubar = g_new0 (WMenu, 1); /* FIXME: subsel used w/o being set */
544 init_widget (&menubar->widget, y, x, 1, cols,
545 (callback_fn) menubar_callback,
546 (destroy_fn) menubar_destroy,
547 (mouse_h) menubar_event, NULL);
548 menubar->menu = menu;
549 menubar->active = 0;
550 menubar->dropped = 0;
551 menubar->items = items;
552 menubar->selected = 0;
553 widget_want_cursor (menubar->widget, 0);
554 menubar_arrange(menubar);
556 return menubar;