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., 675 Mass Ave, Cambridge, MA 02139, USA. */
22 #include <sys/types.h>
34 #include "key.h" /* For mi_getch() */
39 int menubar_visible
= 1; /* This is the new default */
41 Menu
create_menu (char *name
, menu_entry
*entries
, int count
)
45 menu
= (Menu
) xmalloc (sizeof (*menu
), "create_menu");
47 menu
->max_entry_len
= 0;
48 menu
->entries
= entries
;
51 if (entries
!= (menu_entry
*) 0)
53 register menu_entry
* mp
;
54 for (mp
= entries
; count
--; mp
++)
56 if (mp
->text
[0] == '\0')
59 mp
->text
= _(mp
->text
);
62 #endif /* ENABLE_NLS */
69 static void menubar_drop_compute (WMenu
*menubar
)
71 const Menu menu
= menubar
->menu
[menubar
->selected
];
72 int max_entry_len
= 0;
75 for (i
= 0; i
< menu
->count
; i
++)
76 max_entry_len
= max (max_entry_len
, strlen (menu
->entries
[i
].text
));
77 menubar
->max_entry_len
= max_entry_len
= max (max_entry_len
, 20);
80 static void menubar_paint_idx (WMenu
*menubar
, int idx
, int color
)
82 const Menu menu
= menubar
->menu
[menubar
->selected
];
83 const int y
= 2 + idx
;
84 int x
= menubar
-> menu
[menubar
->selected
]->start_x
;
86 if (x
+ menubar
->max_entry_len
+ 3 > menubar
->widget
.cols
)
87 x
= menubar
->widget
.cols
- menubar
->max_entry_len
- 3;
89 widget_move (&menubar
->widget
, y
, x
);
91 hline (' ', menubar
->max_entry_len
+2);
92 if (!*menu
->entries
[idx
].text
){
93 attrset (SELECTED_COLOR
);
94 widget_move (&menubar
->widget
, y
, x
+ 1);
95 hline (slow_terminal
? ' ' : ACS_HLINE
, menubar
->max_entry_len
);
97 char *text
= menu
->entries
[idx
].text
;
99 addch(menu
->entries
[idx
].first_letter
);
100 for (text
= menu
->entries
[idx
].text
; *text
; text
++)
105 menu
->entries
[idx
].hot_key
= tolower(*text
);
106 attrset (color
== MENU_SELECTED_COLOR
?
107 MENU_HOTSEL_COLOR
: MENU_HOT_COLOR
);
115 widget_move (&menubar
->widget
, y
, x
+ 1);
118 static INLINE
void menubar_draw_drop (WMenu
*menubar
)
120 const int count
= (menubar
->menu
[menubar
->selected
])->count
;
122 int sel
= menubar
->subsel
;
123 int column
= menubar
-> menu
[menubar
->selected
]->start_x
- 1;
125 if (column
+ menubar
->max_entry_len
+ 4 > menubar
->widget
.cols
)
126 column
= menubar
->widget
.cols
- menubar
->max_entry_len
- 4;
128 attrset (SELECTED_COLOR
);
129 draw_box (menubar
->widget
.parent
,
130 menubar
->widget
.y
+1, menubar
->widget
.x
+ column
,
131 count
+2, menubar
->max_entry_len
+ 4);
134 for (i
= 0; i
< count
; i
++){
137 menubar_paint_idx (menubar
, i
, MENU_ENTRY_COLOR
);
139 menubar_paint_idx (menubar
, sel
, MENU_SELECTED_COLOR
);
142 static void menubar_draw (WMenu
*menubar
)
144 const int items
= menubar
->items
;
147 /* First draw the complete menubar */
148 attrset (SELECTED_COLOR
);
149 widget_move (&menubar
->widget
, 0, 0);
151 /* ncurses bug: it should work with hline but it does not */
152 for (i
= menubar
->widget
.cols
; i
; i
--)
155 attrset (SELECTED_COLOR
);
156 /* Now each one of the entries */
157 for (i
= 0; i
< items
; i
++){
159 attrset(i
== menubar
->selected
?MENU_SELECTED_COLOR
:SELECTED_COLOR
);
160 widget_move (&menubar
->widget
, 0, menubar
->menu
[i
]->start_x
);
161 printw ("%s", menubar
->menu
[i
]->name
);
164 if (menubar
->dropped
)
165 menubar_draw_drop (menubar
);
167 widget_move (&menubar
->widget
, 0,
168 menubar
-> menu
[menubar
->selected
]->start_x
);
171 static INLINE
void menubar_remove (WMenu
*menubar
)
174 if (menubar
->dropped
){
175 menubar
->dropped
= 0;
177 menubar
->dropped
= 1;
181 static void menubar_left (WMenu
*menu
)
183 menubar_remove (menu
);
184 menu
->selected
= (menu
->selected
- 1) % menu
->items
;
185 if (menu
->selected
< 0)
186 menu
->selected
= menu
->items
-1;
187 menubar_drop_compute (menu
);
191 static void menubar_right (WMenu
*menu
)
193 menubar_remove (menu
);
194 menu
->selected
= (menu
->selected
+ 1) % menu
->items
;
195 menubar_drop_compute (menu
);
199 static void menubar_finish (WMenu
*menubar
)
201 menubar
->dropped
= 0;
203 menubar
->widget
.lines
= 1;
204 widget_want_hotkey (menubar
->widget
, 0);
205 dlg_select_nth_widget (menubar
->widget
.parent
,
206 menubar
->previous_selection
);
210 static void menubar_drop (WMenu
*menubar
, int selected
)
212 menubar
->dropped
= 1;
213 menubar
->selected
= selected
;
215 menubar_drop_compute (menubar
);
216 menubar_draw (menubar
);
219 static void menubar_execute (WMenu
*menubar
, int entry
)
221 const Menu menu
= menubar
->menu
[menubar
->selected
];
223 is_right
= menubar
->selected
!= 0;
224 (*menu
->entries
[entry
].call_back
)(0);
225 menubar_finish (menubar
);
228 static void menubar_move (WMenu
*menubar
, int step
)
230 const Menu menu
= menubar
->menu
[menubar
->selected
];
232 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_ENTRY_COLOR
);
234 menubar
->subsel
+= step
;
235 if (menubar
->subsel
< 0)
236 menubar
->subsel
= menu
->count
- 1;
238 menubar
->subsel
%= menu
->count
;
239 } while (!menu
->entries
[menubar
->subsel
].call_back
);
240 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_SELECTED_COLOR
);
243 static int menubar_handle_key (WMenu
*menubar
, int key
)
248 if (key
< 256 && isalpha (key
)) /* Linux libc.so.5.x.x bug fix */
251 if (is_abort_char (key
)){
252 menubar_finish (menubar
);
256 if (key
== KEY_LEFT
|| key
== XCTRL('b')){
257 menubar_left (menubar
);
259 } else if (key
== KEY_RIGHT
|| key
== XCTRL ('f')){
260 menubar_right (menubar
);
264 /* .ado: NT Alpha can not allow CTRL in Menubar */
270 if (!menubar
->dropped
){
271 const int items
= menubar
->items
;
272 for (i
= 0; i
< items
; i
++){
273 const Menu menu
= menubar
->menu
[i
];
275 /* Hack, we should check for the upper case letter */
276 if (tolower (menu
->name
[1]) == key
){
277 menubar_drop (menubar
, i
);
281 if (key
== KEY_ENTER
|| key
== XCTRL ('n') || key
== KEY_DOWN
283 menubar_drop (menubar
, menubar
->selected
);
288 const int selected
= menubar
->selected
;
289 const Menu menu
= menubar
->menu
[selected
];
290 const int items
= menu
->count
;
292 for (i
= 0; i
< items
; i
++){
293 if (!menu
->entries
[i
].call_back
)
296 if (key
!= menu
->entries
[i
].hot_key
)
299 menubar_execute (menubar
, i
);
303 if (key
== KEY_ENTER
|| key
== '\n'){
304 menubar_execute (menubar
, menubar
->subsel
);
309 if (key
== KEY_DOWN
|| key
== XCTRL ('n'))
310 menubar_move (menubar
, 1);
312 if (key
== KEY_UP
|| key
== XCTRL ('p'))
313 menubar_move (menubar
, -1);
318 static int menubar_callback (Dlg_head
*h
, WMenu
*menubar
, int msg
, int par
)
321 /* We do not want the focus unless we have been activated */
323 if (menubar
->active
){
324 widget_want_cursor (menubar
->widget
, 1);
326 /* Trick to get all the mouse events */
327 menubar
->widget
.lines
= LINES
;
329 /* Trick to get all of the hotkeys */
330 widget_want_hotkey (menubar
->widget
, 1);
332 menubar_drop_compute (menubar
);
333 menubar_draw (menubar
);
338 /* We don't want the buttonbar to activate while using the menubar */
341 if (menubar
->active
){
342 menubar_handle_key (menubar
, par
);
348 /* Put the cursor in a suitable place */
355 widget_want_cursor (menubar
->widget
, 0);
361 menubar_draw (menubar
);
363 return default_proc (h
, msg
, par
);
367 menubar_event (Gpm_Event
*event
, WMenu
*menubar
)
371 int left_x
, right_x
, bottom_y
;
373 if (!(event
->type
& (GPM_UP
|GPM_DOWN
|GPM_DRAG
)))
376 if (!menubar
->dropped
){
377 menubar
->previous_selection
= dlg_item_number(menubar
->widget
.parent
);
379 menubar
->dropped
= 1;
384 /* Mouse operations on the menubar */
385 if (event
->y
== 1 || !was_active
){
386 if (event
->type
& GPM_UP
)
390 while (new_selection
< menubar
->items
391 && event
->x
> menubar
->menu
[new_selection
]->start_x
395 if (new_selection
) /* Don't set the invalid value -1 */
399 menubar
->selected
= new_selection
;
400 dlg_select_widget (menubar
->widget
.parent
, menubar
);
401 menubar_drop_compute (menubar
);
402 menubar_draw (menubar
);
406 menubar_remove (menubar
);
408 menubar
->selected
= new_selection
;
410 menubar_drop_compute (menubar
);
411 menubar_draw (menubar
);
415 if (!menubar
->dropped
)
418 /* Ignore the events on anything below the third line */
422 /* Else, the mouse operation is on the menus or it is not */
423 left_x
= menubar
->menu
[menubar
->selected
]->start_x
;
424 right_x
= left_x
+ menubar
->max_entry_len
+ 4;
425 if (right_x
> menubar
->widget
.cols
)
427 left_x
= menubar
->widget
.cols
- menubar
->max_entry_len
- 3;
428 right_x
= menubar
->widget
.cols
- 1;
431 bottom_y
= (menubar
->menu
[menubar
->selected
])->count
+ 3;
433 if ((event
->x
> left_x
) && (event
->x
< right_x
) && (event
->y
< bottom_y
)){
434 int pos
= event
->y
- 3;
436 if (!menubar
->menu
[menubar
->selected
]->entries
[pos
].call_back
)
439 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_ENTRY_COLOR
);
440 menubar
->subsel
= pos
;
441 menubar_paint_idx (menubar
, menubar
->subsel
, MENU_SELECTED_COLOR
);
443 if (event
->type
& GPM_UP
)
444 menubar_execute (menubar
, pos
);
446 if (event
->type
& GPM_DOWN
)
447 menubar_finish (menubar
);
452 static void menubar_destroy (WMenu
*menubar
)
457 * Properly space menubar items. Should be called when menubar is created
458 * and also when widget width is changed (i.e. upon xterm resize).
461 menubar_arrange(WMenu
* menubar
)
463 register int i
, start_x
= 1;
464 int items
= menubar
->items
;
466 #ifndef RESIZABLE_MENUBAR
469 for (i
= 0; i
< items
; i
++)
471 int len
= strlen(menubar
->menu
[i
]->name
);
472 menubar
->menu
[i
]->start_x
= start_x
;
473 start_x
+= len
+ gap
;
476 #else /* RESIZABLE_MENUBAR */
478 int gap
= menubar
->widget
.cols
- 2;
480 /* First, calculate gap between items... */
481 for (i
= 0; i
< items
; i
++)
483 /* preserve length here, to be used below */
484 gap
-= (menubar
->menu
[i
]->start_x
= strlen(menubar
->menu
[i
]->name
));
491 /* We are out of luck - window is too narrow... */
495 /* ...and now fix start positions of menubar items */
496 for (i
= 0; i
< items
; i
++)
498 int len
= menubar
->menu
[i
]->start_x
;
499 menubar
->menu
[i
]->start_x
= start_x
;
500 start_x
+= len
+ gap
;
502 #endif /* RESIZABLE_MENUBAR */
506 destroy_menu (Menu menu
)
511 WMenu
*menubar_new (int y
, int x
, int cols
, Menu menu
[], int items
)
513 WMenu
*menubar
= (WMenu
*) xmalloc (sizeof (WMenu
), "menubar_new");
515 memset(menubar
, 0, sizeof(*menubar
)); /* FIXME: subsel used w/o being set */
516 init_widget (&menubar
->widget
, y
, x
, 1, cols
,
517 (callback_fn
) menubar_callback
,
518 (destroy_fn
) menubar_destroy
,
519 (mouse_h
) menubar_event
, NULL
);
520 menubar
->menu
= menu
;
522 menubar
->dropped
= 0;
523 menubar
->items
= items
;
524 menubar
->selected
= 0;
525 widget_want_cursor (menubar
->widget
, 0);
526 menubar_arrange(menubar
);