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. */
24 #include <sys/types.h>
26 #include <mhl/memory.h>
27 #include <mhl/string.h>
38 #include "key.h" /* For mi_getch() */
40 int menubar_visible
= 1; /* This is the new default */
43 menu_scan_hotkey (Menu
*menu
)
45 char *cp
= strchr (menu
->name
, '&');
47 if (cp
!= NULL
&& cp
[1] != '\0') {
48 g_strlcpy (cp
, cp
+ 1, strlen (cp
));
49 menu
->hotkey
= tolower ((unsigned char) *cp
);
55 create_menu (const char *name
, menu_entry
*entries
, int count
, const char *help_node
)
60 menu
= (Menu
*) g_malloc (sizeof (*menu
));
62 menu
->max_entry_len
= 20;
63 menu
->entries
= entries
;
65 if (entries
!= (menu_entry
*) NULL
) {
66 register menu_entry
* mp
;
67 for (mp
= entries
; count
--; mp
++) {
68 if (mp
->text
[0] != '\0') {
70 mp
->text
= _(mp
->text
);
71 #endif /* ENABLE_NLS */
72 cp
= strchr (mp
->text
,'&');
74 if (cp
!= NULL
&& *(cp
+1) != '\0') {
75 mp
->hot_key
= tolower ((unsigned char) *(cp
+1));
76 menu
->max_entry_len
= max ((int) (strlen (mp
->text
) - 1),
79 menu
->max_entry_len
= max ((int) strlen (mp
->text
),
86 menu
->name
= g_strdup (name
);
87 menu_scan_hotkey(menu
);
89 menu
->help_node
= g_strdup (help_node
);
93 static void menubar_drop_compute (WMenu
*menubar
)
95 menubar
->max_entry_len
= menubar
->menu
[menubar
->selected
]->max_entry_len
;
98 static void menubar_paint_idx (WMenu
*menubar
, int idx
, int color
)
100 const Menu
*menu
= menubar
->menu
[menubar
->selected
];
101 const int y
= 2 + idx
;
102 int x
= menubar
-> menu
[menubar
->selected
]->start_x
;
104 if (x
+ menubar
->max_entry_len
+ 3 > menubar
->widget
.cols
)
105 x
= menubar
->widget
.cols
- menubar
->max_entry_len
- 3;
107 widget_move (&menubar
->widget
, y
, x
);
109 hline (' ', menubar
->max_entry_len
+2);
110 if (!*menu
->entries
[idx
].text
) {
111 attrset (SELECTED_COLOR
);
112 widget_move (&menubar
->widget
, y
, x
+ 1);
113 hline (slow_terminal
? ' ' : ACS_HLINE
, menubar
->max_entry_len
);
117 addch((unsigned char)menu
->entries
[idx
].first_letter
);
118 for (text
= menu
->entries
[idx
].text
; *text
; text
++)
123 attrset (color
== MENU_SELECTED_COLOR
?
124 MENU_HOTSEL_COLOR
: MENU_HOT_COLOR
);
130 widget_move (&menubar
->widget
, y
, x
+ 1);
133 static inline void menubar_draw_drop (WMenu
*menubar
)
135 const int count
= (menubar
->menu
[menubar
->selected
])->count
;
137 int sel
= menubar
->subsel
;
138 int column
= menubar
-> menu
[menubar
->selected
]->start_x
- 1;
140 if (column
+ menubar
->max_entry_len
+ 4 > menubar
->widget
.cols
)
141 column
= menubar
->widget
.cols
- menubar
->max_entry_len
- 4;
143 attrset (SELECTED_COLOR
);
144 draw_box (menubar
->widget
.parent
,
145 menubar
->widget
.y
+1, menubar
->widget
.x
+ column
,
146 count
+2, menubar
->max_entry_len
+ 4);
149 for (i
= 0; i
< count
; i
++){
152 menubar_paint_idx (menubar
, i
, MENU_ENTRY_COLOR
);
154 menubar_paint_idx (menubar
, sel
, MENU_SELECTED_COLOR
);
157 static void menubar_draw (WMenu
*menubar
)
159 const int items
= menubar
->items
;
162 /* First draw the complete menubar */
163 attrset (SELECTED_COLOR
);
164 widget_move (&menubar
->widget
, 0, 0);
166 hline (' ', menubar
->widget
.cols
);
168 attrset (SELECTED_COLOR
);
169 /* Now each one of the entries */
170 for (i
= 0; i
< items
; i
++){
172 attrset(i
== menubar
->selected
?MENU_SELECTED_COLOR
:SELECTED_COLOR
);
173 widget_move (&menubar
->widget
, 0, menubar
->menu
[i
]->start_x
);
174 tty_printf ("%s", menubar
->menu
[i
]->name
);
177 if (menubar
->dropped
)
178 menubar_draw_drop (menubar
);
180 widget_move (&menubar
->widget
, 0,
181 menubar
-> menu
[menubar
->selected
]->start_x
);
184 static inline void menubar_remove (WMenu
*menubar
)
187 if (menubar
->dropped
){
188 menubar
->dropped
= 0;
190 menubar
->dropped
= 1;
194 static void menubar_left (WMenu
*menu
)
196 menubar_remove (menu
);
197 menu
->selected
= (menu
->selected
- 1) % menu
->items
;
198 if (menu
->selected
< 0)
199 menu
->selected
= menu
->items
-1;
200 menubar_drop_compute (menu
);
204 static void menubar_right (WMenu
*menu
)
206 menubar_remove (menu
);
207 menu
->selected
= (menu
->selected
+ 1) % menu
->items
;
208 menubar_drop_compute (menu
);
213 menubar_finish (WMenu
*menubar
)
215 menubar
->dropped
= 0;
217 menubar
->widget
.lines
= 1;
218 widget_want_hotkey (menubar
->widget
, 0);
220 dlg_select_by_id (menubar
->widget
.parent
, menubar
->previous_widget
);
224 static void menubar_drop (WMenu
*menubar
, int selected
)
226 menubar
->dropped
= 1;
227 menubar
->selected
= selected
;
229 menubar_drop_compute (menubar
);
230 menubar_draw (menubar
);
233 static void menubar_execute (WMenu
*menubar
, int entry
)
235 const Menu
*menu
= menubar
->menu
[menubar
->selected
];
236 const callfn call_back
= menu
->entries
[entry
].call_back
;
238 is_right
= menubar
->selected
!= 0;
240 /* This used to be the other way round, i.e. first callback and
241 then menubar_finish. The new order (hack?) is needed to make
242 change_panel () work which is used in quick_view_cmd () -- Norbert
244 menubar_finish (menubar
);
249 static void menubar_move (WMenu
*menubar
, int step
)
251 const Menu
*menu
= menubar
->menu
[menubar
->selected
];
253 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_ENTRY_COLOR
);
255 menubar
->subsel
+= step
;
256 if (menubar
->subsel
< 0)
257 menubar
->subsel
= menu
->count
- 1;
259 menubar
->subsel
%= menu
->count
;
260 } while (!menu
->entries
[menubar
->subsel
].call_back
);
261 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_SELECTED_COLOR
);
264 static int menubar_handle_key (WMenu
*menubar
, int key
)
269 if (key
< 256 && isalpha (key
)) /* Linux libc.so.5.x.x bug fix */
272 if (is_abort_char (key
)){
273 menubar_finish (menubar
);
277 if (key
== KEY_F(1)) {
278 if (menubar
->dropped
) {
279 interactive_display (NULL
,
280 (menubar
->menu
[menubar
->selected
])->help_node
);
282 interactive_display (NULL
, "[Menu Bar]");
284 menubar_draw (menubar
);
288 if (key
== KEY_LEFT
|| key
== XCTRL('b')){
289 menubar_left (menubar
);
291 } else if (key
== KEY_RIGHT
|| key
== XCTRL ('f')){
292 menubar_right (menubar
);
296 if (!menubar
->dropped
){
297 const int items
= menubar
->items
;
298 for (i
= 0; i
< items
; i
++){
299 const Menu
*menu
= menubar
->menu
[i
];
301 if (menu
->hotkey
== key
){
302 menubar_drop (menubar
, i
);
306 if (key
== KEY_ENTER
|| key
== XCTRL ('n') || key
== KEY_DOWN
308 menubar_drop (menubar
, menubar
->selected
);
313 const int selected
= menubar
->selected
;
314 const Menu
*menu
= menubar
->menu
[selected
];
315 const int items
= menu
->count
;
317 for (i
= 0; i
< items
; i
++){
318 if (!menu
->entries
[i
].call_back
)
321 if (key
!= menu
->entries
[i
].hot_key
)
324 menubar_execute (menubar
, i
);
328 if (key
== KEY_ENTER
|| key
== '\n'){
329 menubar_execute (menubar
, menubar
->subsel
);
334 if (key
== KEY_DOWN
|| key
== XCTRL ('n'))
335 menubar_move (menubar
, 1);
337 if (key
== KEY_UP
|| key
== XCTRL ('p'))
338 menubar_move (menubar
, -1);
344 menubar_callback (Widget
*w
, widget_msg_t msg
, int parm
)
346 WMenu
*menubar
= (WMenu
*) w
;
349 /* We do not want the focus unless we have been activated */
351 if (!menubar
->active
)
352 return MSG_NOT_HANDLED
;
354 widget_want_cursor (menubar
->widget
, 1);
356 /* Trick to get all the mouse events */
357 menubar
->widget
.lines
= LINES
;
359 /* Trick to get all of the hotkeys */
360 widget_want_hotkey (menubar
->widget
, 1);
362 menubar_drop_compute (menubar
);
363 menubar_draw (menubar
);
366 /* We don't want the buttonbar to activate while using the menubar */
369 if (menubar
->active
) {
370 menubar_handle_key (menubar
, parm
);
373 return MSG_NOT_HANDLED
;
376 /* Put the cursor in a suitable place */
377 return MSG_NOT_HANDLED
;
381 return MSG_NOT_HANDLED
;
383 widget_want_cursor (menubar
->widget
, 0);
389 menubar_draw (menubar
);
393 return default_proc (msg
, parm
);
398 menubar_event (Gpm_Event
*event
, void *data
)
400 WMenu
*menubar
= data
;
403 int left_x
, right_x
, bottom_y
;
405 if (!(event
->type
& (GPM_UP
|GPM_DOWN
|GPM_DRAG
)))
408 if (!menubar
->dropped
){
409 menubar
->previous_widget
= menubar
->widget
.parent
->current
->dlg_id
;
411 menubar
->dropped
= 1;
416 /* Mouse operations on the menubar */
417 if (event
->y
== 1 || !was_active
){
418 if (event
->type
& GPM_UP
)
422 while (new_selection
< menubar
->items
423 && event
->x
> menubar
->menu
[new_selection
]->start_x
427 if (new_selection
) /* Don't set the invalid value -1 */
431 menubar
->selected
= new_selection
;
432 dlg_select_widget (menubar
);
433 menubar_drop_compute (menubar
);
434 menubar_draw (menubar
);
438 menubar_remove (menubar
);
440 menubar
->selected
= new_selection
;
442 menubar_drop_compute (menubar
);
443 menubar_draw (menubar
);
447 if (!menubar
->dropped
)
450 /* Ignore the events on anything below the third line */
454 /* Else, the mouse operation is on the menus or it is not */
455 left_x
= menubar
->menu
[menubar
->selected
]->start_x
;
456 right_x
= left_x
+ menubar
->max_entry_len
+ 4;
457 if (right_x
> menubar
->widget
.cols
)
459 left_x
= menubar
->widget
.cols
- menubar
->max_entry_len
- 3;
460 right_x
= menubar
->widget
.cols
- 1;
463 bottom_y
= (menubar
->menu
[menubar
->selected
])->count
+ 3;
465 if ((event
->x
> left_x
) && (event
->x
< right_x
) && (event
->y
< bottom_y
)){
466 int pos
= event
->y
- 3;
468 if (!menubar
->menu
[menubar
->selected
]->entries
[pos
].call_back
)
471 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_ENTRY_COLOR
);
472 menubar
->subsel
= pos
;
473 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_SELECTED_COLOR
);
475 if (event
->type
& GPM_UP
)
476 menubar_execute (menubar
, pos
);
478 if (event
->type
& GPM_DOWN
)
479 menubar_finish (menubar
);
485 * Properly space menubar items. Should be called when menubar is created
486 * and also when widget width is changed (i.e. upon xterm resize).
489 menubar_arrange(WMenu
* menubar
)
491 register int i
, start_x
= 1;
492 int items
= menubar
->items
;
494 #ifndef RESIZABLE_MENUBAR
497 for (i
= 0; i
< items
; i
++)
499 int len
= strlen(menubar
->menu
[i
]->name
);
500 menubar
->menu
[i
]->start_x
= start_x
;
501 start_x
+= len
+ gap
;
504 #else /* RESIZABLE_MENUBAR */
506 int gap
= menubar
->widget
.cols
- 2;
508 /* First, calculate gap between items... */
509 for (i
= 0; i
< items
; i
++)
511 /* preserve length here, to be used below */
512 gap
-= (menubar
->menu
[i
]->start_x
= strlen(menubar
->menu
[i
]->name
));
519 /* We are out of luck - window is too narrow... */
523 /* ...and now fix start positions of menubar items */
524 for (i
= 0; i
< items
; i
++)
526 int len
= menubar
->menu
[i
]->start_x
;
527 menubar
->menu
[i
]->start_x
= start_x
;
528 start_x
+= len
+ gap
;
530 #endif /* RESIZABLE_MENUBAR */
534 destroy_menu (Menu
*menu
)
537 g_free (menu
->help_node
);
542 menubar_new (int y
, int x
, int cols
, Menu
*menu
[], int items
)
544 WMenu
*menubar
= g_new0 (WMenu
, 1); /* FIXME: subsel used w/o being set */
546 init_widget (&menubar
->widget
, y
, x
, 1, cols
,
547 menubar_callback
, menubar_event
);
548 menubar
->menu
= menu
;
550 menubar
->dropped
= 0;
551 menubar
->items
= items
;
552 menubar
->selected
= 0;
553 widget_want_cursor (menubar
->widget
, 0);
554 menubar_arrange (menubar
);