Commit b5e9cbb6 authored by Eli Zaretskii's avatar Eli Zaretskii

Initial incomplete version of tty menus. tty_menu_activate not done yet.

parent f51b6486
......@@ -3115,6 +3115,7 @@ extern int clear_mouse_face (Mouse_HLInfo *);
extern int cursor_in_mouse_face_p (struct window *w);
extern void tty_draw_row_with_mouse_face (struct window *, struct glyph_row *,
int, int, enum draw_glyphs_face);
extern void display_tty_menu_item (const char *, int, int, int, int);
/* Flags passed to try_window. */
#define TRY_WINDOW_CHECK_MARGINS (1 << 0)
......@@ -3278,6 +3279,8 @@ extern void hide_hourglass (void);
int popup_activated (void);
/* Defined in dispnew.c */
extern Lisp_Object buffer_posn_from_coords (struct window *,
int *, int *,
struct display_pos *,
......@@ -3292,35 +3295,35 @@ extern Lisp_Object marginal_area_string (struct window *, enum window_part,
Lisp_Object *,
int *, int *, int *, int *);
extern void redraw_frame (struct frame *);
extern void cancel_line (int, struct frame *);
extern void init_desired_glyphs (struct frame *);
extern int update_frame (struct frame *, int, int);
extern void update_frame_with_menu (struct frame *);
extern void bitch_at_user (void);
void adjust_glyphs (struct frame *);
void free_glyphs (struct frame *);
void free_window_matrices (struct window *);
void check_glyph_memory (void);
void mirrored_line_dance (struct glyph_matrix *, int, int, int *, char *);
void clear_glyph_matrix (struct glyph_matrix *);
void clear_current_matrices (struct frame *f);
void clear_desired_matrices (struct frame *);
void shift_glyph_matrix (struct window *, struct glyph_matrix *,
int, int, int);
void rotate_matrix (struct glyph_matrix *, int, int, int);
void increment_matrix_positions (struct glyph_matrix *,
int, int, ptrdiff_t, ptrdiff_t);
void blank_row (struct window *, struct glyph_row *, int);
void enable_glyph_matrix_rows (struct glyph_matrix *, int, int, int);
void clear_glyph_row (struct glyph_row *);
void prepare_desired_row (struct glyph_row *);
void set_window_update_flags (struct window *, int);
void update_single_window (struct window *, int);
void do_pending_window_change (int);
void change_frame_size (struct frame *, int, int, int, int, int);
void init_display (void);
void syms_of_display (void);
extern void adjust_glyphs (struct frame *);
extern void free_glyphs (struct frame *);
extern void free_window_matrices (struct window *);
extern void check_glyph_memory (void);
extern void mirrored_line_dance (struct glyph_matrix *, int, int, int *,
char *);
extern void clear_glyph_matrix (struct glyph_matrix *);
extern void clear_current_matrices (struct frame *f);
extern void clear_desired_matrices (struct frame *);
extern void shift_glyph_matrix (struct window *, struct glyph_matrix *,
int, int, int);
extern void rotate_matrix (struct glyph_matrix *, int, int, int);
extern void increment_matrix_positions (struct glyph_matrix *,
int, int, ptrdiff_t, ptrdiff_t);
extern void blank_row (struct window *, struct glyph_row *, int);
extern void enable_glyph_matrix_rows (struct glyph_matrix *, int, int, int);
extern void clear_glyph_row (struct glyph_row *);
extern void prepare_desired_row (struct glyph_row *);
extern void set_window_update_flags (struct window *, int);
extern void update_single_window (struct window *, int);
extern void do_pending_window_change (int);
extern void change_frame_size (struct frame *, int, int, int, int, int);
extern void init_display (void);
extern void syms_of_display (void);
extern Lisp_Object Qredisplay_dont_pause;
void spec_glyph_lookup_face (struct window *, GLYPH *);
extern void spec_glyph_lookup_face (struct window *, GLYPH *);
/* Defined in terminal.c */
......
......@@ -3337,6 +3337,39 @@ update_frame (struct frame *f, int force_p, int inhibit_hairy_id_p)
return paused_p;
}
/* Update a TTY frame F that has a menu dropped down over some of its
glyphs. This is like the second part of update_frame, but it
doesn't call build_frame_matrix, because we already have the
desired matrix prepared, and don't want it to be overwritten by the
text of the normal display. */
void
update_frame_with_menu (struct frame *f)
{
struct window *root_window = XWINDOW (f->root_window);
xassert (FRAME_TERMCAP_P (f));
/* We are working on frame matrix basis. Set the frame on whose
frame matrix we operate. */
set_frame_matrix_frame (f);
/* Update the display */
update_begin (f);
paused_p = update_frame_1 (f, 1, 1);
update_end (f);
if (FRAME_TTY (f)->termscript)
fflush (FRAME_TTY (f)->termscript);
fflush (FRAME_TTY (f)->output);
/* Check window matrices for lost pointers. */
#if GLYPH_DEBUG
check_window_matrix_pointers (root_window);
add_frame_display_history (f, paused_p);
#endif
/* Reset flags indicating that a window should be updated. */
set_window_update_flags (root_window, 0);
}
/************************************************************************
......
......@@ -1323,20 +1323,31 @@ no quit occurs and `x-popup-menu' returns nil. */)
/* FIXME: Use a terminal hook! */
#if defined HAVE_NTGUI
selection = w32_menu_show (f, xpos, ypos, for_click,
keymaps, title, &error_name);
#elif defined HAVE_NS
selection = ns_menu_show (f, xpos, ypos, for_click,
keymaps, title, &error_name);
#else /* MSDOS and X11 */
if (FRAME_W32_P (f))
selection = w32_menu_show (f, xpos, ypos, for_click,
keymaps, title, &error_name);
else
#endif
#if defined HAVE_NS
if (FRAME_NS_P (f))
selection = ns_menu_show (f, xpos, ypos, for_click,
keymaps, title, &error_name);
else
#endif
#if (defined (HAVE_X_WINDOWS) || defined (MSDOS))
/* Assume last_event_timestamp is the timestamp of the button event.
Is this assumption ever violated? We can't use the timestamp
stored within POSITION because there the top bits from the actual
timestamp may be truncated away (Bug#4930). */
selection = xmenu_show (f, xpos, ypos, for_click,
keymaps, title, &error_name,
last_event_timestamp);
if (FRAME_X_P (f) || FRAME_MSDOS_P (f))
selection = xmenu_show (f, xpos, ypos, for_click,
keymaps, title, &error_name,
last_event_timestamp);
else
#endif
if (FRAME_TERMCAP_P (f))
selection = tty_menu_show (f, xpos, ypos, for_click,
keymaps, title, &error_name);
UNBLOCK_INPUT;
......
......@@ -51,4 +51,6 @@ extern Lisp_Object ns_menu_show (FRAME_PTR, int, int, int, int,
Lisp_Object, const char **);
extern Lisp_Object xmenu_show (FRAME_PTR, int, int, int, int,
Lisp_Object, const char **, Time);
extern Lisp_Object tty_menu_show (FRAME_PTR, int, int, int, int,
Lisp_Object, const char **);
#endif /* MENU_H */
......@@ -1389,13 +1389,6 @@ IT_delete_glyphs (struct frame *f, int n)
abort ();
}
/* set-window-configuration on window.c needs this. */
void
x_set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval)
{
set_menu_bar_lines (f, value, oldval);
}
/* This was copied from xfaces.c */
extern Lisp_Object Qbackground_color;
......
......@@ -58,6 +58,10 @@ static int been_here = -1;
#include "xterm.h"
#endif
#ifdef HAVE_MENUS
#include "menu.h"
#endif
#ifndef O_RDWR
#define O_RDWR 2
#endif
......@@ -2833,6 +2837,803 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop,
}
#endif /* HAVE_GPM */
/***********************************************************************
Menus
***********************************************************************/
#if defined (HAVE_MENUS) && !defined (MSDOS)
/* TTY menu implementation and main ideas are borrowed from msdos.c.
However, unlike on MSDOS, where the menu text is drawn directly to
the screen, on a TTY we use display_string (see xdisp.c) to put the
glyphs produced from the menu items into the desired_matrix glyph
matrix, and then call update_frame to deliver the results to the
glass. The previous contents of the screen, in the form of the
current_matrix, is stashed away, and used to restore screen
contents when the menu selection changes or when the final
selection is made and the menu should be popped down.
The idea of this implementation was suggested by Gerd Moellmann. */
#define TTYM_FAILURE -1
#define TTYM_SUCCESS 1
#define TTYM_NO_SELECT 2
#define TTYM_IA_SELECT 3
/* These hold text of the current and the previous menu help messages. */
static const char *menu_help_message, *prev_menu_help_message;
/* Pane number and item number of the menu item which generated the
last menu help message. */
static int menu_help_paneno, menu_help_itemno;
typedef struct tty_menu_struct
{
int count;
char **text;
struct tty_menu_struct **submenu;
int *panenumber; /* Also used as enable. */
int allocated;
int panecount;
int width;
const char **help_text;
} tty_menu;
/* Create a brand new menu structure. */
static tty_menu *
tty_menu_create (void)
{
tty_menu *menu;
menu = (tty_menu *) xmalloc (sizeof (tty_menu));
menu->allocated = menu->count = menu->panecount = menu->width = 0;
return menu;
}
/* Allocate some (more) memory for MENU ensuring that there is room for one
for item. */
static void
tty_menu_make_room (tty_menu *menu)
{
if (menu->allocated == 0)
{
int count = menu->allocated = 10;
menu->text = (char **) xmalloc (count * sizeof (char *));
menu->submenu = (tty_menu **) xmalloc (count * sizeof (tty_menu *));
menu->panenumber = (int *) xmalloc (count * sizeof (int));
menu->help_text = (const char **) xmalloc (count * sizeof (char *));
}
else if (menu->allocated == menu->count)
{
int count = menu->allocated = menu->allocated + 10;
menu->text
= (char **) xrealloc (menu->text, count * sizeof (char *));
menu->submenu
= (tty_menu **) xrealloc (menu->submenu, count * sizeof (tty_menu *));
menu->panenumber
= (int *) xrealloc (menu->panenumber, count * sizeof (int));
menu->help_text
= (const char **) xrealloc (menu->help_text, count * sizeof (char *));
}
}
/* Search the given menu structure for a given pane number. */
static tty_menu *
tty_menu_search_pane (tty_menu *menu, int pane)
{
int i;
tty_menu *try;
for (i = 0; i < menu->count; i++)
if (menu->submenu[i])
{
if (pane == menu->panenumber[i])
return menu->submenu[i];
if ((try = tty_menu_search_pane (menu->submenu[i], pane)))
return try;
}
return (tty_menu *) 0;
}
/* Determine how much screen space a given menu needs. */
static void
tty_menu_calc_size (tty_menu *menu, int *width, int *height)
{
int i, h2, w2, maxsubwidth, maxheight;
maxsubwidth = 0;
maxheight = menu->count;
for (i = 0; i < menu->count; i++)
{
if (menu->submenu[i])
{
tty_menu_calc_size (menu->submenu[i], &w2, &h2);
if (w2 > maxsubwidth) maxsubwidth = w2;
if (i + h2 > maxheight) maxheight = i + h2;
}
}
*width = menu->width + maxsubwidth;
*height = maxheight;
}
/* Display MENU at (X,Y) using FACES. */
#define BUILD_CHAR_GLYPH(GLYPH, CODE, FACE_ID, PADDING_P) \
do \
{ \
(GLYPH).type = CHAR_GLYPH; \
SET_CHAR_GLYPH ((GLYPH), CODE, FACE_ID, PADDING_P); \
(GLYPH).charpos = -1; \
} \
while (0)
static void
tty_menu_display (tty_menu *menu, int y, int x, int pn, int *faces,
int disp_help)
{
int i, face, width, mx = -1, my = -1, enabled, mousehere, row, col;
struct frame *sf = SELECTED_FRAME ();
struct tty_display_info *tty = FRAME_TTY (sf);
#if defined (HAVE_MOUSE) || defined (HAVE_GPM)
Lisp_Object lmx, lmy, lisp_dummy;
enum scroll_bar_part part_dummy;
Time time_dummy;
if (FRAME_TERMINAL (sf)->mouse_position_hook)
(*FRAME_TERMINAL (sf)->mouse_position_hook) (&sf, -1,
&lispy_dummy, &party_dummy,
&lmx, &lmy,
&time_dummy);
if (!NILP (lmx))
{
mx = XINT (lmx);
my = XINT (lmy);
}
else
{
mx = x;
my = y;
}
#else
/* FIXME: need to set mx and my from cursor movement commands. */
mx = x;
my = y;
#endif
menu_help_message = NULL;
width = menu->width;
col = curX (tty);
row = curY (tty);
#if 0
IT_update_begin (sf); /* FIXME: do we need an update_begin_hook? */
#endif
for (i = 0; i < menu->count; i++)
{
int max_width = width + 2;
cursor_to (sf, y + i, x);
enabled
= (!menu->submenu[i] && menu->panenumber[i]) || (menu->submenu[i]);
mousehere = (y + i == my && x <= mx && mx < x + max_width);
face = faces[enabled + mousehere * 2];
/* Display the menu help string for the i-th menu item even if
the menu item is currently disabled. That's what the GUI
code does. */
if (disp_help && enabled + mousehere * 2 >= 2)
{
menu_help_message = menu->help_text[i];
menu_help_paneno = pn - 1;
menu_help_itemno = i;
}
display_tty_menu_item (menu->text[i], face, y + i, x,
menu->submenu[i] != NULL);
}
update_frame_with_menu (sf);
cursor_to (sf, row, col);
}
/* --------------------------- X Menu emulation ---------------------- */
/* Report availability of menus. */
int
have_menus_p (void) { return 1; }
/* Create a new pane and place it on the outer-most level. */
int
tty_menu_add_pane (Display *foo, tty_menu *menu, const char *txt)
{
int len;
const char *p;
tty_menu_make_room (menu);
menu->submenu[menu->count] = tty_menu_create ();
menu->text[menu->count] = (char *)txt;
menu->panenumber[menu->count] = ++menu->panecount;
menu->help_text[menu->count] = NULL;
menu->count++;
/* Update the menu width, if necessary. */
for (len = 0, p = txt; *p; )
{
int ch_len;
int ch = STRING_CHAR_AND_LENGTH (p, ch_len);
len += CHAR_WIDTH (ch);
p += ch_len;
}
if (len > menu->width)
menu->width = len;
return menu->panecount;
}
/* Create a new item in a menu pane. */
int
tty_menu_add_selection (tty_menu *menu, int pane,
char *txt, int enable, char const *help_text)
{
int len;
char *p;
if (pane)
if (!(menu = tty_menu_search_pane (menu, pane)))
return TTYM_FAILURE;
tty_menu_make_room (menu);
menu->submenu[menu->count] = (tty_menu *) 0;
menu->text[menu->count] = txt;
menu->panenumber[menu->count] = enable;
menu->help_text[menu->count] = help_text;
menu->count++;
/* Update the menu width, if necessary. */
for (len = 0, p = txt; *p; )
{
int ch_len;
int ch = STRING_CHAR_AND_LENGTH (p, ch_len);
len += CHAR_WIDTH (ch);
p += ch_len;
}
if (len > menu->width)
menu->width = len;
return TTYM_SUCCESS;
}
/* Decide where the menu would be placed if requested at (X,Y). */
void
tty_menu_locate (tty_menu *menu, int x, int y,
int *ulx, int *uly, int *width, int *height)
{
tty_menu_calc_size (menu, width, height);
*ulx = x + 1;
*uly = y;
*width += 2;
}
struct tty_menu_state
{
void *screen_behind;
tty_menu *menu;
int pane;
int x, y;
};
/* Display menu, wait for user's response, and return that response. */
int
tty_menu_activate (tty_menu *menu, int *pane, int *selidx,
int x0, int y0, char **txt,
void (*help_callback)(char const *, int, int))
{
struct tty_menu_state *state;
int statecount, x, y, i, b, screensize, leave, result, onepane;
int title_faces[4]; /* face to display the menu title */
int faces[4], buffers_num_deleted = 0;
struct frame *sf = SELECTED_FRAME ();
Lisp_Object saved_echo_area_message, selectface;
/* Don't allow non-positive x0 and y0, lest the menu will wrap
around the display. */
if (x0 <= 0)
x0 = 1;
if (y0 <= 0)
y0 = 1;
/* We will process all the mouse events directly, so we had
better prevent dos_rawgetc from stealing them from us. */
mouse_preempted++;
state = alloca (menu->panecount * sizeof (struct tty_menu_state));
screensize = screen_size * 2;
faces[0]
= lookup_derived_face (sf, intern ("tty-menu-disabled-face"),
DEFAULT_FACE_ID, 1);
faces[1]
= lookup_derived_face (sf, intern ("tty-menu-enabled-face"),
DEFAULT_FACE_ID, 1);
selectface = intern ("tty-menu-selected-face");
faces[2] = lookup_derived_face (sf, selectface,
faces[0], 1);
faces[3] = lookup_derived_face (sf, selectface,
faces[1], 1);
/* Make sure the menu title is always displayed with
`msdos-menu-active-face', no matter where the mouse pointer is. */
for (i = 0; i < 4; i++)
title_faces[i] = faces[3];
statecount = 1;
/* Don't let the title for the "Buffers" popup menu include a
digit (which is ugly).
This is a terrible kludge, but I think the "Buffers" case is
the only one where the title includes a number, so it doesn't
seem to be necessary to make this more general. */
if (strncmp (menu->text[0], "Buffers 1", 9) == 0)
{
menu->text[0][7] = '\0';
buffers_num_deleted = 1;
}
#if 0
/* We need to save the current echo area message, so that we could
restore it below, before we exit. See the commentary below,
before the call to message_with_string. */
saved_echo_area_message = Fcurrent_message ();
#endif
state[0].menu = menu;
mouse_off (); /* FIXME */
ScreenRetrieve (state[0].screen_behind = xmalloc (screensize)); /* FIXME */
/* Turn off the cursor. Otherwise it shows through the menu
panes, which is ugly. */
show_cursor (0); /* FIXME: need a new hook. */
/* Display the menu title. */
tty_menu_display (menu, y0 - 1, x0 - 1, 1, title_faces, 0);
if (buffers_num_deleted)
menu->text[0][7] = ' ';
if ((onepane = menu->count == 1 && menu->submenu[0]))
{
menu->width = menu->submenu[0]->width;
state[0].menu = menu->submenu[0];
}
else
{
state[0].menu = menu;
}
state[0].x = x0 - 1;
state[0].y = y0;
state[0].pane = onepane;
mouse_last_x = -1; /* A hack that forces display. */
leave = 0;
while (!leave)
{
if (!mouse_visible) mouse_on ();
mouse_check_moved ();
if (sf->mouse_moved)
{
sf->mouse_moved = 0;
result = TTYM_IA_SELECT;
mouse_get_xy (&x, &y);
for (i = 0; i < statecount; i++)
if (state[i].x <= x && x < state[i].x + state[i].menu->width + 2)
{
int dy = y - state[i].y;
if (0 <= dy && dy < state[i].menu->count)
{
if (!state[i].menu->submenu[dy])
{
if (state[i].menu->panenumber[dy])
result = TTYM_SUCCESS;
else
result = TTYM_IA_SELECT;
}
*pane = state[i].pane - 1;
*selidx = dy;
/* We hit some part of a menu, so drop extra menus that
have been opened. That does not include an open and
active submenu. */
if (i != statecount - 2
|| state[i].menu->submenu[dy] != state[i+1].menu)
while (i != statecount - 1)
{
statecount--;
mouse_off ();
ScreenUpdate (state[statecount].screen_behind);
xfree (state[statecount].screen_behind);
}
if (i == statecount - 1 && state[i].menu->submenu[dy])
{
tty_menu_display (state[i].menu,
state[i].y,
state[i].x,
state[i].pane,
faces, 1);
state[statecount].menu = state[i].menu->submenu[dy];
state[statecount].pane = state[i].menu->panenumber[dy];
mouse_off ();
ScreenRetrieve (state[statecount].screen_behind
= xmalloc (screensize));
state[statecount].x
= state[i].x + state[i].menu->width + 2;
state[statecount].y = y;
statecount++;
}
}
}
tty_menu_display (state[statecount - 1].menu,
state[statecount - 1].y,
state[statecount - 1].x,
state[statecount - 1].pane,