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. */
21 #include <sys/types.h>
32 #include "key.h" /* For mi_getch() */
35 int menubar_visible
= 1; /* This is the new default */
37 static void menu_scan_hotkey(Menu menu
)
39 char* cp
= strchr (menu
->name
, '&');
41 if (cp
!= NULL
&& cp
[1] != '\0'){
43 menu
->hotkey
= tolower(*cp
);
49 Menu
create_menu (char *name
, menu_entry
*entries
, int count
, char *help_node
)
54 menu
= (Menu
) g_malloc (sizeof (*menu
));
56 menu
->max_entry_len
= 20;
57 menu
->entries
= entries
;
59 if (entries
!= (menu_entry
*) NULL
) {
60 register menu_entry
* mp
;
61 for (mp
= entries
; count
--; mp
++) {
62 if (mp
->text
[0] != '\0') {
64 mp
->text
= _(mp
->text
);
65 #endif /* ENABLE_NLS */
66 cp
= strchr (mp
->text
,'&');
68 if (cp
!= NULL
&& *(cp
+1) != '\0') {
69 mp
->hot_key
= tolower (*(cp
+1));
70 menu
->max_entry_len
= max (strlen (mp
->text
) - 1,
73 menu
->max_entry_len
= max (strlen (mp
->text
),
80 menu
->name
= g_strdup (name
);
81 menu_scan_hotkey(menu
);
83 menu
->help_node
= g_strdup (help_node
);
87 static void menubar_drop_compute (WMenu
*menubar
)
89 menubar
->max_entry_len
= menubar
->menu
[menubar
->selected
]->max_entry_len
;
92 static void menubar_paint_idx (WMenu
*menubar
, int idx
, int color
)
94 const Menu menu
= menubar
->menu
[menubar
->selected
];
95 const int y
= 2 + idx
;
96 int x
= menubar
-> menu
[menubar
->selected
]->start_x
;
98 if (x
+ menubar
->max_entry_len
+ 3 > menubar
->widget
.cols
)
99 x
= menubar
->widget
.cols
- menubar
->max_entry_len
- 3;
101 widget_move (&menubar
->widget
, y
, x
);
103 hline (' ', menubar
->max_entry_len
+2);
104 if (!*menu
->entries
[idx
].text
) {
105 attrset (SELECTED_COLOR
);
106 widget_move (&menubar
->widget
, y
, x
+ 1);
107 hline (slow_terminal
? ' ' : ACS_HLINE
, menubar
->max_entry_len
);
111 addch((unsigned char)menu
->entries
[idx
].first_letter
);
112 for (text
= menu
->entries
[idx
].text
; *text
; text
++)
117 attrset (color
== MENU_SELECTED_COLOR
?
118 MENU_HOTSEL_COLOR
: MENU_HOT_COLOR
);
124 widget_move (&menubar
->widget
, y
, x
+ 1);
127 static inline void menubar_draw_drop (WMenu
*menubar
)
129 const int count
= (menubar
->menu
[menubar
->selected
])->count
;
131 int sel
= menubar
->subsel
;
132 int column
= menubar
-> menu
[menubar
->selected
]->start_x
- 1;
134 if (column
+ menubar
->max_entry_len
+ 4 > menubar
->widget
.cols
)
135 column
= menubar
->widget
.cols
- menubar
->max_entry_len
- 4;
137 attrset (SELECTED_COLOR
);
138 draw_box (menubar
->widget
.parent
,
139 menubar
->widget
.y
+1, menubar
->widget
.x
+ column
,
140 count
+2, menubar
->max_entry_len
+ 4);
143 for (i
= 0; i
< count
; i
++){
146 menubar_paint_idx (menubar
, i
, MENU_ENTRY_COLOR
);
148 menubar_paint_idx (menubar
, sel
, MENU_SELECTED_COLOR
);
151 static void menubar_draw (WMenu
*menubar
)
153 const int items
= menubar
->items
;
156 /* First draw the complete menubar */
157 attrset (SELECTED_COLOR
);
158 widget_move (&menubar
->widget
, 0, 0);
160 /* ncurses bug: it should work with hline but it does not */
161 for (i
= menubar
->widget
.cols
; i
; i
--)
164 attrset (SELECTED_COLOR
);
165 /* Now each one of the entries */
166 for (i
= 0; i
< items
; i
++){
168 attrset(i
== menubar
->selected
?MENU_SELECTED_COLOR
:SELECTED_COLOR
);
169 widget_move (&menubar
->widget
, 0, menubar
->menu
[i
]->start_x
);
170 printw ("%s", menubar
->menu
[i
]->name
);
173 if (menubar
->dropped
)
174 menubar_draw_drop (menubar
);
176 widget_move (&menubar
->widget
, 0,
177 menubar
-> menu
[menubar
->selected
]->start_x
);
180 static inline void menubar_remove (WMenu
*menubar
)
183 if (menubar
->dropped
){
184 menubar
->dropped
= 0;
186 menubar
->dropped
= 1;
190 static void menubar_left (WMenu
*menu
)
192 menubar_remove (menu
);
193 menu
->selected
= (menu
->selected
- 1) % menu
->items
;
194 if (menu
->selected
< 0)
195 menu
->selected
= menu
->items
-1;
196 menubar_drop_compute (menu
);
200 static void menubar_right (WMenu
*menu
)
202 menubar_remove (menu
);
203 menu
->selected
= (menu
->selected
+ 1) % menu
->items
;
204 menubar_drop_compute (menu
);
208 static void menubar_finish (WMenu
*menubar
)
210 menubar
->dropped
= 0;
212 menubar
->widget
.lines
= 1;
213 widget_want_hotkey (menubar
->widget
, 0);
214 dlg_select_nth_widget (menubar
->widget
.parent
,
215 menubar
->previous_selection
);
219 static void menubar_drop (WMenu
*menubar
, int selected
)
221 menubar
->dropped
= 1;
222 menubar
->selected
= selected
;
224 menubar_drop_compute (menubar
);
225 menubar_draw (menubar
);
228 static void menubar_execute (WMenu
*menubar
, int entry
)
230 const Menu menu
= menubar
->menu
[menubar
->selected
];
231 const callfn call_back
= menu
->entries
[entry
].call_back
;
233 is_right
= menubar
->selected
!= 0;
235 /* This used to be the other way round, i.e. first callback and
236 then menubar_finish. The new order (hack?) is needed to make
237 change_panel () work which is used in quick_view_cmd () -- Norbert
239 menubar_finish (menubar
);
244 static void menubar_move (WMenu
*menubar
, int step
)
246 const Menu menu
= menubar
->menu
[menubar
->selected
];
248 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_ENTRY_COLOR
);
250 menubar
->subsel
+= step
;
251 if (menubar
->subsel
< 0)
252 menubar
->subsel
= menu
->count
- 1;
254 menubar
->subsel
%= menu
->count
;
255 } while (!menu
->entries
[menubar
->subsel
].call_back
);
256 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_SELECTED_COLOR
);
259 static int menubar_handle_key (WMenu
*menubar
, int key
)
264 if (key
< 256 && isalpha (key
)) /* Linux libc.so.5.x.x bug fix */
267 if (is_abort_char (key
)){
268 menubar_finish (menubar
);
272 if (key
== KEY_F(1)) {
273 if (menubar
->dropped
) {
274 interactive_display (NULL
,
275 (menubar
->menu
[menubar
->selected
])->help_node
);
277 interactive_display (NULL
, "[Menu Bar]");
279 menubar_draw (menubar
);
283 if (key
== KEY_LEFT
|| key
== XCTRL('b')){
284 menubar_left (menubar
);
286 } else if (key
== KEY_RIGHT
|| key
== XCTRL ('f')){
287 menubar_right (menubar
);
291 /* .ado: NT Alpha can not allow CTRL in Menubar */
292 #if defined(NATIVE_WIN32)
297 if (!menubar
->dropped
){
298 const int items
= menubar
->items
;
299 for (i
= 0; i
< items
; i
++){
300 const Menu menu
= menubar
->menu
[i
];
302 if (menu
->hotkey
== key
){
303 menubar_drop (menubar
, i
);
307 if (key
== KEY_ENTER
|| key
== XCTRL ('n') || key
== KEY_DOWN
309 menubar_drop (menubar
, menubar
->selected
);
314 const int selected
= menubar
->selected
;
315 const Menu menu
= menubar
->menu
[selected
];
316 const int items
= menu
->count
;
318 for (i
= 0; i
< items
; i
++){
319 if (!menu
->entries
[i
].call_back
)
322 if (key
!= menu
->entries
[i
].hot_key
)
325 menubar_execute (menubar
, i
);
329 if (key
== KEY_ENTER
|| key
== '\n'){
330 menubar_execute (menubar
, menubar
->subsel
);
335 if (key
== KEY_DOWN
|| key
== XCTRL ('n'))
336 menubar_move (menubar
, 1);
338 if (key
== KEY_UP
|| key
== XCTRL ('p'))
339 menubar_move (menubar
, -1);
344 static int menubar_callback (Dlg_head
*h
, WMenu
*menubar
, int msg
, int par
)
347 /* We do not want the focus unless we have been activated */
349 if (menubar
->active
){
350 widget_want_cursor (menubar
->widget
, 1);
352 /* Trick to get all the mouse events */
353 menubar
->widget
.lines
= LINES
;
355 /* Trick to get all of the hotkeys */
356 widget_want_hotkey (menubar
->widget
, 1);
358 menubar_drop_compute (menubar
);
359 menubar_draw (menubar
);
364 /* We don't want the buttonbar to activate while using the menubar */
367 if (menubar
->active
){
368 menubar_handle_key (menubar
, par
);
374 /* Put the cursor in a suitable place */
381 widget_want_cursor (menubar
->widget
, 0);
387 menubar_draw (menubar
);
389 return default_proc (h
, msg
, par
);
393 menubar_event (Gpm_Event
*event
, WMenu
*menubar
)
397 int left_x
, right_x
, bottom_y
;
399 if (!(event
->type
& (GPM_UP
|GPM_DOWN
|GPM_DRAG
)))
402 if (!menubar
->dropped
){
403 menubar
->previous_selection
= dlg_item_number(menubar
->widget
.parent
);
405 menubar
->dropped
= 1;
410 /* Mouse operations on the menubar */
411 if (event
->y
== 1 || !was_active
){
412 if (event
->type
& GPM_UP
)
416 while (new_selection
< menubar
->items
417 && event
->x
> menubar
->menu
[new_selection
]->start_x
421 if (new_selection
) /* Don't set the invalid value -1 */
425 menubar
->selected
= new_selection
;
426 dlg_select_widget (menubar
->widget
.parent
, menubar
);
427 menubar_drop_compute (menubar
);
428 menubar_draw (menubar
);
432 menubar_remove (menubar
);
434 menubar
->selected
= new_selection
;
436 menubar_drop_compute (menubar
);
437 menubar_draw (menubar
);
441 if (!menubar
->dropped
)
444 /* Ignore the events on anything below the third line */
448 /* Else, the mouse operation is on the menus or it is not */
449 left_x
= menubar
->menu
[menubar
->selected
]->start_x
;
450 right_x
= left_x
+ menubar
->max_entry_len
+ 4;
451 if (right_x
> menubar
->widget
.cols
)
453 left_x
= menubar
->widget
.cols
- menubar
->max_entry_len
- 3;
454 right_x
= menubar
->widget
.cols
- 1;
457 bottom_y
= (menubar
->menu
[menubar
->selected
])->count
+ 3;
459 if ((event
->x
> left_x
) && (event
->x
< right_x
) && (event
->y
< bottom_y
)){
460 int pos
= event
->y
- 3;
462 if (!menubar
->menu
[menubar
->selected
]->entries
[pos
].call_back
)
465 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_ENTRY_COLOR
);
466 menubar
->subsel
= pos
;
467 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_SELECTED_COLOR
);
469 if (event
->type
& GPM_UP
)
470 menubar_execute (menubar
, pos
);
472 if (event
->type
& GPM_DOWN
)
473 menubar_finish (menubar
);
478 static void menubar_destroy (WMenu
*menubar
)
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).
487 menubar_arrange(WMenu
* menubar
)
489 register int i
, start_x
= 1;
490 int items
= menubar
->items
;
492 #ifndef RESIZABLE_MENUBAR
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
));
517 /* We are out of luck - window is too narrow... */
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 */
532 destroy_menu (Menu menu
)
535 g_free (menu
->help_node
);
539 WMenu
*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 (callback_fn
) menubar_callback
,
545 (destroy_fn
) menubar_destroy
,
546 (mouse_h
) menubar_event
, NULL
);
547 menubar
->menu
= menu
;
549 menubar
->dropped
= 0;
550 menubar
->items
= items
;
551 menubar
->selected
= 0;
552 widget_want_cursor (menubar
->widget
, 0);
553 menubar_arrange(menubar
);