/***********************************************************************\
		Editor mined
		menu handling part
\***********************************************************************/

#include "syntax.h"

#include "mined.h"

#include "io.h"

#define menu_wheel


/***********************************************************************\
	Menu types
\***********************************************************************/

typedef struct {
	char * menuname; int menulen;
	menuitemtype * menuitems;
	} menutype;

typedef struct {
	char * (* dispflag) ();
	void (* toggle) ();
	char * menutitle;
	menuitemtype * menu;
	int menulen;
	} flagitemtype;


/***********************************************************************\
	Global variables
\***********************************************************************/

/* mode tuning */
#define use_graphic_borders	(menu_border_style != '@')
local int popupmenumargin;

local int flags_pos = 62;
local int flags_displayed = 9;

/* layout tuning */
local int menu_width = 12;
local int popupmenu_width = 16;
local int horizontal_bar_width = 1;

/* state */
local int first_dirty_line = -1;
local int last_dirty_line = 0;
local menuitemtype * last_menu;
local int last_menulen;
local int last_menuwidth;
local FLAG last_disp_only;
local char * last_menutitle;

local FLAG pulldownmenu_displayed = False;
local FLAG popupmenu_displayed = False;

local FLAG menuline_dirty = False;

#ifdef full_Keymap_menu
local char keymap_menuname [22];
#endif


/***********************************************************************\
	Local functions
\***********************************************************************/
local void
	action_menu _((menuitemtype * menu, int menulen, 
			int column, int line, char * title, FLAG is_flag_menu));
local void
	MINMEN _((int column, int itemno));


/***********************************************************************\
	utf8_col_count () returns # of screen columns in the UTF-8 string
\***********************************************************************/
local int
utf8_col_count (string)
  char * string;
{
	int count = 0;
	char * start = string;

	If string != NIL_PTR
	Then	Dowhile * string != '\0'
		Do	advance_utf8_scr (& string, & count, start);
		Done
	Fi
	return count;
}


/***********************************************************************\
	menu state and saved menu status for redrawmenu ()
\***********************************************************************/

local FLAG pulldownmenu_active = False;
local FLAG popupmenu_active = False;

local int last_pulldown_menu;
local int last_pulldown_item;

local int last_popup_column;
local int last_popup_line;
local int last_popup_item;
local int last_is_flag_menu;
local int last_minwidth;
local int last_scroll;
local int last_maxscroll;
local int last_popup_index;


/***********************************************************************\
	Flag operations
\***********************************************************************/

local void
	toggle_nothing ()
{
}

local char *
	dispnothing ()
{
	return " ";
}


local void
	toggle_HOP ()
{
	If hop_flag > 0
	Then	hop_flag = 0;
	Else	hop_flag = 1;
	Fi
	displayflags ();
}

local char *
	dispHOP ()
{
	return hop_flag > 0 ? "#H" : "h";
}


#ifdef debug_ring_buffer
local char *
	disp_buffer_open ()
{
	static char f [3];
	f [0] = '#';
	f [1] = buffer_open_flag + '0';
	f [2] = '\0';
	return f;
}
#endif


local void
	select_editmode (menu, i)
	menuitemtype * menu;
	int i;
{
	If i == 0
	Then	EDITmode ();
	Else	VIEWmode ();
	Fi
}

local char *
	dispVIEW ()
{
	return viewonly ? "#V" : "E";
}

local int
	editorviewon (item, i)
	menuitemtype * item;
	int i;
{
	return viewonly == i;
}


global void
	toggle_append ()
{
	If append_flag
	Then	append_flag = False;
	Else	append_flag = True;
	Fi
	displayflags ();
}

local void
	select_buffermode (menu, i)
	menuitemtype * menu;
	int i;
{
	If i == 0
	Then	append_flag = False;
	Else	append_flag = True;
	Fi
}

local void
	toggle_pastebuf_mode ()
{
	If pastebuf_utf8
	Then	pastebuf_utf8 = False;
	Else	pastebuf_utf8 = True;
	Fi
	displayflags ();
}

local int
	pastebuf_utf8on (item, i)
	menuitemtype * item;
	int i;
{
	return pastebuf_utf8;
}

local char *
	disppastebuf ()
{
	If pastebuf_utf8 && ! utf8_text
	Then	If append_flag
		Then	return "##+";
		Else	return "##=";
		Fi
	Else	If append_flag
		Then	return "#+";
		Else	return "=";
		Fi
	Fi
}

local int
	overwriteorappendon (item, i)
	menuitemtype * item;
	int i;
{
	return append_flag == i;
}


local void
	toggle_autoindent ()
{
	If autoindent
	Then	autoindent = False;
	Else	autoindent = True;
	Fi
}

local void
	select_autoindent (menu, i)
	menuitemtype * menu;
	int i;
{
	If i == 0
	Then	autoindent = True;
	Else	autoindent = False;
	Fi
}

local char *
	disp_autoindent ()
{
	If autoindent
	Then	return "#»";
	Else	return "#¦";
	Fi
}

local int
	autoindenton (item, i)
	menuitemtype * item;
	int i;
{
	return autoindent != i;
}


local void
	toggle_JUSlevel ()
{
	JUSlevel ++;
	If JUSlevel > 2
	Then	JUSlevel = 0;
	Fi
}

local void
	select_justify (menu, i)
	menuitemtype * menu;
	int i;
{
	JUSlevel = i;
}

local char *
	dispJUSlevel ()
{
	If JUSlevel == 0
	Then	return "j";
	Elsif JUSlevel == 1
	Then	return "#j";
	Else	return "#J";
	Fi
}

local int
	justifymodeon (item, i)
	menuitemtype * item;
	int i;
{
	return JUSlevel == i;
}


local void
	toggle_JUSmode ()
{
	JUSmode = 1 - JUSmode;
}

local void
	select_paragraph (menu, i)
	menuitemtype * menu;
	int i;
{
	JUSmode = i;
}

local char *
	dispJUSmode ()
{
	return JUSmode == 1 ? "#«" : "# ";
}

local int
	paragraphmodeon (item, i)
	menuitemtype * item;
	int i;
{
	return JUSmode == i;
}


local char * prev_text_encoding = "";

global void
	toggle_encoding ()
{
	char * curpos = cur_text;

	char * new_prev_text_encoding = get_text_encoding ();

	/** Determine encoding to switch to:
		1. Previously set text encoding (toggle) if defined
		2. Terminal encoding if different from current text encoding
		3. UTF-8 unless currently UTF-8
		4. Latin-1
	 */
	If * prev_text_encoding == '\0'
	Then	prev_text_encoding = get_term_encoding ();
		If prev_text_encoding == new_prev_text_encoding
		Then	If utf8_text
			Then	prev_text_encoding = "ISO 8859-1";
			Else	prev_text_encoding = "UTF-8";
			Fi
		Fi
	Fi

	If set_text_encoding (prev_text_encoding, ' ')
	Then
		prev_text_encoding = new_prev_text_encoding;

		RD ();
		displaymenuline ();
		/* move cursor to or behind actual previous character position */
		move_address (curpos, y);
		/* adjust not to stay amidst a character */
		move_to (x, y);
	Fi
}

global void
	change_encoding (new_text_encoding)
	char * new_text_encoding;
{
	If ! streq (get_text_encoding (), new_text_encoding)
	Then	prev_text_encoding = get_text_encoding ();
	Fi
	set_text_encoding (new_text_encoding, ' ');
}

local void
	select_encoding (menu, i)
	menuitemtype * menu;
	int i;
{
	char * curpos = cur_text;

#ifdef selectable_term_encoding
	/* this is a debugging option */
	If hop_flag
	Then	If set_term_encoding (menu [i].hopitemname, ' ')
		Then	RD ();
			move_address (curpos, y);
			move_to (x, y);
		Else	ring_bell ();
		Fi
		return;
	Fi
#endif

	char * new_prev_text_encoding = get_text_encoding ();

	If set_text_encoding (menu [i].hopitemname, ' ')
	Then
#ifdef cjk_term_constraint
		If cjk_term & ! cjk_text
		Then	error ("Non-CJK text encoding not supported in CJK terminal");
			ring_bell ();
			(void) set_text_encoding (new_prev_text_encoding);
			return;
		Fi
#endif
		prev_text_encoding = new_prev_text_encoding;

		RD ();
		menuline_dirty = True;
		/* move cursor to or behind actual previous character position */
		move_address (curpos, y);
		/* adjust not to stay amidst a character */
		move_to (x, y);
	Else	error ("Selected encoding not supported in this version");
		ring_bell ();
		return;
	Fi
}

local char cjk_flag [3];

local char *
	disp_encoding_1 ()
{
	If utf8_text && utf16_text
	Then	If utf16_little_endian
		Then	return "#6";
		Else	return "#1";
		Fi
	Elsif utf8_text
	Then	return "#U";
	Elsif	cjk_text || mapped_text
	Then	cjk_flag [0] = '#';
		cjk_flag [1] = text_encoding_flag [0];
		cjk_flag [2] = '\0';
		return cjk_flag;
	Else	return "#L";
	Fi
}

local char *
	disp_encoding_2 ()
{
	If utf8_text && utf16_text
	Then	If utf16_little_endian
		Then	return "#1";
		Else	return "#6";
		Fi
	Elsif utf8_text
	Then	return "#8";
	Elsif	cjk_text || mapped_text
	Then	cjk_flag [0] = '#';
		cjk_flag [1] = text_encoding_flag [1];
		cjk_flag [2] = '\0';
		return cjk_flag;
	Else	return "#1";
	Fi
}

local int
	encodingon (item, i)
	menuitemtype * item;
	int i;
{
	return streq (item->hopitemname, get_text_encoding ());
}


local void
	toggle_combining ()
{
	If encoding_has_combining ()
	Then	If combining_screen
		Then	char * curpos = cur_text;
			If combining_mode
			Then	combining_mode = False;
			Else	combining_mode = True;
			Fi
			RD ();
			displaymenuline ();
			/* move cursor to actual previous character position */
			move_address (curpos, y);
			/* adjust not to stay amidst a combined character */
		/*	move_to (x, y);	*/
		Elsif ! combining_mode
		Then	error ("Terminal cannot display combined characters");
		Fi
	Fi
}

local void
	select_combining (menu, i)
	menuitemtype * menu;
	int i;
{
	If encoding_has_combining ()
	Then	If combining_screen
		Then	char * curpos = cur_text;
			FLAG old_combining_mode = combining_mode;
			If i == 0
			Then	combining_mode = True;
			Else	combining_mode = False;
			Fi
			If combining_mode != old_combining_mode
			Then	RD ();
				menuline_dirty = True;
				/* move cursor to actual previous character position */
				move_address (curpos, y);
			Fi
		Elsif i == 0
		Then	error ("Terminal cannot display combined characters");
		Fi
	Fi
}

local char *
	disp_combining ()
{
	If encoding_has_combining ()
	Then	If combining_screen && combining_mode
		Then	return "ç";
		Else	return "##`";
		Fi
	Else	return " ";
	Fi
}

local int
	comborsepon (item, i)
	menuitemtype * item;
	int i;
{
	return (! combining_mode) == i;
}


local char *
	disp_leftquote ()
{
	If smart_quotes
	Then	return quote_mark (quote_type, 0);
	Else	return " ";
	Fi
}

local char *
	disp_rightquote ()
{
	int utfcount;
	unsigned long unichar;
	char * quotemark;

	If smart_quotes
	Then	quotemark = quote_mark (quote_type, 0);
		utf8_info (quotemark, & utfcount, & unichar);
		If iswide (unichar)
		Then	/* left quote mark used up the space already */
			return "";
		Else	/* proceed to right quote mark */
			advance_utf8 (& quotemark);
			utf8_info (quotemark, & utfcount, & unichar);
			If iswide (unichar)
			Then	/* no space to display */
				return " ";
			Else
				return quotemark;
			Fi
		Fi
	Else	return " ";
	Fi
}


local void
	toggle_HTML ()
{
	If dim_HTML
	Then	dim_HTML = False;
	Else	dim_HTML = True;
	Fi
	RD ();
}


local void
	select_keymap_entry (menu, i)
	menuitemtype * menu;
	int i;
{
	setKEYMAP (menu [i].hopitemname);
}

local int
	keymapon (item, i)
	menuitemtype * item;
	int i;
{
	return streq (keyboard_mapping, item->hopitemname);
}


local int prev_quote_type = 0;

local void
	select_quote_type (menu, i)
	menuitemtype * menu;
	int i;
{
	prev_quote_type = quote_type;
	set_quote_type (i);
}

local void
	toggle_quote_type ()
{
	int prev = prev_quote_type;
	prev_quote_type = quote_type;
	set_quote_type (prev);
}

local int
	quoteon (item, i)
	menuitemtype * item;
	int i;
{
	return quote_type == i;
}


local char km [3];

local char *
	dispKEYMAP0 ()
{
	If allow_keymap
	Then	km [0] = '#';
		km [1] = keyboard_mapping [0];
		km [2] = '\0';
		return km;
	Else	return " ";
	Fi
}

local char *
	dispKEYMAP1 ()
{
	If allow_keymap
	Then	km [0] = '#';
		km [1] = keyboard_mapping [1];
		km [2] = '\0';
		return km;
	Else	return " ";
	Fi
}


local char *
	dispinfo ()
{
	return "?";
}

local void
	toggle_info ()
{
}

local void
	select_file_info ()
{
	hop_flag = 1;
	FS ();

	if (always_disp_fstat) {
		always_disp_code = False;
		if (! disp_Han_full) {
			always_disp_Han = False;
		}
	}
}

local void
	select_char_info ()
{
	hop_flag = 1;
	display_code ();

	if (always_disp_code) {
		always_disp_fstat = False;
		if (! disp_Han_full) {
			always_disp_Han = False;
		}
	}
}

local void
	select_Han_info ()
{
	always_disp_Han = ! always_disp_Han;

	if (always_disp_Han && ! disp_Han_full) {
		always_disp_fstat = False;
		always_disp_code = False;
	}
}

local void
	toggle_Mandarin ()
{
	disp_Han_Mandarin = ! disp_Han_Mandarin;
	If disp_Han_Mandarin
	Then	always_disp_Han = True;
	Fi
}

local void
	toggle_Cantonese ()
{
	disp_Han_Cantonese = ! disp_Han_Cantonese;
	If disp_Han_Cantonese
	Then	always_disp_Han = True;
	Fi
}

local void
	toggle_Japanese ()
{
	disp_Han_Japanese = ! disp_Han_Japanese;
	If disp_Han_Japanese
	Then	always_disp_Han = True;
	Fi
}

local void
	toggle_Sino_Japanese ()
{
	disp_Han_Sino_Japanese = ! disp_Han_Sino_Japanese;
	If disp_Han_Sino_Japanese
	Then	always_disp_Han = True;
	Fi
}

local void
	toggle_Korean ()
{
	disp_Han_Korean = ! disp_Han_Korean;
	If disp_Han_Korean
	Then	always_disp_Han = True;
	Fi
}

local void
	toggle_Vietnamese ()
{
	disp_Han_Vietnamese = ! disp_Han_Vietnamese;
	If disp_Han_Vietnamese
	Then	always_disp_Han = True;
	Fi
}

local void
	toggle_HanyuPinlu ()
{
	disp_Han_HanyuPinlu = ! disp_Han_HanyuPinlu;
	If disp_Han_HanyuPinlu
	Then	always_disp_Han = True;
	Fi
}

local void
	toggle_Tang ()
{
	disp_Han_Tang = ! disp_Han_Tang;
	If disp_Han_Tang
	Then	always_disp_Han = True;
	Fi
}

local void
	toggle_Han_short_description ()
{
	If disp_Han_description && disp_Han_full
	Then	disp_Han_full = False;
	Else	disp_Han_description = ! disp_Han_description;
		disp_Han_full = False;
	Fi
	If disp_Han_description
	Then	always_disp_Han = True;
	Fi
}

local void
	toggle_Han_full_description ()
{
	If disp_Han_description && ! disp_Han_full
	Then	disp_Han_full = True;
	Else	disp_Han_description = ! disp_Han_description;
		disp_Han_full = True;
	Fi
	If disp_Han_description
	Then	always_disp_Han = True;
	Fi
}

local int
	infoon_file (menu, i)
	menuitemtype * menu;
	int i;
{
	return always_disp_fstat;
}

local int
	infoon_char (menu, i)
	menuitemtype * menu;
	int i;
{
	return always_disp_code;
}

local int
	infoon_Han (menu, i)
	menuitemtype * menu;
	int i;
{
	return always_disp_Han;
}

local int
	infoon_Mandarin (menu, i)
	menuitemtype * menu;
	int i;
{
	return disp_Han_Mandarin;
}

local int
	infoon_Cantonese (menu, i)
	menuitemtype * menu;
	int i;
{
	return disp_Han_Cantonese;
}

local int
	infoon_Japanese (menu, i)
	menuitemtype * menu;
	int i;
{
	return disp_Han_Japanese;
}

local int
	infoon_Sino_Japanese (menu, i)
	menuitemtype * menu;
	int i;
{
	return disp_Han_Sino_Japanese;
}

local int
	infoon_Korean (menu, i)
	menuitemtype * menu;
	int i;
{
	return disp_Han_Korean;
}

local int
	infoon_Vietnamese (menu, i)
	menuitemtype * menu;
	int i;
{
	return disp_Han_Vietnamese;
}

local int
	infoon_HanyuPinlu (menu, i)
	menuitemtype * menu;
	int i;
{
	return disp_Han_HanyuPinlu;
}

local int
	infoon_Tang (menu, i)
	menuitemtype * menu;
	int i;
{
	return disp_Han_Tang;
}

local int
	infoon_descr_short (menu, i)
	menuitemtype * menu;
	int i;
{
	return disp_Han_description && ! disp_Han_full;
}

local int
	infoon_descr_full (menu, i)
	menuitemtype * menu;
	int i;
{
	return disp_Han_description && disp_Han_full;
}


/***********************************************************************\
	Pulldown menu tables
\***********************************************************************/

global void
	separator ()
{
}


local menuitemtype Filemenu [] =
{
	{"Open", EDIT, ""},
	{"View", VIEW, ""},
	{"Save", WT, ""},
	{"Save To ...", NN, ""},
	{"Save As ...", SAVEAS, ""},
	{"Save & Exit", EXED, ""},
	{"", separator, ""},
	{"Insert file", INSFILE, ""},
	{"Copy to file", WB, "Append to file"},
	{"Print", PRINT, ""},
	{"", separator, ""},
	{"Check Out", checkout, ""},
	{"Check In", checkin, ""},
	{"", separator, ""},
	{"Next file", NXTFILE, "First file"},
	{"Prev file", PRVFILE, "Last file"},
	{"N-th file", NTHFILE, ""},
	{"Save Position", SAVPOS, ""},
	{"Discard & Quit", QUED, ""},
};

local menuitemtype Editmenu [] =
{
	{"Mark", MARK, "Go to Mark"},
	{"Cut", CUT, "Cut & Append"},
	{"Copy", COPY, "Append"},
	{"Paste", PASTE, "Paste External buf"},
	{"Paste Previous", YANKRING, ""},
	{"", separator, ""},
	{"Go to Mark", GOMA, ""},
	{"Set marker N", MARKER, ""},
	{"Go marker N", GOMARKER, ""},
};

local menuitemtype Searchmenu [] =
{
	{"Find", SFW, "Find identifier"},
	{"Find backw.", SRV, "Find idf. backw."},
	{"Find again", RS, "Previous Search"},
	{"", separator, ""},
	{"Find Identifier", SIDFW, ""},
	{"Find Idf. backw.", SIDRV, ""},
	{"Find <matching>", (voidfunc) SCORR, "Find wrong enc."},
	{"Go to Idf. def.", Stag, "Go to def. ..."},
	{"", separator, ""},
	{"Substitute", GR, ""},
	{"Replace (?)", REPL, ""},
	{"Replace on line", LR, ""},
	{"", separator, ""},
	{"Go to Mark", GOMA, ""},
	{"Go to ...", GOTO, ""},
};

local menuitemtype Paragraphmenu [] =
{
	{"Justify Clever", JUSclever, "Clever justify other"},
	{"Justify Simple", JUS, "Justify other mode"},
	{"set parameters", separator, ""},
	{"Set Left margin", ADJLM, ""},
	{"Set First line left m.", ADJFLM, ""},
	{"Set Other lines l. m.", ADJNLM, ""},
	{"Set Right margin", ADJRM, ""},
	{"Toggle Auto-justif.", toggle_JUSlevel, ""},
	{"Toggle Line-end mode", toggle_JUSmode, ""},
};

local menuitemtype Extramenu [] =
{
	{"Insert control/composed", CTRLINS, ""},
	{"Insert HTML/XML tag", HTML, "Embed in HTML/XML tags"},
	{"Case toggle", LOWCAP, "Case toggle word"},
	{"Word case switch", LOWCAPWORD, ""},
	{"editing features", separator, ""},
	{"Toggle auto indent", toggle_autoindent, ""},
	{"Toggle HTML disp", toggle_HTML, ""},
	{"Toggle TAB width", toggle_tabsize, ""},
	{"character functions", separator, ""},
	{"Combine 2 chars", (voidfunc) UML, ""},
	{"ins. Hex char bytes", changehex, "char (Hex)"},
	{"ins. Unicode char value", changeuni, "char (Unicode)"},
	{"ins. Decimal char value", changedec, "char (Dec)"},
	{"ins. Octal char value", changeoct, "char (Octal)"},
	{"display options", separator, ""},
	{"Encoding", handleEncodingmenu, ""},
	{"Combined display", toggle_combining, ""},
	{"File info", FS, "Toggle File info"},
	{"Char info", display_code, "Toggle Char info"},
	{"", separator, ""},
	{"Help", HELP, ""},
};

#ifdef msdos
local menuitemtype Screensizemenu [] =
{
#ifdef msdos
	{"bigger", screenbigger, "video mode"},
	{"smaller", screensmaller, "graphic mode"},
	{"more lines", screenmorelines, "font bank"},
	{"fewer lines", screenlesslines, "char height"},
#else
	{"bigger", screenbigger, ""},
	{"smaller", screensmaller, ""},
	{"more lines", screenmorelines, ""},
	{"fewer lines", screenlesslines, ""},
#endif
	{"switch line #", LNSW, ""},
	{"cycle line #", LNCI, ""},
};
#endif


local menutype menus [] =
{
	{"File", arrlen (Filemenu), Filemenu},
	{"Edit", arrlen (Editmenu), Editmenu},
	{"Search", arrlen (Searchmenu), Searchmenu},
	{"eXtra", arrlen (Extramenu), Extramenu},
	{"Paragraph", arrlen (Paragraphmenu), Paragraphmenu},
#ifdef msdos
	{"Screen size", arrlen (Screensizemenu), Screensizemenu},
#endif
/* this is replaced by the flags keymap menu: */
#ifdef full_Keymap_menu
	{keymap_menuname, arrlen (Keymapmenu), Keymapmenu},
#endif
};


/***********************************************************************\
	Flag menu tables
\***********************************************************************/

/* Unicode quotation marks and their usage in some languages
   The UTF-8 codes of all quotation marks are either C2AB or C2BB 
   or start with either E280 or E380. This may help for efficient 
   detection during file loading (e.g. by checking if 
   current_byte & 0xDE == 0xC2).

	“ 201C; LEFT DOUBLE QUOTATION MARK; DOUBLE TURNED COMMA QUOTATION MARK
		left English, Spanish, Turkish
		right German, Danish, Polish, Russian, Romanian, Slovak, Slovenian, Czech, Hungarian
	” 201D; RIGHT DOUBLE QUOTATION MARK; DOUBLE COMMA QUOTATION MARK
		right English, Spanish, Turkish
		right Dutch, Hungarian
		left/right Swedish, Finnish
		right nested traditional Greek
	„ 201E; DOUBLE LOW-9 QUOTATION MARK; LOW DOUBLE COMMA QUOTATION MARK
		left German, Danish, Polish, Russian, Romanian, Slovak, Sloven, Czech, Hungarian
		left Dutch, Hungarian
	‟ 201F; DOUBLE HIGH-REVERSED-9 QUOTATION MARK; DOUBLE REVERSED COMMA QUOTATION MARK
		left nested traditional Greek
	‘ 2018; LEFT SINGLE QUOTATION MARK; SINGLE TURNED COMMA QUOTATION MARK
	’ 2019; RIGHT SINGLE QUOTATION MARK; SINGLE COMMA QUOTATION MARK
	‚ 201A; SINGLE LOW-9 QUOTATION MARK; LOW SINGLE COMMA QUOTATION MARK
?	‛ 201B; SINGLE HIGH-REVERSED-9 QUOTATION MARK; SINGLE REVERSED COMMA QUOTATION MARK
	« 00AB; LEFT-POINTING DOUBLE ANGLE QUOTATION MARK; LEFT POINTING GUILLEMET
		left French, Italian, Norwegian, Portuguese, Russian, Slovenian, Turkish
		right German, Polish, Slovak, Czech, Serbian, Croatian
	» 00BB; RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK; RIGHT POINTING GUILLEMET
		right French, Italian, Norwegian, Portuguese, Russian, Slovenian, Turkish
		left German, Polish, Slovak, Czech, Serbian, Croatian
		left/right Swedish, Finnish
	‹ 2039; SINGLE LEFT-POINTING ANGLE QUOTATION MARK; LEFT POINTING SINGLE GUILLEMET
	› 203A; SINGLE RIGHT-POINTING ANGLE QUOTATION MARK; RIGHT POINTING SINGLE GUILLEMET
C?	〈3008; LEFT ANGLE BRACKET; OPENING ANGLE BRACKET
C?	〉3009; RIGHT ANGLE BRACKET; CLOSING ANGLE BRACKET
C?	《300A; LEFT DOUBLE ANGLE BRACKET; OPENING DOUBLE ANGLE BRACKET
		left Chinese
C?	》300B; RIGHT DOUBLE ANGLE BRACKET; CLOSING DOUBLE ANGLE BRACKET
		right Chinese
J	「300C; LEFT CORNER BRACKET; OPENING CORNER BRACKET
		left Japanese
J	」300D; RIGHT CORNER BRACKET; CLOSING CORNER BRACKET
		right Japanese
J?	『300E; LEFT WHITE CORNER BRACKET; OPENING WHITE CORNER BRACKET
J?	』300F; RIGHT WHITE CORNER BRACKET; CLOSING WHITE CORNER BRACKET
*/


local menuitemtype Quotemenu [] =
{
	{"\"plain\"", select_quote_type, "  \"\" ''", quoteon},

	{"typographic", separator, ""},
	/*	English, Spanish, Turkish, Irish, Portuguese */
	{"“English”", select_quote_type, "  “” ‘’", quoteon},
#ifdef single_double
	/*	British English */
	{"‘British’", select_quote_type, "  ‘’ “”", quoteon},
#endif
	/*	German, Danish, Polish, Russian, Romanian, Slovak, 
		Slovenian, Czech, Hungarian, 
		Bulgarian, Serbian, Icelandic, Lithuanian */
	{"„German“", select_quote_type, "  „“ ‚‘", quoteon},
	/*	French, Italian, Portuguese, Russian, Slovenian, 
		Turkish, Spanish, Albanian, Greek */
	{"French «“.”»", select_quote_type, "  «» “”", quoteon},
	/*	Swiss, Italian, Norwegian, Portuguese, Russian, Slovenian, 
		Turkish, Spanish, Albanian, Greek */
	{"«Swiss»", select_quote_type, "  «» ‹›", quoteon},
	/*	Danish, Croatian, 
		German, Polish, Slovak, Czech, Serbian */
	{"»Danish«", select_quote_type, "  »« ›‹", quoteon},
	/*	Norwegian */
	{"Norweg. «‘’»", select_quote_type, "  «» ‘’", quoteon},
	/*	Swedish, Finnish */
	{"”Swedish”", select_quote_type, "  ”” ’’", quoteon},
	/*	Swedish, Finnish */
	{"»Swedish»", select_quote_type, "  »» ››", quoteon},
	/*	Hebrew */
	{"Hebrew ״.״", select_quote_type, "  ״״ ׳׳", quoteon},
#ifdef single_double
	/*	CJK */
	{"「亞 CJK」", select_quote_type, "「」 『』", quoteon},
#else
	/*	CJK */
	{"「亞 CJK」", select_quote_type, "『』 「」", quoteon},
#endif

	{"traditional", separator, ""},
	/*	traditional Dutch, Afrikaans, Hungarian, Polish */
	{"„Dutch”", select_quote_type, "  „” ‚’", quoteon},
	/*	traditional Greek */
	{"Greek «‟.”»", select_quote_type, "  «» ‟”", quoteon},
	/*	Chinese book marks */
	{"《Chinese》", select_quote_type, "《》 〈〉", quoteon},
};

local menuitemtype Keymapmenu [] =
{
	{"none", select_keymap_entry, "--", keymapon},
#ifdef use_keymap_tables
#include "keymapsm.h"
#endif
};

#include "charesub.h"

local menuitemtype encodingmenu [] =
{
	{"Unicode", select_encoding, "UTF-8", encodingon},

	{"8 Bit", separator, ""},
	{"Latin-1", select_encoding, "ISO 8859-1", encodingon},
#include "charemen.h"
};

local menuitemtype combiningmenu [] =
{
	{"combined", select_combining, "", comborsepon},
	{"separated", select_combining, "", comborsepon},
};

local menuitemtype infomenu [] =
{
	{"File info", select_file_info, "", infoon_file},
	{"Char info", select_char_info, "", infoon_char},
	{"Han info", select_Han_info, "", infoon_Han},
	{"display Han", separator, ""},
	{"Bottom line", toggle_Han_short_description, "", infoon_descr_short},
	{"Popup display", toggle_Han_full_description, "", infoon_descr_full},
	{"pronunciations", separator, ""},
	{"Mandarin", toggle_Mandarin, "", infoon_Mandarin},
	{"Cantonese", toggle_Cantonese, "", infoon_Cantonese},
	{"Japanese", toggle_Japanese, "", infoon_Japanese},
	{"Sino-Japanese", toggle_Sino_Japanese, "", infoon_Sino_Japanese},
	{"Korean", toggle_Korean, "", infoon_Korean},
	{"Vietnamese", toggle_Vietnamese, "", infoon_Vietnamese},
	{"Hanyu Pinlu", toggle_HanyuPinlu, "", infoon_HanyuPinlu},
	{"Tang", toggle_Tang, "", infoon_Tang},
};

local menuitemtype textmenu [] =
{
	{"edit", select_editmode, "modify", editorviewon},
	{"view", select_editmode, "readonly", editorviewon},
};

local menuitemtype buffermenu [] =
{
	{"overwrite", select_buffermode, "", overwriteorappendon},
	{"append", select_buffermode, "", overwriteorappendon},
	{"encoding", separator, ""},
	{"Unicode", toggle_pastebuf_mode, "", pastebuf_utf8on},
};

local menuitemtype autoindentmenu [] =
{
	{"auto indent", select_autoindent, "", autoindenton},
	{"off", select_autoindent, "", autoindenton},
};

local menuitemtype justifymenu [] =
{
	{"on command", select_justify, "", justifymodeon},
	{"at line end", select_justify, "", justifymodeon},
	{"always", select_justify, "", justifymodeon},
};

local menuitemtype paragraphmenu [] =
{
	{"non-blank line-end", select_paragraph, "", paragraphmodeon},
	{"empty line", select_paragraph, "", paragraphmodeon},
};


/***********************************************************************\
	Popup menu table
\***********************************************************************/

local menuitemtype Popupmenu [] =
{
	{"Mark", MARK, "Go to Mark"},
	{"Cut", CUT, "Cut-Append"},
	{"Copy", COPY, "Append"},
	{"Paste", PASTE, "Paste external"},
	{"HTML tag", HTML, "Embed HTML"},
	{"", separator, ""},
	{"Find", SFW, "FindIdf"},
	{"FindReverse", SRV, "FindIdfRev"},
	{"FindNext", RS, "Previous Find"},
	{"FindIdf", SIDFW, ""},
	{"FindIdfRev", SIDRV, ""},
	{"Go to Idf. def.", Stag, "Go to def. ..."},
	{"Go to Mark", GOMA, ""},
	{"Find matching ()", (voidfunc) SCORR, "Find wrong enc."},
};


/***********************************************************************\
	menu structure auxiliary functions
\***********************************************************************/
global int
	count_quote_types ()
{
	return arrlen (Quotemenu);
}


global char *
	quote_mark (i, n)
	int i;
	int n;
{
	char * q = Quotemenu [i].hopitemname;
	while (* q == ' ') {
		q ++;
	}
	while (n > 0) {
		advance_utf8 (& q);
		while (* q == ' ') {
			q ++;
		}
		n --;
	}
	return q;
}


global int
	lookup_quotes (q)
	char * q;
{
	int i;
	For i = 0 While i < arrlen (Quotemenu) Step i ++
	Do	If strisprefix (q, quote_mark (i, 0))
		Then	return i;
		Fi
	Done
	For i = 0 While i < arrlen (Quotemenu) Step i ++
	Do	If strisprefix (q, quote_mark (i, 2))
		Then	return i;
		Fi
	Done
	return -1;
}




/***********************************************************************\
	Flag table and next/previous function
\***********************************************************************/

local char * dispKEYMAP0 ();
local char * dispKEYMAP1 ();


local flagitemtype Flagmenu [] =
{
	{dispinfo, toggle_info, "Info display", infomenu, arrlen (infomenu)},
	{dispnothing, toggle_nothing, NIL_PTR},
	{dispKEYMAP0, toggleKEYMAP, "Input Method", Keymapmenu, arrlen (Keymapmenu)},
	{dispKEYMAP1, toggleKEYMAP, "Input Method", Keymapmenu, arrlen (Keymapmenu)},
	{dispnothing, toggle_nothing, NIL_PTR},
	{disp_leftquote, toggle_quote_type /*quote_type_down*/, "Quotes", Quotemenu, arrlen (Quotemenu)},
	{disp_rightquote, toggle_quote_type /*quote_type_up*/, "Quotes", Quotemenu, arrlen (Quotemenu)},
	{dispnothing, toggle_nothing, NIL_PTR},
	{disp_encoding_1, toggle_encoding, "Encoding", encodingmenu, arrlen (encodingmenu)},
	{disp_encoding_2, toggle_encoding, "Encoding", encodingmenu, arrlen (encodingmenu)},
	{disp_combining, toggle_combining, "Combining", combiningmenu, arrlen (combiningmenu)},
	{dispnothing, toggle_nothing, NIL_PTR},
	{dispHOP, toggle_HOP, NIL_PTR},
#ifdef debug_ring_buffer
	{disp_buffer_open, toggle_nothing, NIL_PTR},
#endif
	{dispnothing, toggle_nothing, NIL_PTR},
	{dispVIEW, toggle_VIEWmode, "Text", textmenu, arrlen (textmenu)},
	{disppastebuf, toggle_append, "Paste buf", buffermenu, arrlen (buffermenu)},
	{dispnothing, toggle_nothing, NIL_PTR},
	{disp_autoindent, toggle_autoindent, "Auto indent", autoindentmenu, arrlen (autoindentmenu)},
	{dispJUSlevel, toggle_JUSlevel, "Word wrap", justifymenu, arrlen (justifymenu)},
	{dispJUSmode, toggle_JUSmode, "Paragraph ends at", paragraphmenu, arrlen (paragraphmenu)},
};


local void
	flag_menu (i)
	int i;
{
	action_menu (Flagmenu [i].menu, 
			Flagmenu [i].menulen, 
			- (flags_pos + i), -1, 
			Flagmenu [i].menutitle, True);
}


local void
	next_flag_menu (current_menu)
	menuitemtype * current_menu;
{
	int i;
	FLAG passed = False;
	If current_menu == (menuitemtype *) 0
	Then	passed = True;
	Fi

	For i = 0 While i < arrlen (Flagmenu) Step i ++
	Do
		If Flagmenu [i].menutitle != NIL_PTR
		&& Flagmenu [i].menu == current_menu
		Then	passed = True;
		Elsif passed && Flagmenu [i].menutitle != NIL_PTR
		      && * ((* Flagmenu [i].dispflag) ()) != ' '
		Then
			/* on a double-column flag menu, move right */
			If i + 1 < arrlen (Flagmenu)
			&& Flagmenu [i + 1].menutitle != NIL_PTR
			&& streq (Flagmenu [i].menutitle, Flagmenu [i + 1].menutitle)
			Then	i ++;
			Fi
			flag_menu (i);
			return;
		Fi
	Done
	openmenu (0);
}


local void
	prev_flag_menu (current_menu)
	menuitemtype * current_menu;
{
	int i;
	FLAG passed = False;
	If current_menu == (menuitemtype *) 0
	Then	passed = True;
	Fi

	For i = arrlen (Flagmenu) - 1 While i >= 0 Step i --
	Do	If Flagmenu [i].menutitle != NIL_PTR
		&& Flagmenu [i].menu == current_menu
		Then	passed = True;
		Elsif	passed && Flagmenu [i].menutitle != NIL_PTR
			&& * ((* Flagmenu [i].dispflag) ()) != ' '
		Then	flag_menu (i);
			return;
		Fi
	Done
	openmenu (arrlen (menus) - 1);
}




/***********************************************************************\
	putblock () writes a block graphics character
\***********************************************************************/
local void
	putblock (c)
	char c;
{
	If in_menu_border
	Then	putblockchar (c);
	Else	menuborder_on ();
		putblockchar (c);
		menuborder_off ();
	Fi
}


/***********************************************************************\
	putnstr () writes a string to screen at adjusted length
	putnstr_mark () does the same and marks given item (word)
\***********************************************************************/

#define dont_debug_item_marking

local char *
	putnstr_mark (s, l, mark_item, fill_line, selected)
	char * s;
	int l;
	int mark_item;
	FLAG fill_line;
	FLAG selected;
{
	int i = 0;
	char * spoi = s;
	int utfcount;
	unsigned long unichar;
	int index = 0;
	char marked = ' ';
	FLAG title_active = True;
	FLAG show_info = True;
	char * show_char = NIL_PTR;
#ifdef debug_item_marking
	printf ("putnstr <%s>\n", s);
#endif

	If mark_item == 0
	Then
#ifdef debug_item_marking
	printf ("border on @ %d\n", i);
#endif
		/*menuitem_on ();*/
		dim_on ();
		marked = 'b';
	Elsif mark_item > 0
	Then
#ifdef debug_item_marking
	printf ("header on @ %d\n", i);
#endif
		menuheader_on ();
		marked = 'h';
	Fi
	Dowhile i < l
	Do
		If * spoi == '\0'
		Then	If index == mark_item
			Then	/* end marked item */
#ifdef debug_item_marking
	printf ("end string @ %d\n", i);
#endif
				menuitem_off ();
				menuheader_on ();
				marked = 'h';
			Fi
			index ++;
			If fill_line
			Then
				If title_active
				Then	menuheader_off ();
					title_active = False;
					If use_graphic_borders
					&& horizontal_bar_width == 2
					&& (i & 1)
					Then
						putchar (' ');
					Fi
				Fi
				If use_graphic_borders
				Then	If horizontal_bar_width == 1
					|| ! (i & 1)
					Then	putblock ('q');
					Fi
				Else	menudisp_on ();
					putchar (' ');
				Fi
			Else	putchar (' ');
			Fi
			i ++;
		Elsif (* spoi & 0x80) != 0	/* UTF-8 character */
		Then
			utf8_info (spoi, & utfcount, & unichar);
			If index == mark_item && show_info && unichar >= 0x2E80
			Then
				show_char = spoi;
				show_info = False;
			Fi

			put_unichar (unichar);
			advance_utf8_scr (& spoi, & i, s);
			If selected && (cjk_term || mapped_term) &&
			   no_char (mappedtermchar (unichar))
			Then	/* restore screen attribute of selected item 
				   after highlighted replacement display */
				menuheader_on ();
			Fi
		Else	/* ASCII character */
			If * spoi == ' '
			Then	If index == mark_item
				Then	/* end marked item */
#ifdef debug_item_marking
	printf ("end marked item @ %d\n", i);
#endif
					menuitem_off ();
					menuheader_on ();
					marked = 'h';
				Fi
				index ++;
				putchar (* spoi ++);
				If index == mark_item
				Then	/* begin marked item */
#ifdef debug_item_marking
	printf ("begin marked item @ %d\n", i);
#endif
					menuheader_off ();
					/*menuitem_on ();*/
					dim_on ();
					marked = 'b';
				Fi
			Elsif marked == 'b' && * spoi == ':'
			Then	putchar (* spoi ++);
				dim_off ();
				menuitem_on ();
				marked = 'i';
			Else
				putchar (* spoi ++);
			Fi
			i ++;
		Fi
	Done
	If marked == 'h'
	Then
#ifdef debug_item_marking
	printf ("header off @ %d\n", i);
#endif
		menuheader_off ();
	Elsif marked == 'b' || marked == 'i'
	Then
#ifdef debug_item_marking
	printf ("border off @ %d\n", i);
#endif
		menuitem_off ();
		menuheader_off ();
	Fi

	return show_char;
}


local void
	putnstr (s, l)
	char * s;
	int l;
{
	(void) putnstr_mark (s, l, -2, False, False);
}


local void
	putnstr_sel (s, l, selected)
	char * s;
	int l;
	FLAG selected;
{
	(void) putnstr_mark (s, l, -2, False, selected);
}


local void
	putnstr_fill (s, l)
	char * s;
	int l;
{
	(void) putnstr_mark (s, l, -2, True, False);
}


/***********************************************************************\
	putborder_top/middle/bottom () writes menu borders
\***********************************************************************/
local void
	putborder_top (x, y, width, title, hook, scrolled)
	int x, y, width;
	char * title;
	FLAG hook;
	int scrolled;
{
	int i;

	set_cursor (x, y - 1);
	If title != NIL_PTR
	Then
		If use_graphic_borders
		Then	menuborder_on ();
			If scrolled
			Then	putblock ('f');
			Else	putblock ('l');
			Fi
			menuheader_on ();
			putnstr ("", menumargin);
		Else	menudisp_on ();
			putnstr ("", 1 + menumargin);
		Fi

		If standout_glitch && y == 0
		Then	For i = 0 While i < width - 2 - 2 * menumargin Step i ++
			Do	menuheader_on ();
				putchar (' ');
			Done
			set_cursor (x + 1 + menumargin, y - 1);
		Fi
		putnstr (title, width - 2 - 2 * menumargin);

		If use_graphic_borders
		Then	putnstr ("", menumargin);
			menuheader_off ();
			menuborder_on ();
			If scrolled
			Then	putblock ('f');
			Elsif hook
			Then	putblock ('k');	/* 'u' ? */
			Else	putblock ('k');
			Fi
		Else	putnstr ("", 1 + menumargin);
		Fi
	Else
		If use_graphic_borders
		Then	menuborder_on ();
			If scrolled
			Then	putblock ('f');
			Else	putblock ('l');
			Fi

			For i = 2 While i < width Step i += horizontal_bar_width
			Do	putblock ('q');
			Done

			If scrolled
			Then	putblock ('f');
			Else	putblock ('k');
			Fi
		Else	menudisp_on ();
			putnstr ("", width);
		Fi
	Fi
}

local void
	putborder_middle (x, y, width, subtitle)
	int x, y, width;
	char * subtitle;
{
	int i;

	set_cursor (x, y - 1);
	If subtitle != NIL_PTR && * subtitle != '\0'
	Then	If use_graphic_borders
		Then	putblock ('t');
			menuheader_on ();
		Else	putnstr ("", 1 + menumargin);
		Fi

		putnstr_fill (subtitle, width - 2);

		If use_graphic_borders
		Then	menuheader_off ();
			menuborder_on ();
			putblock ('u');
			menuborder_off ();
		Else	putnstr ("", 1 + menumargin);
		Fi
	Else
		If use_graphic_borders
		Then
			putblock ('t');

			For i = 2 While i < width Step i += horizontal_bar_width
			Do	putblock ('q');
				If horizontal_bar_width == 2
				Then	/* work-around for xterm font handling bug */
					menuborder_on ();
				Fi
			Done

			putblock ('u');
			menuborder_off ();
		Else	putnstr ("", width);
		Fi
	Fi
}

local void
	putborder_bottom (x, y, width, scrolled)
	int x, y, width;
	int scrolled;
{
	int i;

	set_cursor (x, y - 1);
	If use_graphic_borders
	Then
		If scrolled
		Then	putblock ('g');
		Else	putblock ('m');
		Fi

		For i = 2 While i < width Step i += horizontal_bar_width
		Do	putblock ('q');
			If horizontal_bar_width == 2
			Then	/* work-around for xterm font handling bug */
				menuborder_on ();
			Fi
		Done

		If scrolled
		Then	putblock ('g');
		Else	putblock ('j');
		Fi
		menuborder_off ();
	Else	putnstr ("", width);
		menudisp_off ();
	Fi
}


/***********************************************************************\
	displaymenuheader () displays a menu header
\***********************************************************************/
local void
	displaymenuheader (meni)
	int meni;
{
	set_cursor (meni * menu_width, -1);
	putchar (' ');
	menudisp_on ();
	putnstr ("", menumargin);
	putnstr (menus [meni].menuname, menu_width - 2 - 2 * menumargin);
	putnstr ("", menumargin);
	menudisp_off ();
	putchar (' ');
}


/***********************************************************************\
	number_menus () determines the number of pull-down menus
	set_scripttag () sets script tag into name of Keymap menu
\***********************************************************************/
local int
	number_menus ()
{
#ifdef full_Keymap_menu
	If allow_keymap
	Then	return arrlen (menus);
	Else	return arrlen (menus) - 1;
	Fi
#else
	return arrlen (menus);
#endif
}

#ifdef full_Keymap_menu
global void
	set_scripttag ()
{
	strcpy (keymap_menuname, keyboard_mapping);
	strcat (keymap_menuname, " Keymap");
}
#endif


/***********************************************************************\
	calcmenuvalues () determines menu screen positions
	width_of_menu () determines the display width of the menu
\***********************************************************************/
local int
	width_of_menu (menu, menulen, startcol, isflagmenu, minwidth)
	menuitemtype * menu; int menulen;
	int startcol;
	int isflagmenu;
	int minwidth;
{
	int i;
	menuitemtype * item;
	int itemlen;
	int width = minwidth;
	For i = 0 While i < menulen Step i ++
	Do
		item = & (menu [i]);
		If item->itemfu == separator && item->itemname != NIL_PTR
		Then	itemlen = utf8_col_count (item->itemname) - isflagmenu;
		Elsif hop_flag > 0 && item->hopitemname != NIL_PTR && item->hopitemname [0] != '\0'
		Then	itemlen = utf8_col_count (item->hopitemname);
		Else	itemlen = utf8_col_count (item->itemname);
		Fi
		itemlen += 2 + 2 * menumargin;
		If itemlen > width
		Then	width = itemlen;
		Fi
	Done
	width += isflagmenu;
	If width > XMAX
	Then	width = XMAX;
	Fi
	If startcol > 0 && startcol + width > XMAX - 1
	Then	width = XMAX + 1 - startcol;
	Fi
	If (width_data_version & cjk_width_data) && (width & 1)
	Then	width ++;
	Fi
	return width;
}


local void
	calcmenuvalues ()
{
	int flags_gap;

	If width_data_version & cjk_width_data
	Then	horizontal_bar_width = 2;
	Fi

	/* determine positions for menus and flags display */
	menu_width = (XMAX - arrlen (Flagmenu) - 4) / number_menus ();

	popupmenumargin = menumargin;

	If menu_width < 10
	Then	menumargin = 0;
	Fi

	/* determine popup menu width: */
	popupmenu_width = width_of_menu (Popupmenu, arrlen (Popupmenu), 0, 0, 0);

	If popupmenu_width < 15
	Then	popupmenumargin = 0;
	Fi

	/* determine position and length of displayed flags */
	flags_pos = XMAX - 2 - arrlen (Flagmenu);
	flags_gap = flags_pos - number_menus () * menu_width;
	flags_displayed = arrlen (Flagmenu);
	If flags_gap < 2
	Then	flags_pos += 2 - flags_gap;
		flags_displayed -= 2 - flags_gap;
	Fi
}


/***********************************************************************\
	displaymenuheaders () displays the menu headers
\***********************************************************************/
local void
	displaymenuheaders_from (firsti)
	int firsti;
{
	int i;
	int starti;

	If menuline_dirty
	Then	starti = 0;
		menuline_dirty = False;
	Else	starti = firsti;
	Fi

	calcmenuvalues ();

	If menu_width < 3
	Then	return;
	Fi

#ifdef full_Keymap_menu
	If number_menus () == arrlen (menus)
	Then	set_scripttag ();
	Fi
#endif

	If standout_glitch
	Then	set_cursor (starti * menu_width, -1);
		clear_eol ();
		disp_normal ();
	Fi

	For i = starti While i < number_menus () Step i ++
	Do
		displaymenuheader (i);
	Done
}

local void
	displaymenuheaders ()
{
	displaymenuheaders_from (0);
}


/***********************************************************************\
	cleargap () clears the gap between menu headers and flags
\***********************************************************************/
local void
	cleargap ()
{
	int i;
	int gappos = number_menus () * menu_width;

	set_cursor (gappos, -1);
	For i = gappos While i < flags_pos Step i ++
	Do
		putchar (' ');
	Done
}


/***********************************************************************\
	displayflags () displays the menu flags
\***********************************************************************/
global void
	displayflags ()
{
	int i;
	char * flags;
	FLAG is_reverse;

	calcmenuvalues ();

	If menu_width < 3
	Then	return;
	Fi

#ifdef full_Keymap_menu
	If number_menus () == arrlen (menus)
	Then	set_scripttag ();
		displaymenuheader (number_menus () - 1);
	Fi
#endif

	set_cursor (flags_pos, -1);
	For i = 0 While i < flags_displayed Step i ++
	Do
		flags = (* Flagmenu [i].dispflag) ();

		If * flags == '#'
		Then	is_reverse = True;
			flags ++;
			If * flags == '#'
			Then	flags ++;
				combiningdisp_on ();
			Else	menudisp_on ();
			Fi
		Else	is_reverse = False;
		Fi
		If (* flags & 0x80) != 0
		Then
			unsigned long unichar;
			int utflen;

			utf8_info (flags, & utflen, & unichar);
			If (cjk_term || mapped_term)
			    && (is_reverse || unichar == 0xE7)
			    && no_char (mappedtermchar (unichar)
			   )
			Then	If unichar == 0xAB	/* « */
				Then	putchar ('<');
				Elsif unichar == 0xBB	/* » */
				Then	putchar ('>');
				Elsif unichar == 0xA6	/* ¦ */
				Then	putchar ('|');
				Elsif unichar == 0xE7	/* ç */
				Then	putchar (';');
				Else	put_unichar (unichar);
				Fi
			Else	put_unichar (unichar);
			Fi
		Elsif * flags != 0
		Then	putchar (* flags);
		Fi
		If	is_reverse
		Then	disp_normal ();
		Fi
	Done
	flags_changed = False;
}


/***********************************************************************\
	displaymenuline () displays the menu line
\***********************************************************************/
local void
	displaymenuline_from (firsti)
	int firsti;
{
	displaymenuheaders_from (firsti);
	cleargap ();
	displayflags ();
	clear_eol ();
	top_line_scrolled = False;
}


global void
	displaymenuline ()
{
	displaymenuheaders ();
	cleargap ();
	displayflags ();
	clear_eol ();
	top_line_scrolled = False;
}


/***********************************************************************\
	prepare_menuline prepares menu line to prevent 
	right-to-left menu obstruction
\***********************************************************************/
local void
	prepare_menuline (column, line, menu_line_poi)
		int column; int line; LINE * * menu_line_poi;
{
	char * cp;
	int utfcount;
	unsigned long unichar;

	If standout_glitch
	Then	set_cursor (column, line - 1);
		clear_eol ();
		disp_normal ();
	Fi

	If * menu_line_poi == tail || * menu_line_poi == NIL_LINE
	Then	return;
	Fi

	If bidi_screen && utf8_text
	Then	For cp = (* menu_line_poi)->text
		While * cp != '\0'
		Step advance_utf8 (& cp)
		Do	utf8_info (cp, & utfcount, & unichar);
			If is_right_to_left (unichar)
			Then	break;
			Fi
		Done
		If * cp != '\0'
		Then	set_cursor (0, line - 1);
			clear_eol ();
		Fi
	Fi
	If * menu_line_poi == bot_line
	Then	* menu_line_poi = NIL_LINE;
	Else	* menu_line_poi = (* menu_line_poi)->next;
	Fi
}




/***********************************************************************\
	pulldown_menu () displays a pulldown menu
\***********************************************************************/
local int
	pulldown_menu (meni)
	int meni;
{
	int i;
	int menulen = menus [meni].menulen;
	int meniwidth;
	int menuxpos = meni * menu_width;
	menuitemtype * item;
	LINE * menu_line;

	menu_mouse_mode (True);

	/* save redraw values: */
	last_pulldown_menu = meni;
	last_pulldown_item = -1;

	If menulen > YMAX - 1
	Then menulen = YMAX - 1;
	Fi

	/* determine menu width: */
	meniwidth = width_of_menu (menus [meni].menuitems, menulen, menuxpos, 0, 0);

	/* top menu border, with menu name: */
	putborder_top (menuxpos, 0, meniwidth, menus [meni].menuname, False, False);

	/* text lines may have to be cleared: */
	menu_line = top_line;

	/* menu items: */
	For i = 0 While i < menulen Step i ++
	Do
	    prepare_menuline (menuxpos, 1 + i, & menu_line);
	    item = & (menus [meni].menuitems [i]);
	    If item->itemfu == separator
	    Then
		putborder_middle (menuxpos, 1 + i, meniwidth, item->itemname);
	    Else
		set_cursor (menuxpos, i);
		If use_graphic_borders
		Then	putblock ('x');
			menuborder_off ();
		Else	putchar (' ');
			menudisp_off ();
		Fi
		putnstr ("", menumargin);

		If hop_flag > 0 && item->hopitemname != NIL_PTR && item->hopitemname [0] != '\0'
		Then	putnstr (item->hopitemname, meniwidth - 2 - 2 * menumargin);
		Else	putnstr (item->itemname, meniwidth - 2 - 2 * menumargin);
		Fi

		putnstr ("", menumargin);
		If use_graphic_borders
		Then	menuborder_on ();
			putblock ('x');
		Else	menudisp_on ();
			putchar (' ');
		Fi
	    Fi
	Done

	/* bottom menu border: */
	prepare_menuline (menuxpos, 1 + menulen, & menu_line);
	putborder_bottom (menuxpos, 1 + menulen, meniwidth, False);

	set_cursor (menuxpos, -1);
	If menulen > last_dirty_line
	Then	last_dirty_line = menulen;
	Fi
	pulldownmenu_displayed = True;
	pulldownmenu_active = True;

	return meniwidth;
}


/***********************************************************************\
	display_menu () displays the given menu, returns menu width
\***********************************************************************/
local int
	display_menu (menu, menulen, column, line, title, isflagmenu, disp_only, minwidth, scroll, maxscroll)
		menuitemtype * menu; int menulen;
		int column; int line;
		char * title;
		int isflagmenu;
		FLAG disp_only;
		int minwidth;
		int scroll, maxscroll;
{
	int i;
	menuitemtype * item;
	int menu_width = width_of_menu (menu, menulen, 0, isflagmenu, minwidth);
	LINE * menu_line;

	menu_mouse_mode (True);

	/* save redraw values: */
	last_popup_column = column;
	last_popup_line = line;
	last_popup_item = -1;
	last_is_flag_menu = isflagmenu;
	last_minwidth = minwidth;
	last_scroll = scroll;
	last_maxscroll = maxscroll;

	/* text lines may have to be cleared: */
	menu_line = proceed (top_line, line - 1);

	/* top menu border: */
	prepare_menuline (column, line, & menu_line);
	putborder_top (column, line, menu_width, title, True, scroll > 0);

	/* menu items: */
	For i = 0 While i < menulen Step i ++
	Do
	    prepare_menuline (column, line + 1 + i, & menu_line);
	    item = & (menu [i]);
	    If item->itemfu == separator
	    Then
		putborder_middle (column, line + 1 + i, menu_width, item->itemname);
	    Else
		set_cursor (column, line + i);
		If use_graphic_borders
		Then	putblock ('x');
			menuborder_off ();
		Else	putchar (' ');
			menudisp_off ();
		Fi
		putnstr ("", popupmenumargin);

		If isflagmenu
		Then	If (* item->itemon) (item, i)
			Then	unidisp_on ();
				If utf8_screen
				Then	put_unichar (menu_marker);
				Else	putchar ('*');
				Fi
				unidisp_off ();
			Else	putchar (' ');
			Fi
		Fi

		If hop_flag > 0 && item->hopitemname != NIL_PTR && item->hopitemname [0] != '\0'
		Then	putnstr (item->hopitemname, menu_width - isflagmenu - 2 - 2 * popupmenumargin);
		Else	putnstr (item->itemname, menu_width - isflagmenu - 2 - 2 * popupmenumargin);
		Fi

		putnstr ("", popupmenumargin);
		If use_graphic_borders
		Then	menuborder_on ();
			putblock ('x');
		Else	menudisp_on ();
			putchar (' ');
		Fi
	    Fi
	Done

	/* bottom menu border: */
	prepare_menuline (column, line + 1 + menulen, & menu_line);
	putborder_bottom (column, line + 1 + menulen, menu_width, scroll < maxscroll);

	If line > 0 && title != NIL_PTR
	Then	/* flag menu */
		set_cursor (column + menu_width - 2 + horizontal_bar_width, line - 1);
	Else	set_cursor (column, line - 1);
	Fi
	If first_dirty_line < 0 || line - 1 < first_dirty_line
	Then	first_dirty_line = line - 1;
	Fi
	If line - 1 + menulen + 2 > last_dirty_line
	Then	last_dirty_line = line - 1 + menulen + 2;
	Fi
	last_menu = menu;
	last_menulen = menulen;
	last_menuwidth = menu_width;
	last_disp_only = disp_only;
	last_menutitle = title;
	popupmenu_displayed = True;
	popupmenu_active = True;

	return menu_width;
}


/***********************************************************************\
	menselected () highlights a selected pulldown menu item
\***********************************************************************/
local void
	menselected (selected, meni, itemno)
		FLAG selected; int meni; int itemno;
{
	menuitemtype * item;
	int meniwidth;
	int menulen = menus [meni].menulen;
	int menuxpos = meni * menu_width;

	If itemno < 0
	Then	return;
	Fi

	/* save redraw values: */
	If selected
	Then	last_pulldown_item = itemno;
	Else	last_pulldown_item = -1;
	Fi

	/* determine menu width: */
	meniwidth = width_of_menu (menus [meni].menuitems, menulen, menuxpos, 0, 0);

	set_cursor (menuxpos, itemno);
	/* draw left border element */
	If selected && use_unicode_menubar ()
	Then	reverse_on ();
		If menu_border_style == 'f'
		Then	put_unichar (0x258D);	/* ▍ LEFT THREE EIGHTHS BLOCK */
		Else	put_unichar (0x258D);	/* ▍ LEFT THREE EIGHTHS BLOCK */
		Fi
	Elsif use_graphic_borders
	Then	putblock ('x');
	Else	menudisp_on ();
		putchar (' ');
		menudisp_off ();
	Fi
	/* switch to menu item content */
	If selected
	Then	menuheader_on ();
	Else	If standout_glitch
		Then	menuheader_off ();
		Fi
	Fi
	putnstr ("", menumargin);

	item = & (menus [meni].menuitems [itemno]);
	If hop_flag > 0 && item->hopitemname != NIL_PTR && item->hopitemname [0] != '\0'
	Then	putnstr_sel (item->hopitemname, meniwidth - 2 - 2 * menumargin, selected);
	Else	putnstr_sel (item->itemname, meniwidth - 2 - 2 * menumargin, selected);
	Fi

	putnstr ("", menumargin);
	/* switch to border */
	If selected
	Then	menuheader_off ();
	Fi
	/* draw right border element */
	If selected && use_unicode_menubar ()
	Then
		If menu_border_style == 'f'
		Then	put_unichar (0x258B);	/* ▋ LEFT FIVE EIGHTHS BLOCK */
		Else
#ifdef half_block_right
			put_unichar (0x258C);	/* ▌ LEFT HALF BLOCK */
#else
			put_unichar (0x258B);	/* ▋ LEFT FIVE EIGHTHS BLOCK */
#endif
		Fi
	Elsif use_graphic_borders
	Then	putblock ('x');
	Else	menudisp_on ();
		putchar (' ');
		menudisp_off ();
	Fi
}


/***********************************************************************\
	popupmenselected () highlights a selected popup menu item
\***********************************************************************/
local void
	popupmenselected (menu, menu_width, selected, column, line, i, index, isflagmenu)
		menuitemtype * menu; int menu_width; FLAG selected;
		int column; int line; int i; int index;
		int isflagmenu;
{
	menuitemtype * item;
	FLAG show_info = False;
	char * show_char;

	If i < 0
	Then	return;
	Fi

	/* save redraw values: */
	If selected
	Then	last_popup_item = i;
		last_popup_index = index;
	Else	last_popup_item = -1;
	Fi

	set_cursor (column, line + i);
	/* draw left border element */
	If selected && use_unicode_menubar ()
	Then	reverse_on ();
		put_unichar (0x258D);	/* ▍ LEFT THREE EIGHTHS BLOCK */
	Elsif use_graphic_borders
	Then	putblock ('x');
	Else	menudisp_on ();
		putchar (' ');
		menudisp_off ();
	Fi
	/* switch to menu item content */
	If selected && index < 0
	Then	menuheader_on ();
	Else	If standout_glitch
		Then	menuheader_off ();
		Fi
	Fi
	putnstr ("", popupmenumargin);

	item = & (menu [i]);

	If isflagmenu
	Then	If (* item->itemon) (item, i)
		Then	unidisp_on ();
			If utf8_screen
			Then	put_unichar (menu_marker);
			Else	putchar ('*');
			Fi
			unidisp_off ();
			If selected && index < 0
			Then	menuheader_on ();
			Fi
		Else	putchar (' ');
		Fi
	Fi

	If selected && index >= 0
	Then	show_char = putnstr_mark (item->itemname, menu_width - isflagmenu - 2 - 2 * popupmenumargin, index, False, selected);
		show_info = True;
	Elsif hop_flag > 0 && item->hopitemname != NIL_PTR && item->hopitemname [0] != '\0'
	Then	putnstr_sel (item->hopitemname, menu_width - isflagmenu - 2 - 2 * popupmenumargin, selected);
	Else	putnstr_sel (item->itemname, menu_width - isflagmenu - 2 - 2 * popupmenumargin, selected);
	Fi

	putnstr ("", popupmenumargin);
	/* switch to border */
	If selected && index < 0
	Then	menuheader_off ();
	Fi
	/* draw right border element */
	If selected && use_unicode_menubar ()
	Then	put_unichar (0x258B);	/* ▋ LEFT FIVE EIGHTHS BLOCK */
	Elsif use_graphic_borders
	Then	putblock ('x');
	Else	menudisp_on ();
		putchar (' ');
		menudisp_off ();
	Fi

	If show_info
	Then	clear_status ();
		If show_char != NIL_PTR
		Then	display_Han (show_char, True);
		Fi
	Fi
}


/***********************************************************************\
	clean_menus () wipes menus which are still displayed
	clear_menus () clears menus which are still displayed
\***********************************************************************/
global void
	clean_menus ()
{
	menu_mouse_mode (False);
	If pulldownmenu_displayed
	Then
		pulldownmenu_displayed = False;
		display (0, top_line, last_dirty_line, last_dirty_line);
		last_dirty_line = 0;
		set_cursor_xy ();
	Fi
	If popupmenu_displayed
	Then
		LINE * start_line = proceed (top_line, first_dirty_line);

		int ypos = y;
		If ypos < first_dirty_line
		Then	/* Han info menu */
			ypos = first_dirty_line;
		Elsif ypos > first_dirty_line + 1 + last_menulen
		Then	/* limit ypos to lower menu border as until 2000.11 */
			ypos = first_dirty_line + 1 + last_menulen;
		Fi

		popupmenu_displayed = False;
		display (first_dirty_line, start_line, 
			 last_dirty_line - 1 - first_dirty_line, ypos);
		first_dirty_line = -1;
		last_dirty_line = 0;
		set_cursor_xy ();
	Fi
}

local void
	clear_menus ()
{
	clean_menus ();
	pulldownmenu_active = False;
	popupmenu_active = False;
}


/***********************************************************************\
	redrawmenu () redraws an open menu after screen refresh
\***********************************************************************/
global void
	redrawmenu ()
{
	If pulldownmenu_active
	Then
		int item = last_pulldown_item;
		pulldown_menu (last_pulldown_menu);
		If item >= 0
		Then	menselected (True, last_pulldown_menu, item);
		Fi
	Fi
	If popupmenu_active
	Then
		int item = last_popup_item;
		display_menu (last_menu, last_menulen, last_popup_column, last_popup_line, last_menutitle, last_is_flag_menu, last_disp_only, last_minwidth, last_scroll, last_maxscroll);
		If item >= 0
		Then	popupmenselected (last_menu, last_menuwidth, True, 
					last_popup_column, last_popup_line, 
					item, last_popup_index, last_is_flag_menu);
		Fi
		If last_disp_only
		Then	menu_mouse_mode (False);
			set_cursor_xy ();
		Fi
	Fi
}


/***********************************************************************\
	is_menu_open () checks if a menu is open
\***********************************************************************/
global int
	is_menu_open ()
{
	return pulldownmenu_active || popupmenu_active;
}


/***********************************************************************\
	select_item () selects a menu item based on key letter
\***********************************************************************/
local int
	select_item (menu, menulen, curitemno, key)
		menuitemtype * menu;
		int menulen;
		int curitemno;
		unsigned long key;
{
	int i = curitemno + 1;
	char * label;

	key |= 0x20;	/* normalise to small letter */

	Dowhile True
	Do
		If i > menulen
		Then	i = 1;
			If curitemno == 0
			Then	return 1;
			Fi
		Fi
		If i == curitemno
		Then	return curitemno;
		Fi
		If menu [i - 1].itemfu != separator
		Then
			If hop_flag > 0
			Then	label = menu [i - 1].hopitemname;
				If * label == '\0'
				Then	label = menu [i - 1].itemname;
				Fi
			Else	label = menu [i - 1].itemname;
			Fi
			If ((character) * label | 0x20) == key
			Then	return i;
			Fi
		Fi
		i ++;
	Done
	/*return curitemno;*/
}


/***********************************************************************\
	item_count () counts menu items in a menu line string
\***********************************************************************/
local int
	item_count (line)
		character * line;
{
	character prev = (character) ' ';
	int count = 0;
	Dowhile * line != '\0'
	Do	If (character) * line > (character) ' ' && prev == ' '
		Then	count ++;
		Fi
		prev = * line;
		If prev == (character) '\t'
		Then	prev = (character) ' ';
		Fi
		line ++;
	Done
	return count;
}


/***********************************************************************\
	QUICKMEN () handles right mouse button menu
	action_menu () handles given action menu
	popup_menu () handles given action or selection menu
	menu_scroll () moves menu item and, if necessary,
			scrolls a partially displayed menu
\***********************************************************************/
#define dont_debug_menu_scroll

local FLAG
	menu_scroll (menu, menulen, delta, fullmenu, scrolloffset, maxscrolloffset, itemno)
		menuitemtype * * menu;
		int menulen;
		int delta;
		menuitemtype * fullmenu;
		int * scrolloffset;
		int maxscrolloffset;
		int * itemno;
{
	int prev_scrolloffset = * scrolloffset;

	If delta == 1
	Then	* itemno += delta;
		If * itemno > menulen
		Then	If * scrolloffset == maxscrolloffset
			Then	/* cycle at end */
				* scrolloffset = 0;
				* itemno = 1;
			Else	/* scroll 1 item */
				* scrolloffset += delta;
				* itemno -= delta;
			Fi
		Fi
		If fullmenu [* scrolloffset + * itemno - 1].itemfu == separator
		Then	* itemno += delta;
			If * itemno > menulen
			Then	* scrolloffset += delta;
				* itemno -= delta;
			Fi
		Fi
	Elsif delta == -1
	Then	* itemno += delta;
		If * itemno < 1
		Then	If * scrolloffset == 0
			Then	/* cycle at end */
				* scrolloffset = maxscrolloffset;
				* itemno = menulen;
			Else	/* scroll 1 item */
				* scrolloffset += delta;
				* itemno -= delta;
			Fi
		Fi
		If fullmenu [* scrolloffset + * itemno - 1].itemfu == separator
		Then	* itemno += delta;
			If * itemno < 1
			Then	* scrolloffset += delta;
				* itemno -= delta;
			Fi
		Fi
	Elsif delta > 0
	Then	* itemno += delta;
#ifdef debug_menu_scroll
	printf ("   itemno %d + %d\n", * itemno, * scrolloffset);
#endif
		Dowhile * itemno > menulen
		Do	* scrolloffset += menulen;
			* itemno -= menulen;
#ifdef debug_menu_scroll
	printf ("-> itemno %d + %d\n", * itemno, * scrolloffset);
#endif
		Done

		If * scrolloffset > maxscrolloffset
		Then	* itemno += * scrolloffset - maxscrolloffset;
			* scrolloffset = maxscrolloffset;
#ifdef debug_menu_scroll
	printf ("=> itemno %d + %d\n", * itemno, * scrolloffset);
#endif
			If * scrolloffset == prev_scrolloffset
			Then	/* paging at end: move to menu end item */
				* itemno = menulen;
#ifdef debug_menu_scroll
	printf ("=> itemno %d + %d\n", * itemno, * scrolloffset);
#endif
			Fi
			If * itemno > menulen
			Then	* itemno = menulen;
#ifdef debug_menu_scroll
	printf ("=> itemno %d + %d\n", * itemno, * scrolloffset);
#endif
			Fi
		Fi

		If fullmenu [* scrolloffset + * itemno - 1].itemfu == separator
		Then	If * itemno < menulen
			Then	* itemno += 1;
			Else	* itemno -= 1;
			Fi
#ifdef debug_menu_scroll
	printf ("!> itemno %d + %d\n", * itemno, * scrolloffset);
#endif
		Fi
	Else	* itemno += delta;
		Dowhile * itemno < 1
		Do	* scrolloffset -= menulen;
			* itemno += menulen;
		Done

		If * scrolloffset < 0
		Then	* itemno += * scrolloffset;
			* scrolloffset = 0;
			If * scrolloffset == prev_scrolloffset
			Then	/* paging at end: move to menu end item */
				* itemno = 1;
			Fi
			If * itemno < 1
			Then	* itemno = 1;
			Fi
		Fi

		If fullmenu [* scrolloffset + * itemno - 1].itemfu == separator
		Then	If * itemno > 1
			Then	* itemno -= 1;
			Else	* itemno += 1;
			Fi
		Fi
	Fi

	If * scrolloffset == prev_scrolloffset
	Then	return False;
	Else	* menu = & fullmenu [* scrolloffset];
		return True;
	Fi
}


/* popup_menu token to ignore 1 (initial) mouse release event */
static FLAG ignore_1releasebutton = False;


global int
	popup_menu (menu, menulen, column, line, title, is_flag_menu, disp_only, select_keys)
		menuitemtype * menu; int menulen;
		int column; int line;
		char * title;
		FLAG is_flag_menu;
		FLAG disp_only;	/* Han info menu */
		char * select_keys;	/* pick list */
{
	menuitemtype * fullmenu = menu;
	int fullmenulen = menulen;
	int scrolloffset = 0;
	int maxscrolloffset = 0;

	unsigned long c;
	int startline;
	int startcolumn;
	int itemno;
	int ret = -1;
	char * cpoi;
	char * select;
	int scol;
	int select_index = -2;
	int sticky_index = -2;
	int minwidth = disp_only ? utf8_col_count (title) + 2 : 0;
	int menu_width;
	FLAG ignore_releasebutton;

	calcmenuvalues ();

	/* set token to ignore 1 mouse release event */
	ignore_releasebutton = ignore_1releasebutton;
	/* set token to ignore initial mouse release event */
	ignore_releasebutton = True;
	/* clear token to ignore 1 mouse release event */
	ignore_1releasebutton = False;

	If line < 0
	Then	line = 0;
		is_flag_menu = True;
	Fi

	menu_width = width_of_menu (menu, menulen, 0, is_flag_menu, minwidth);

	/* adjust menu position and check with screen limits */
	If column >= 0
	Then	startcolumn = column;
	Else	startcolumn = 4 - column - menu_width - 2 * horizontal_bar_width;
	Fi
	If startcolumn > XMAX - menu_width - 2 * horizontal_bar_width + 2
	Then	startcolumn = XMAX - menu_width - 2 * horizontal_bar_width + 2;
	Fi
	If startcolumn < 0 || menu_width < 3
	Then	ring_bell ();
		return ret;
	Fi

	startline = line + 1;
	If startline > YMAX - menulen - 1
	Then	startline = YMAX - menulen - 1;
	Fi
	/* setup partial menu values for menu scrolling */
	If startline < 1
	Then	maxscrolloffset = 1 - startline;
		menulen = menulen - maxscrolloffset;
		startline = 1;
	Fi

	/* initial display */
	menu_width = display_menu (menu, menulen, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);

	/* initial selection */
	If select_keys == NIL_PTR
	Then	itemno = 0;
	Else	itemno = 1;
		select_index = 0;
		sticky_index = 0;
		popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
	Fi

	flush ();

	If disp_only
	Then	menu_mouse_mode (False);
		return ret;
	Fi

	c = readcharacter_allbuttons ();
	Dowhile command (c) == MDN || command (c) == MUP
		|| command (c) == PD || command (c) == PU
		|| command (c) == HOMEkey || command (c) == smallHOMEkey
		|| command (c) == ENDkey || command (c) == smallENDkey
		|| (c == ' '
			&& (select_keys == NIL_PTR
			    || selection_space != SPACE_SELECT))
		|| command (c) == HOP || command (c) == GOTO
		|| (command (c) == MOUSEescape
			&& mouse_button != leftbutton
			&& (mouse_button != releasebutton
			    || select_keys != NIL_PTR
			    || ! (mouse_prevbutton == leftbutton 
			          || mouse_prevbutton == rightbutton
			         )
			    || ! (
				  mouse_xpos > startcolumn
				   && mouse_xpos < startcolumn + menu_width
				  && mouse_ypos >= startline
				   && mouse_ypos < startline + menulen
			         )
			    || ignore_releasebutton
			   )
		   )
		|| (select_keys != NIL_PTR
			&& (c == '<' || c == '>'
			   || command (c) == MLF || command (c) == MRT
			   )
		   )
		|| (c != FUNcmd && c > (character) ' ' && select_keys == NIL_PTR)
	Do
	    If (c == ' ' && select_keys == NIL_PTR)
		|| command (c) == HOP
		|| command (c) == GOTO
		|| (command (c) == MOUSEescape && mouse_button == middlebutton)
	    Then
		toggle_HOP ();
		clear_menus ();
		/* recalculate x pos to prevent menu display wrap-around */
		menu_width = width_of_menu (menu, menulen, 0, is_flag_menu, 0);
		If column >= 0
		Then	startcolumn = column;
		Else	startcolumn = 4 - column - menu_width - 2 * horizontal_bar_width;
		Fi
		If startcolumn > XMAX - menu_width - 2 * horizontal_bar_width + 2
		Then	startcolumn = XMAX - menu_width - 2 * horizontal_bar_width + 2;
		Fi
		If startline < 1 || startcolumn < 0 || menu_width < 3
		Then	ring_bell ();
			return ret;
		Fi
		/* display the menu */
		menu_width = display_menu (menu, menulen, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
		popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
	    Elsif command (c) == MOUSEescape
			&& mouse_button != wheelup
			&& mouse_button != wheeldown
			&& (mouse_button != releasebutton
			    || select_keys == NIL_PTR)
	    Then
	      If mouse_button == releasebutton && ignore_releasebutton
	      Then
		ignore_releasebutton = False;
	      Else
		popupmenselected (menu, menu_width, False, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
		itemno = mouse_ypos - startline + 1;
		If itemno <= 0 || itemno > menulen
		|| menu [itemno - 1].itemfu == separator
		Then	itemno = 0;
		Else	popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
		Fi
	      Fi
	    Elsif select_keys != NIL_PTR && (command (c) == MLF || c == '<')
	    Then
		If select_index > 0
		Then	select_index --;
		Else	int prev_itemno = itemno;
			If menu_scroll (& menu, menulen, - 1, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
			Then	menu_width = display_menu (menu, menulen, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
			Else	popupmenselected (menu, menu_width, False, startcolumn, startline, prev_itemno - 1, select_index, is_flag_menu);
			Fi

			select_index = item_count (menu [itemno - 1].itemname) - 1;
		Fi
		sticky_index = select_index;
		popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
	    Elsif select_keys != NIL_PTR
		  && (command (c) == MRT || c == '>'
		      || (c == ' ' && selection_space == SPACE_NEXT))
	    Then
		If select_index < item_count (menu [itemno - 1].itemname) - 1
		Then	select_index ++;
		Else	int prev_itemno = itemno;
			If menu_scroll (& menu, menulen, 1, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
			Then	menu_width = display_menu (menu, menulen, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
			Else	popupmenselected (menu, menu_width, False, startcolumn, startline, prev_itemno - 1, select_index, is_flag_menu);
			Fi

			select_index = 0;
		Fi
		sticky_index = select_index;
		popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
	    Elsif select_keys != NIL_PTR &&
		  (command (c) == HOMEkey || command (c) == smallHOMEkey)
	    Then
		select_index = 0;
		sticky_index = 0;
		popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
	    Elsif select_keys != NIL_PTR &&
		  (command (c) == ENDkey || command (c) == smallENDkey)
	    Then
		select_index = item_count (menu [itemno - 1].itemname) - 1;
		sticky_index = strlen (select_keys) - 1;
		popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
	    Elsif command (c) == HOMEkey || command (c) == smallHOMEkey
	    Then
		int prev_itemno = itemno;
		If menu_scroll (& menu, menulen, - scrolloffset - menulen, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
		Then	menu_width = display_menu (menu, menulen, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
		Else	popupmenselected (menu, menu_width, False, startcolumn, startline, prev_itemno - 1, select_index, is_flag_menu);
			itemno = 1;
		Fi
		popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
	    Elsif command (c) == ENDkey || command (c) == smallENDkey
	    Then
		int prev_itemno = itemno;
		If menu_scroll (& menu, menulen, maxscrolloffset + menulen, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
		Then	menu_width = display_menu (menu, menulen, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
		Else	popupmenselected (menu, menu_width, False, startcolumn, startline, prev_itemno - 1, select_index, is_flag_menu);
			itemno = menulen;
		Fi
		popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
	    Elsif command (c) == PD
	    Then
		int prev_itemno = itemno;
		If menu_scroll (& menu, menulen, menulen, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
		Then	menu_width = display_menu (menu, menulen, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
		Else	popupmenselected (menu, menu_width, False, startcolumn, startline, prev_itemno - 1, select_index, is_flag_menu);
		Fi

		If select_keys != NIL_PTR
		Then	select_index = item_count (menu [itemno - 1].itemname) - 1;
			If sticky_index < select_index
			Then	select_index = sticky_index;
			Fi
		Fi

		popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
	    Elsif command (c) == PU
	    Then
		int prev_itemno = itemno;
		If menu_scroll (& menu, menulen, - menulen, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
		Then	menu_width = display_menu (menu, menulen, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
		Else	popupmenselected (menu, menu_width, False, startcolumn, startline, prev_itemno - 1, select_index, is_flag_menu);
		Fi

		If select_keys != NIL_PTR
		Then	select_index = item_count (menu [itemno - 1].itemname) - 1;
			If sticky_index < select_index
			Then	select_index = sticky_index;
			Fi
		Fi

		popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
	    Else
		If command (c) == MDN
		   || (c == ' ' && selection_space == SPACE_NEXTROW)
#ifdef menu_wheel
		   || (command (c) == MOUSEescape && mouse_button == wheeldown)
#endif
		Then
			int prev_itemno = itemno;
			If menu_scroll (& menu, menulen, 1, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
			Then	menu_width = display_menu (menu, menulen, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
			Else	popupmenselected (menu, menu_width, False, startcolumn, startline, prev_itemno - 1, select_index, is_flag_menu);
			Fi

			If select_keys != NIL_PTR
			Then	select_index = item_count (menu [itemno - 1].itemname) - 1;
				If sticky_index < select_index
				Then	select_index = sticky_index;
				Fi
			Fi
			popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
		Elsif command (c) == MUP
#ifdef menu_wheel
		      || (command (c) == MOUSEescape && mouse_button == wheelup)
#endif
		Then
			int prev_itemno = itemno;
			If menu_scroll (& menu, menulen, - 1, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
			Then	menu_width = display_menu (menu, menulen, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
			Else	popupmenselected (menu, menu_width, False, startcolumn, startline, prev_itemno - 1, select_index, is_flag_menu);
			Fi

			If select_keys != NIL_PTR
			Then	select_index = item_count (menu [itemno - 1].itemname) - 1;
				If sticky_index < select_index
				Then	select_index = sticky_index;
				Fi
			Fi
			popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
		Elsif select_keys == NIL_PTR
		Then	/*
			popupmenselected (menu, menu_width, False, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
			itemno = select_item (menu, menulen, itemno, c);
			*/
			int prev_itemno = itemno;
			int new_itemno = select_item (fullmenu, fullmenulen, itemno + scrolloffset, c);
			new_itemno -= scrolloffset;
			If menu_scroll (& menu, menulen, new_itemno - prev_itemno, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
			Then	menu_width = display_menu (menu, menulen, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
			Else	popupmenselected (menu, menu_width, False, startcolumn, startline, prev_itemno - 1, select_index, is_flag_menu);
			Fi

			popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
		Fi
	    Fi

	    If command (c) == MOUSEescape
	    Then	ignore_releasebutton = False;
	    Fi

	    flush ();
	    c = readcharacter_allbuttons ();
	Done
	If command (c) == MOUSEescape
	Then	/* mouse click */
		mouse_xpos -= startcolumn;
		mouse_ypos -= startline;
		If mouse_xpos > 0 && mouse_xpos < menu_width
		&& mouse_ypos >= 0 && mouse_ypos < menulen
		Then
		    If select_keys != NIL_PTR
		    Then
			/* determine mouse position in menu line */
			/* used for keyboard mapping selection */
			select = menu [mouse_ypos].itemname;
			cpoi = select;
			ret = 0;
			scol = 1;
			Dowhile scol < mouse_xpos && * cpoi != '\0'
			Do	If * cpoi == ' '
				Then	ret ++;
				Fi
				advance_utf8_scr (& cpoi, & scol, select);
			Done
			If ret < strlen (select_keys)
			Then	ret = (scrolloffset + mouse_ypos)
					* strlen (select_keys)
					+ ret;
			Else	ret = -1;
			Fi
		    Elsif menu [mouse_ypos].itemfu != separator
		    Then
			popupmenselected (menu, menu_width, True, startcolumn, startline, mouse_ypos, select_index, is_flag_menu);
			menu_mouse_mode (False);
			(* menu [mouse_ypos].itemfu) (menu, mouse_ypos);
			displayflags ();
		    Fi
		Elsif mouse_ypos + startline == -1
		Then	clear_menus ();
			displaymenuheaders ();
			MINMEN (mouse_xpos + startcolumn, 0);
		Fi
	Elsif (c == '\n' || command (c) == SNL 
		|| (c == ' ' && select_keys != NIL_PTR
			     && selection_space == SPACE_SELECT)
		) && itemno > 0
	Then
		menu_mouse_mode (False);
		If select_keys != NIL_PTR
		Then	ret = (scrolloffset + itemno - 1)
				* strlen (select_keys)
				+ select_index;
		Else	(* menu [itemno - 1].itemfu) (menu, itemno - 1);
			displayflags ();
		Fi
	Elsif select_keys != NIL_PTR
	Then
		cpoi = strchr (select_keys, c);
		If cpoi != NIL_PTR && * cpoi != '\0'
		Then	ret = (scrolloffset + itemno - 1)
				* strlen (select_keys)
				+ (cpoi - select_keys);
		Fi
	Elsif command (c) == MLF && input_active == False
	Then	clear_menus ();
		prev_flag_menu (fullmenu);
	Elsif command (c) == MRT && input_active == False
	Then	clear_menus ();
		next_flag_menu (fullmenu);
	Fi

	clear_menus ();

	If select_keys != NIL_PTR
	Then	clear_status ();
	Fi

	return ret;
}


local void
	action_menu (menu, menulen, column, line, title, is_flag_menu)
		menuitemtype * menu; int menulen;
		int column; int line;
		char * title;
		FLAG is_flag_menu;
{
	clear_menus ();
	(void) popup_menu (menu, menulen, column, line, title, is_flag_menu, False, NIL_PTR);
}


global void
	QUICKMEN (column, line)
	int column;
	int line;
{
	action_menu (Popupmenu, arrlen (Popupmenu), column, line, NIL_PTR, False);
}


/***********************************************************************\
	MINMEN (screen_column, itemno) handles menu dialog or flag toggling
		if itemno > 0, the item is pre-selected
	openmenu (i) opens the i-th menu
	openmenuat (c) opens the menu at screen column c
\***********************************************************************/
global void
	openmenu (i)
	int i;
{
	MINMEN (i * menu_width, 0);
}

global void
	openmenuat (c)
	int c;
{
	If mouse_button == middlebutton && hop_flag == 0
	Then	toggle_HOP ();
	Fi

	MINMEN (c, 0);
}

local void
	MINMEN (column, itemno)
	int column;
	int itemno;
{
	int meni;
	int menulen;
	unsigned long c;
	int popmenwidth;	/* width of the menu, determined dynamically */

	calcmenuvalues ();

	If menu_width < 3
	Then	return;
	Fi
	meni = column / menu_width;
	If meni < 0 || meni >= number_menus ()
	Then	/* check if it's a flag toggle position */
		meni = column - flags_pos;
		If meni >= 0 && meni < flags_displayed
		Then
			If mouse_button == rightbutton
			&& Flagmenu [meni].menutitle != NIL_PTR
			Then	If * ((* Flagmenu [meni].dispflag) ()) != ' '
				Then	action_menu (
						Flagmenu [meni].menu, 
						Flagmenu [meni].menulen, 
						- (column - 1), 
						-1, 
						Flagmenu [meni].menutitle, 
						False);
				Fi
			Else	(* Flagmenu [meni].toggle) (column);
			Fi
			displayflags ();
			set_cursor_xy ();
		Fi
		return;
	Fi


	/* Now handle the pull down menu */

	/* adjust menu length to screen size */
	menulen = menus [meni].menulen;
	If menulen > YMAX - 1
	Then menulen = YMAX - 1;
	Fi

	popmenwidth = pulldown_menu (meni);
	If itemno > 0
	Then	menselected (True, meni, itemno - 1);
	Fi

	flush ();
	c = readcharacter_allbuttons ();
	Dowhile command (c) == MDN || command (c) == MUP
		|| (command (c) == MOUSEescape
			&& mouse_button != leftbutton
			&& (mouse_button != releasebutton ||
			    mouse_ypos < 0 ||
			    ! (mouse_prevbutton == leftbutton 
			       || mouse_prevbutton == rightbutton
			      )
			   )
			&& mouse_button != middlebutton
			&& (mouse_button != rightbutton ||
			    mouse_ypos >= 0 || mouse_xpos / menu_width == meni)
			&& (mouse_button != movebutton ||
			    mouse_ypos >= 0 || mouse_xpos / menu_width == meni)
		   )
		|| (c != FUNcmd && c > (character) ' ')
	Do
	    If command (c) == MOUSEescape && mouse_button != wheelup && mouse_button != wheeldown
	    Then
		If itemno > 0
		Then	menselected (False, meni, itemno - 1);
		Fi
		itemno = mouse_ypos + 1;
		If itemno <= 0 || itemno > menulen
		|| menus [meni].menuitems [itemno - 1].itemfu == separator
		Then
			itemno = 0;
		Else
			menselected (True, meni, itemno - 1);
		Fi
	    Else
		If itemno > 0
		Then	menselected (False, meni, itemno - 1);
		Fi
		If command (c) == MDN
#ifdef menu_wheel
		   || (command (c) == MOUSEescape && mouse_button == wheeldown)
#endif
		Then	itemno ++;
			If menus [meni].menuitems [itemno - 1].itemfu == separator
			Then	itemno ++;
			Fi
			If itemno > menulen
			Then	itemno = 1;
			Fi
		Elsif command (c) == MUP
#ifdef menu_wheel
		   || (command (c) == MOUSEescape && mouse_button == wheelup)
#endif
		Then	itemno --;
			If menus [meni].menuitems [itemno - 1].itemfu == separator
			Then	itemno --;
			Fi
			If itemno <= 0
			Then	itemno = menulen;
				If menus [meni].menuitems [itemno - 1].itemfu == separator
				Then	itemno --;
				Fi
			Fi
		Else	itemno = select_item (menus [meni].menuitems, menulen, itemno, c);
		Fi
		menselected (True, meni, itemno - 1);
	    Fi
	    flush ();
	    c = readcharacter_allbuttons ();
	Done
	If c == ' ' || command (c) == HOP || command (c) == GOTO
		|| (command (c) == MOUSEescape && mouse_button == middlebutton)
	Then	toggle_HOP ();
		clear_menus ();
		displaymenuline_from (meni);
		MINMEN (column, itemno);
	Elsif command (c) == MOUSEescape
	Then	/* mouse click */
		If mouse_xpos >= menu_width * meni
		&& mouse_xpos < menu_width * meni + popmenwidth
		&& mouse_ypos >= 0 && mouse_ypos < menulen
		&& (mouse_button == leftbutton || mouse_button == releasebutton)
		&& menus [meni].menuitems [mouse_ypos].itemfu != separator
		Then
			/*menselected (True, meni, mouse_ypos);*/
			clear_menus ();
			menu_mouse_mode (False);
			(* menus [meni].menuitems [mouse_ypos].itemfu)
				(menus [meni].menuitems [mouse_ypos].hopitemname);
		Elsif mouse_ypos == -1 && mouse_xpos / menu_width != meni
		Then	clear_menus ();
		/*	displaymenuheaders ();	*/
			displaymenuline_from (meni);
			MINMEN (mouse_xpos, 0);
		Fi
	Elsif (c == '\n' || command (c) == SNL) && itemno > 0
	Then
		clear_menus ();
		menu_mouse_mode (False);
		(* menus [meni].menuitems [itemno - 1].itemfu)
			(menus [meni].menuitems [itemno - 1].hopitemname);
	Elsif command (c) == MLF || command (c) == MRT
	Then	clear_menus ();
		displaymenuline_from (meni);
		If command (c) == MLF
		Then	If meni > 0
			Then	meni --;
			Else	/*meni = number_menus () - 1;*/
				/*flag_menu (arrlen (Flagmenu) - 1);*/
				prev_flag_menu ((menuitemtype *) 0);
				return;
			Fi
		Else	If meni < number_menus () - 1
			Then	meni ++;
			Else	/*meni = 0;*/
				/*flag_menu (0);*/
				next_flag_menu ((menuitemtype *) 0);
				return;
			Fi
		Fi
		openmenu (meni);
	Fi
	clear_menus ();
/*	displaymenuheaders ();	*/
/*	displaymenuline_from (meni);	*/
	displaymenuline ();
}


/***********************************************************************\
	Invoking flag menus explicitly
\***********************************************************************/

local void
	this_flag_menu (fm)
	menuitemtype * fm;
{
	int i;
	For i = 0 While i < arrlen (Flagmenu) Step i ++
	Do
		If Flagmenu [i].menu == fm
		Then
			ignore_1releasebutton = True;
			action_menu (Flagmenu [i].menu, 
				Flagmenu [i].menulen, 
				- (flags_pos + i - 1), -1, 
				Flagmenu [i].menutitle, True);
			return;
		Fi
	Done
	ring_bell ();
	error2 ("Internal error: ", "Menu not found");
}

global void
	handleQuotemenu ()
{
	this_flag_menu (Quotemenu);
}


global void
	handleKeymapmenu ()
{
	this_flag_menu (Keymapmenu);
}


global void
	handleEncodingmenu ()
{
	this_flag_menu (encodingmenu);
}


global void
	handleFlagmenus ()
{
	flag_menu (0);
}


/***********************************************************************\
	End
\***********************************************************************/
