Not only comment it out but removing it
[midnight-commander.git] / src / menu.c
blobf291071a641bd13c8598491832d0ceba4855759c
1 /* Pulldown menu code.
2 Copyright (C) 1994, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2007 Free Software Foundation, Inc.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19 #include <config.h>
21 #include <ctype.h>
22 #include <stdarg.h>
23 #include <string.h>
25 #include <sys/types.h>
27 #include "global.h"
28 #include "tty.h"
29 #include "menu.h"
30 #include "help.h"
31 #include "dialog.h"
32 #include "color.h"
33 #include "main.h"
34 #include "mouse.h"
35 #include "win.h"
36 #include "key.h" /* For mi_getch() */
38 int menubar_visible = 1; /* This is the new default */
40 static void
41 menu_scan_hotkey (Menu *menu)
43 char *cp = strchr (menu->name, '&');
45 if (cp != NULL && cp[1] != '\0') {
46 g_strlcpy (cp, cp + 1, strlen (cp));
47 menu->hotkey = tolower ((unsigned char) *cp);
48 } else
49 menu->hotkey = 0;
52 Menu *
53 create_menu (const char *name, menu_entry *entries, int count, const char *help_node)
55 Menu *menu;
56 const char *cp;
58 menu = (Menu *) g_malloc (sizeof (*menu));
59 menu->count = count;
60 menu->max_entry_len = 20;
61 menu->entries = entries;
63 if (entries != (menu_entry*) NULL) {
64 register menu_entry* mp;
65 for (mp = entries; count--; mp++) {
66 if (mp->text[0] != '\0') {
67 #ifdef ENABLE_NLS
68 mp->text = _(mp->text);
69 #endif /* ENABLE_NLS */
70 cp = strchr (mp->text,'&');
72 if (cp != NULL && *(cp+1) != '\0') {
73 mp->hot_key = tolower ((unsigned char) *(cp+1));
74 menu->max_entry_len = max ((int) (strlen (mp->text) - 1),
75 menu->max_entry_len);
76 } else {
77 menu->max_entry_len = max ((int) strlen (mp->text),
78 menu->max_entry_len);
84 menu->name = g_strdup (name);
85 menu_scan_hotkey(menu);
86 menu->start_x = 0;
87 menu->help_node = g_strdup (help_node);
88 return menu;
91 static void menubar_drop_compute (WMenu *menubar)
93 menubar->max_entry_len = menubar->menu [menubar->selected]->max_entry_len;
96 static void menubar_paint_idx (WMenu *menubar, int idx, int color)
98 const Menu *menu = menubar->menu [menubar->selected];
99 const int y = 2 + idx;
100 int x = menubar-> menu[menubar->selected]->start_x;
102 if (x + menubar->max_entry_len + 3 > menubar->widget.cols)
103 x = menubar->widget.cols - menubar->max_entry_len - 3;
105 widget_move (&menubar->widget, y, x);
106 attrset (color);
107 hline (' ', menubar->max_entry_len+2);
108 if (!*menu->entries [idx].text) {
109 attrset (SELECTED_COLOR);
110 widget_move (&menubar->widget, y, x + 1);
111 hline (slow_terminal ? ' ' : ACS_HLINE, menubar->max_entry_len);
112 } else {
113 const char *text;
115 addch((unsigned char)menu->entries [idx].first_letter);
116 for (text = menu->entries [idx].text; *text; text++)
118 if (*text != '&')
119 addch(*text);
120 else {
121 attrset (color == MENU_SELECTED_COLOR ?
122 MENU_HOTSEL_COLOR : MENU_HOT_COLOR);
123 addch(*(++text));
124 attrset(color);
128 widget_move (&menubar->widget, y, x + 1);
131 static inline void menubar_draw_drop (WMenu *menubar)
133 const int count = (menubar->menu [menubar->selected])->count;
134 int i;
135 int sel = menubar->subsel;
136 int column = menubar-> menu[menubar->selected]->start_x - 1;
138 if (column + menubar->max_entry_len + 4 > menubar->widget.cols)
139 column = menubar->widget.cols - menubar->max_entry_len - 4;
141 attrset (SELECTED_COLOR);
142 draw_box (menubar->widget.parent,
143 menubar->widget.y+1, menubar->widget.x + column,
144 count+2, menubar->max_entry_len + 4);
146 column++;
147 for (i = 0; i < count; i++){
148 if (i == sel)
149 continue;
150 menubar_paint_idx (menubar, i, MENU_ENTRY_COLOR);
152 menubar_paint_idx (menubar, sel, MENU_SELECTED_COLOR);
155 static void menubar_draw (WMenu *menubar)
157 const int items = menubar->items;
158 int i;
160 /* First draw the complete menubar */
161 attrset (SELECTED_COLOR);
162 widget_move (&menubar->widget, 0, 0);
164 hline (' ', menubar->widget.cols);
166 attrset (SELECTED_COLOR);
167 /* Now each one of the entries */
168 for (i = 0; i < items; i++){
169 if (menubar->active)
170 attrset(i == menubar->selected?MENU_SELECTED_COLOR:SELECTED_COLOR);
171 widget_move (&menubar->widget, 0, menubar->menu [i]->start_x);
172 tty_printf ("%s", menubar->menu [i]->name);
175 if (menubar->dropped)
176 menubar_draw_drop (menubar);
177 else
178 widget_move (&menubar->widget, 0,
179 menubar-> menu[menubar->selected]->start_x);
182 static inline void menubar_remove (WMenu *menubar)
184 menubar->subsel = 0;
185 if (menubar->dropped){
186 menubar->dropped = 0;
187 do_refresh ();
188 menubar->dropped = 1;
192 static void menubar_left (WMenu *menu)
194 menubar_remove (menu);
195 menu->selected = (menu->selected - 1) % menu->items;
196 if (menu->selected < 0)
197 menu->selected = menu->items -1;
198 menubar_drop_compute (menu);
199 menubar_draw (menu);
202 static void menubar_right (WMenu *menu)
204 menubar_remove (menu);
205 menu->selected = (menu->selected + 1) % menu->items;
206 menubar_drop_compute (menu);
207 menubar_draw (menu);
210 static void
211 menubar_finish (WMenu *menubar)
213 menubar->dropped = 0;
214 menubar->active = 0;
215 menubar->widget.lines = 1;
216 widget_want_hotkey (menubar->widget, 0);
218 dlg_select_by_id (menubar->widget.parent, menubar->previous_widget);
219 do_refresh ();
222 static void menubar_drop (WMenu *menubar, int selected)
224 menubar->dropped = 1;
225 menubar->selected = selected;
226 menubar->subsel = 0;
227 menubar_drop_compute (menubar);
228 menubar_draw (menubar);
231 static void menubar_execute (WMenu *menubar, int entry)
233 const Menu *menu = menubar->menu [menubar->selected];
234 const callfn call_back = menu->entries [entry].call_back;
236 is_right = menubar->selected != 0;
238 /* This used to be the other way round, i.e. first callback and
239 then menubar_finish. The new order (hack?) is needed to make
240 change_panel () work which is used in quick_view_cmd () -- Norbert
242 menubar_finish (menubar);
243 (*call_back) ();
244 do_refresh ();
247 static void menubar_move (WMenu *menubar, int step)
249 const Menu *menu = menubar->menu [menubar->selected];
251 menubar_paint_idx (menubar, menubar->subsel, MENU_ENTRY_COLOR);
252 do {
253 menubar->subsel += step;
254 if (menubar->subsel < 0)
255 menubar->subsel = menu->count - 1;
257 menubar->subsel %= menu->count;
258 } while (!menu->entries [menubar->subsel].call_back);
259 menubar_paint_idx (menubar, menubar->subsel, MENU_SELECTED_COLOR);
262 static int menubar_handle_key (WMenu *menubar, int key)
264 int i;
266 /* Lowercase */
267 if (key < 256 && isalpha (key)) /* Linux libc.so.5.x.x bug fix */
268 key = tolower (key);
270 if (is_abort_char (key)){
271 menubar_finish (menubar);
272 return 1;
275 if (key == KEY_F(1)) {
276 if (menubar->dropped) {
277 interactive_display (NULL,
278 (menubar->menu [menubar->selected])->help_node);
279 } else {
280 interactive_display (NULL, "[Menu Bar]");
282 menubar_draw (menubar);
283 return 1;
286 if (key == KEY_LEFT || key == XCTRL('b')){
287 menubar_left (menubar);
288 return 1;
289 } else if (key == KEY_RIGHT || key == XCTRL ('f')){
290 menubar_right (menubar);
291 return 1;
294 if (!menubar->dropped){
295 const int items = menubar->items;
296 for (i = 0; i < items; i++){
297 const Menu *menu = menubar->menu [i];
299 if (menu->hotkey == key){
300 menubar_drop (menubar, i);
301 return 1;
304 if (key == KEY_ENTER || key == XCTRL ('n') || key == KEY_DOWN
305 || key == '\n'){
306 menubar_drop (menubar, menubar->selected);
307 return 1;
309 return 1;
310 } else {
311 const int selected = menubar->selected;
312 const Menu *menu = menubar->menu [selected];
313 const int items = menu->count;
315 for (i = 0; i < items; i++){
316 if (!menu->entries [i].call_back)
317 continue;
319 if (key != menu->entries [i].hot_key)
320 continue;
322 menubar_execute (menubar, i);
323 return 1;
326 if (key == KEY_ENTER || key == '\n'){
327 menubar_execute (menubar, menubar->subsel);
328 return 1;
332 if (key == KEY_DOWN || key == XCTRL ('n'))
333 menubar_move (menubar, 1);
335 if (key == KEY_UP || key == XCTRL ('p'))
336 menubar_move (menubar, -1);
338 return 0;
341 static cb_ret_t
342 menubar_callback (Widget *w, widget_msg_t msg, int parm)
344 WMenu *menubar = (WMenu *) w;
346 switch (msg) {
347 /* We do not want the focus unless we have been activated */
348 case WIDGET_FOCUS:
349 if (!menubar->active)
350 return MSG_NOT_HANDLED;
352 widget_want_cursor (menubar->widget, 1);
354 /* Trick to get all the mouse events */
355 menubar->widget.lines = LINES;
357 /* Trick to get all of the hotkeys */
358 widget_want_hotkey (menubar->widget, 1);
359 menubar->subsel = 0;
360 menubar_drop_compute (menubar);
361 menubar_draw (menubar);
362 return MSG_HANDLED;
364 /* We don't want the buttonbar to activate while using the menubar */
365 case WIDGET_HOTKEY:
366 case WIDGET_KEY:
367 if (menubar->active) {
368 menubar_handle_key (menubar, parm);
369 return MSG_HANDLED;
370 } else
371 return MSG_NOT_HANDLED;
373 case WIDGET_CURSOR:
374 /* Put the cursor in a suitable place */
375 return MSG_NOT_HANDLED;
377 case WIDGET_UNFOCUS:
378 if (menubar->active)
379 return MSG_NOT_HANDLED;
380 else {
381 widget_want_cursor (menubar->widget, 0);
382 return MSG_HANDLED;
385 case WIDGET_DRAW:
386 if (menubar_visible)
387 menubar_draw (menubar);
388 return MSG_HANDLED;
390 default:
391 return default_proc (msg, parm);
395 static int
396 menubar_event (Gpm_Event *event, void *data)
398 WMenu *menubar = data;
399 int was_active;
400 int new_selection;
401 int left_x, right_x, bottom_y;
403 if (!(event->type & (GPM_UP|GPM_DOWN|GPM_DRAG)))
404 return MOU_NORMAL;
406 if (!menubar->dropped){
407 menubar->previous_widget = menubar->widget.parent->current->dlg_id;
408 menubar->active = 1;
409 menubar->dropped = 1;
410 was_active = 0;
411 } else
412 was_active = 1;
414 /* Mouse operations on the menubar */
415 if (event->y == 1 || !was_active){
416 if (event->type & GPM_UP)
417 return MOU_NORMAL;
419 new_selection = 0;
420 while (new_selection < menubar->items
421 && event->x > menubar->menu[new_selection]->start_x
423 new_selection++;
425 if (new_selection) /* Don't set the invalid value -1 */
426 --new_selection;
428 if (!was_active){
429 menubar->selected = new_selection;
430 dlg_select_widget (menubar);
431 menubar_drop_compute (menubar);
432 menubar_draw (menubar);
433 return MOU_NORMAL;
436 menubar_remove (menubar);
438 menubar->selected = new_selection;
440 menubar_drop_compute (menubar);
441 menubar_draw (menubar);
442 return MOU_NORMAL;
445 if (!menubar->dropped)
446 return MOU_NORMAL;
448 /* Ignore the events on anything below the third line */
449 if (event->y <= 2)
450 return MOU_NORMAL;
452 /* Else, the mouse operation is on the menus or it is not */
453 left_x = menubar->menu[menubar->selected]->start_x;
454 right_x = left_x + menubar->max_entry_len + 4;
455 if (right_x > menubar->widget.cols)
457 left_x = menubar->widget.cols - menubar->max_entry_len - 3;
458 right_x = menubar->widget.cols - 1;
461 bottom_y = (menubar->menu [menubar->selected])->count + 3;
463 if ((event->x > left_x) && (event->x < right_x) && (event->y < bottom_y)){
464 int pos = event->y - 3;
466 if (!menubar->menu [menubar->selected]->entries [pos].call_back)
467 return MOU_NORMAL;
469 menubar_paint_idx (menubar, menubar->subsel, MENU_ENTRY_COLOR);
470 menubar->subsel = pos;
471 menubar_paint_idx (menubar, menubar->subsel, MENU_SELECTED_COLOR);
473 if (event->type & GPM_UP)
474 menubar_execute (menubar, pos);
475 } else
476 if (event->type & GPM_DOWN)
477 menubar_finish (menubar);
479 return MOU_NORMAL;
483 * Properly space menubar items. Should be called when menubar is created
484 * and also when widget width is changed (i.e. upon xterm resize).
486 void
487 menubar_arrange(WMenu* menubar)
489 register int i, start_x = 1;
490 int items = menubar->items;
492 #ifndef RESIZABLE_MENUBAR
493 int gap = 3;
495 for (i = 0; i < items; i++)
497 int len = strlen(menubar->menu[i]->name);
498 menubar->menu[i]->start_x = start_x;
499 start_x += len + gap;
502 #else /* RESIZABLE_MENUBAR */
504 int gap = menubar->widget.cols - 2;
506 /* First, calculate gap between items... */
507 for (i = 0; i < items; i++)
509 /* preserve length here, to be used below */
510 gap -= (menubar->menu[i]->start_x = strlen(menubar->menu[i]->name));
513 gap /= (items - 1);
515 if (gap <= 0)
517 /* We are out of luck - window is too narrow... */
518 gap = 1;
521 /* ...and now fix start positions of menubar items */
522 for (i = 0; i < items; i++)
524 int len = menubar->menu[i]->start_x;
525 menubar->menu[i]->start_x = start_x;
526 start_x += len + gap;
528 #endif /* RESIZABLE_MENUBAR */
531 void
532 destroy_menu (Menu *menu)
534 g_free (menu->name);
535 g_free (menu->help_node);
536 g_free (menu);
539 WMenu *
540 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 menubar_callback, menubar_event);
546 menubar->menu = menu;
547 menubar->active = 0;
548 menubar->dropped = 0;
549 menubar->items = items;
550 menubar->selected = 0;
551 widget_want_cursor (menubar->widget, 0);
552 menubar_arrange (menubar);
554 return menubar;