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

#include "syntax.h"

#include "mined.h"
#include "io.h"
#include "termprop.h"


/***********************************************************************\
	Menu types and aux. function
\***********************************************************************/

#define STAYINMENU 0x8000

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

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


static
void
dummyfunc ()
{
}

void
fill_menuitem (item, s)
  menuitemtype * item;
  char * s;
{
  /* init menuitemtype struct to null */
  menuitemtype nullitem;
  memcpy ((void *) item, (void *) & nullitem, sizeof (menuitemtype));

  if (s) {
	item->itemname = s;
	item->itemfu = dummyfunc;
  } else {
	item->itemname = "";
	item->itemfu = separator;
  }

  item->hopitemname = "";
  item->itemon = (flagfunc) dummyfunc;
  item->extratag = NIL_PTR;
}


/***********************************************************************\
	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 pulldownmenu_width = 12;
local int horizontal_bar_width = 1;
local int extratag_length = 1;

/* display status */
local FLAG popupmenu_active = False;
local FLAG menuline_dirty = False;

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

/* state for menu history */
local FLAG menu_reposition = False;	/* token to go to previous item */
local menuitemtype * last_fullmenu = 0;
local menuitemtype * previous_fullmenu = 0;
local int previous_menuline = -1;
local int previous_scroll = -1;

/* menu state for sub-menu positioning */
local int last_menucolumn;
local int last_menuline;


/* menu state and saved menu status for redrawmenu () */
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;


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

local void handleInfomenu _((void));
local void handleBuffermenu _((void));
local void handleJustifymenu _((void));
local void handleParagraphmodemenu _((void));
#ifdef more_menu_handlers
local void handleCombiningmenu _((void));
local void handleTextmenu _((void));
local void handleIndentmenu _((void));
#endif

local void handleScreensizemenu _((void));


/***********************************************************************\
	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;
}


/***********************************************************************\
	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
}

#ifdef debug_rectangular_paste_mode
local void
	toggle_rectangular_paste_mode ()
{
	If rectangular_paste_flag
	Then	rectangular_paste_flag = False;
	Else	rectangular_paste_flag = True;
	Fi
	displayflags ();
}
#endif

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 rectangular_paste_flag
	Then	If pastebuf_utf8 && ! utf8_text
		Then	If append_flag
			Then	return "##/";
			Else	return "##[";
			Fi
		Else	If append_flag
			Then	return "#/";
			Else	return "#[";
			Fi
		Fi
	Else	If pastebuf_utf8 && ! utf8_text
		Then	If append_flag
			Then	return "##+";
			Else	return "##=";
			Fi
		Else	If append_flag
			Then	return "#+";
			Else	return "=";
			Fi
		Fi
	Fi
}

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

#ifdef debug_rectangular_paste_mode
local int
	rectangularpasteon (item, i)
		menuitemtype * item;
		int i;
{
	return rectangular_paste_flag == True;
}
#endif


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 int
	tabexpansionon (item, i)
		menuitemtype * item;
		int i;
{
	return expand_tabs;
}


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
	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
	(void) set_text_encoding (new_text_encoding, ' ', "men: change_encoding");
}

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

	If utf8_text && utf16_file
	Then	error ("Text encoding not switchable when editing UTF-16 file");
			ring_bell ();
			return;
	Fi

	/** 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 (); /* sic */
		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

	/** Special case: instead of UTF-16, switch to UTF-8 unless 
		it's current, then switch to Latin-1
	 */
	If strisprefix ("UTF-16", prev_text_encoding)
	Then	If utf8_text
		Then	prev_text_encoding = "ISO 8859-1";
		Else	prev_text_encoding = "UTF-8";
		Fi
	Fi

	If set_text_encoding (prev_text_encoding, ' ', "men: toggle_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
}

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

#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

	If utf8_text && utf16_file
	Then	error ("Text encoding not switchable when editing UTF-16 file");
		ring_bell ();
		return;
	Fi

	If set_text_encoding (menu [i].hopitemname, ' ', "men: select_encoding")
	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, ' ', "men: select_encoding prev");
			return;
		Fi
#endif
		If ! streq (new_prev_text_encoding, menu [i].hopitemname)
		Then	prev_text_encoding = new_prev_text_encoding;
		Fi

		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 encoding_flag [3];

local char *
	disp_encoding_1 ()
{
	encoding_flag [0] = '#';
	encoding_flag [1] = text_encoding_flag [0];
	encoding_flag [2] = '\0';
	return encoding_flag;
}

local char *
	disp_encoding_2 ()
{
	encoding_flag [0] = '#';
	encoding_flag [1] = text_encoding_flag [1];
	encoding_flag [2] = '\0';
	return encoding_flag;
}

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 uniscrwidth (unichar, quotemark, quotemark) == 2
		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 uniscrwidth (unichar, quotemark, quotemark) == 2
			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;
{
	/* strip off optional additional note from mapping tag */
	/* this is actually obsolete already as additional notes 
	   are explicitly included in the menu table as extratag;
	   leaving in the code for now just in case...
	 */
	char tag [55];
	char * tp = tag;
	char * np = menu [i].hopitemname;
	Dowhile * np && * np != ' '
	Do	* tp ++ = * np ++;
	Done
	* tp = '\0';
	/* set input method according to stripped tag */
	setKEYMAP (tag);
}

local int
	keymapon (item, i)
		menuitemtype * item;
		int i;
{
	return strisprefix (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
	Then	always_disp_code = False;
		If ! disp_Han_full
		Then	always_disp_Han = False;
		Fi
	Fi
}

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

	If always_disp_code
	Then	always_disp_fstat = False;
		If ! disp_Han_full
		Then	always_disp_Han = False;
		Fi
	Fi
}

local void
	select_script ()
{
	disp_scriptname = ! disp_scriptname;

	If disp_scriptname
	Then	always_disp_code = True;
	Fi
}

local void
	select_charname ()
{
	disp_charname = ! disp_charname;

	If disp_charname
	Then	always_disp_code = True;
		disp_scriptname = False;
		disp_decomposition = False;
		disp_mnemos = False;
	Fi
}

local void
	select_decomposition ()
{
	disp_decomposition = ! disp_decomposition;

	If disp_decomposition
	Then	always_disp_code = True;
		disp_charname = False;
		disp_mnemos = False;
	Fi
}

local void
	select_mnemos ()
{
	disp_mnemos = ! disp_mnemos;

	If disp_mnemos
	Then	always_disp_code = True;
		disp_charname = False;
		disp_decomposition = False;
	Fi
}

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_Hangul ()
{
	disp_Han_Hangul = ! disp_Han_Hangul;
	If disp_Han_Hangul
	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_XHCHanyuPinyin ()
{
	disp_Han_XHCHanyuPinyin = ! disp_Han_XHCHanyuPinyin;
	If disp_Han_XHCHanyuPinyin
	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 always_disp_Han && disp_Han_full
	Then	disp_Han_full = False;
	Else	always_disp_Han = ! always_disp_Han;
		disp_Han_full = False;
	Fi

	If always_disp_Han && ! disp_Han_full
	Then	always_disp_fstat = False;
		always_disp_code = False;
	Fi
}

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

	If always_disp_Han && ! disp_Han_full
	Then	always_disp_fstat = False;
		always_disp_code = False;
	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_script (menu, i)
		menuitemtype * menu;
		int i;
{
	return disp_scriptname | STAYINMENU;
}

local int
	infoon_charname (menu, i)
		menuitemtype * menu;
		int i;
{
	return disp_charname | STAYINMENU;
}

local int
	infoon_decomposition (menu, i)
		menuitemtype * menu;
		int i;
{
	return disp_decomposition | STAYINMENU;
}

local int
	infoon_mnemos (menu, i)
		menuitemtype * menu;
		int i;
{
	return disp_mnemos | STAYINMENU;
}

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 | STAYINMENU;
}

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

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

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

local int
	infoon_Hangul (menu, i)
		menuitemtype * menu;
		int i;
{
	return disp_Han_Hangul | STAYINMENU;
}

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

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

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

local int
	infoon_XHCHanyuPinyin (menu, i)
		menuitemtype * menu;
		int i;
{
	return disp_Han_XHCHanyuPinyin | STAYINMENU;
}

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

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

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


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

global void
	separator ()
{
}


local menuitemtype Filemenu [] =
{
	{"Open", EDIT, ""},
	{"View", VIEW, ""},
	{"Save", WT, ""},
	{"Set Name...", 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, ""},
	{"Quit", QUED, ""},
};

local menuitemtype Editmenu [] =
{
	{"Mark", MARK, "Go to Mark"},
	{"Cut", CUT, "Cut & Append"},
	{"Copy", COPY, "Append"},
	{"Paste", PASTE, "Paste Other"},
	{"Paste Previous", YANKRING, ""},
	{"Paste Other", PASTEEXT, ""},

	{"set/go markers", separator, ""},
	{"Go to Mark", GOMA, ""},
	{"Set marker N", MARKER, ""},
	{"Go marker N", GOMARKER, ""},

	{"insert", separator, ""},
	{"HTML/XML tag", HTML, "Embed in HTML/XML"},
	{"Control/Composed", CTRLINS, ""},

	{"conversion", separator, ""},
	{"Combine mnemonic", (voidfunc) UML, ""},
	{"Case toggle", LOWCAP, "Case toggle word"},
	{"Word case switch", LOWCAPWORD, ""},

	{"char -> code", separator, "code -> char"},
	{"Hex bytes", changehex, ""},
	{"Unicode value", changeuni, ""},
	{"Decimal value", changedec, ""},
	{"Octal value", changeoct, ""},
};

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 Character", SCURCHARFW, ""},
	{"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 Optionsmenu [] =
{
	{"editing features", separator, ""},
	{"Toggle auto indent", toggle_autoindent, ""},
	{"Word wrap mode...", handleJustifymenu, ""},
	{"Paragraph end...", handleParagraphmodemenu, ""},
	{"Edit/View only", toggle_VIEWmode, ""},
	{"Paste buffer mode...", handleBuffermenu, ""},

	{"display settings", separator, ""},
	{"Toggle HTML disp", toggle_HTML, ""},
	{"Toggle TAB width", toggle_tabsize, ""},
	{"Toggle TAB expansion", toggle_tab_expansion, ""},
	{"Screen size...", handleScreensizemenu, ""},
	{"File info", FS, "Toggle File info"},
	{"Char info", display_code, "Toggle Char info"},
	{"CJK/Info display...", handleInfomenu, ""},

	{"character handling", separator, ""},
	{"Text Encoding...", handleEncodingmenu, ""},
	{"Combined display", toggle_combining, ""},
	{"Input Method...", handleKeymapmenu, ""},
	{"Smart Quotes style...", handleQuotemenu, ""},

	{"help", separator, ""},
	{"Help topics", HELP_topics, ""},
	{"About mined", ABOUT, ""},
	{"function key bar", HELP_Fn, ""},
	{"accent prefix bar", HELP_accents, ""},
	{"toggle help bar", toggle_FHELP, ""},
};

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


local menutype Pulldownmenu [] =
{
	{"File", arrlen (Filemenu), Filemenu},
	{"Edit", arrlen (Editmenu), Editmenu},
	{"Search/go", arrlen (Searchmenu), Searchmenu},
	{"Paragraph", arrlen (Paragraphmenu), Paragraphmenu},
	{"Options", arrlen (Optionsmenu), Optionsmenu},
};


/***********************************************************************\
	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 */
	/*w	Armenian */
		{"French «“.”»", select_quote_type, "  «» “”", quoteon},

	/*	Swiss, Italian, Norwegian, Portuguese, Russian, Slovenian, 
		Turkish, Spanish, Albanian, Greek */
		{"«Swiss»", select_quote_type, "  «» ‹›", quoteon},

	/*w	Greek */
		{"“Greek„", select_quote_type, "  “„ ‘‚", quoteon},

	/*	Danish, Croatian, 
		German, Polish, Slovak, Czech, Serbian */
		{"»Danish«", select_quote_type, "  »« ›‹", quoteon},

	/*	Norwegian */
		{"Norweg. «‘’»", select_quote_type, "  «» ‘’", quoteon},

	/*	Swedish, Finnish */
	/*w	Dutch */
		{"”Swedish”", select_quote_type, "  ”” ’’", quoteon},
	/*	Swedish, Finnish */
		{"»Finnish»", select_quote_type, "  »» ››", quoteon},

	/*	Hebrew */
		{"Hebrew ״.״", select_quote_type, "  ״״ ׳׳", quoteon},

	/*	CJK */
	{"亞 CJK", separator, ""},
#ifdef single_double
		{"「corner」", select_quote_type, "「」 『』", quoteon},
#else
		{"「corner」", select_quote_type, "『』 「」", quoteon},
#endif
		{"【square】", select_quote_type, "【】 〖〗", quoteon},
		{" ❝quotes❞", select_quote_type, "❝❞ ❛❜", quoteon},
	/*	Chinese book marks */
		{"《bookmarks》", select_quote_type, "《》 〈〉", quoteon},

	{"traditional", separator, ""},

	/*	traditional Dutch, Afrikaans, Hungarian, Polish */
		{"„Dutch”", select_quote_type, "  „” ‚’", quoteon},

	/*	traditional Greek */
		{"Greek «‟.”»", select_quote_type, "  «» ‟”", quoteon},
};

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

#include "charesub.t"

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

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

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

local menuitemtype infomenu [] =
{
	{"File info", select_file_info, "", infoon_file},

	{"display char info", separator, ""},
	{"Char info", select_char_info, "", infoon_char},
	{" with script", select_script, "", infoon_script},
	{" with char name", select_charname, "", infoon_charname},
	{" with decompos.", select_decomposition, "", infoon_decomposition},
	{" with mnemos", select_mnemos, "", infoon_mnemos},

	{"display Han info", separator, ""},
	{"Han info", select_Han_info, "", infoon_Han},
	{" in status line", toggle_Han_short_description, "", infoon_descr_short},
	{" in popup", 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},
	{"Hangul", toggle_Hangul, "", infoon_Hangul},
	{"Korean", toggle_Korean, "", infoon_Korean},
	{"Vietnamese", toggle_Vietnamese, "", infoon_Vietnamese},
	{"Hanyu Pinlu", toggle_HanyuPinlu, "", infoon_HanyuPinlu},
	{"XHC Hànyǔ pīnyīn", toggle_XHCHanyuPinyin, "", infoon_XHCHanyuPinyin},
	{"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},

#define dont_debug_rectangular_paste_mode
#ifdef debug_rectangular_paste_mode
	{"rectangular", toggle_rectangular_paste_mode, "", rectangularpasteon},
#endif

	{"encoding", separator, ""},
	{"Unicode", toggle_pastebuf_mode, "", pastebuf_utf8on},
};

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

	{"", separator, ""},
	{"TAB expand", toggle_tab_expansion, "", tabexpansionon},
};

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

local menuitemtype paragraphmodemenu [] =
{
	{"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/XML tag", HTML, "Embed in HTML/XML"},

	{"", 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, ""},
	{"Screen size...", handleScreensizemenu, ""},
	{"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;
}




local menuitemtype *
	lookup_menuitem (menu, len, hopitem)
		menuitemtype * menu;
		int len;
		char * hopitem;
{
	int i;
	For i = 0 While i < len Step i ++
	Do	If streq (hopitem, menu [i].hopitemname)
		Then	return & menu [i];
		Fi
	Done
	return 0;
}


global menuitemtype *
	lookup_Keymap_menuitem (hopitem)
		char * hopitem;
{
	return lookup_menuitem (Keymapmenu, arrlen (Keymapmenu), hopitem);
}




/***********************************************************************\
	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*/, "Quote marks", Quotemenu, arrlen (Quotemenu)},
	{disp_rightquote, toggle_quote_type /*quote_type_up*/, "Quote marks", 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)},
#ifdef debug_rectangular_paste_mode
	{disppastebuf, toggle_rectangular_paste_mode, "Paste buf", buffermenu, arrlen (buffermenu)},
#else
	{disppastebuf, toggle_append, "Paste buf", buffermenu, arrlen (buffermenu)},
#endif
	{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", paragraphmodemenu, arrlen (paragraphmodemenu)},
};


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


local FLAG
	open_next_menu (current_menu)
		menuitemtype * current_menu;
{
	int i;
	FLAG passed = False;

	If current_menu == (menuitemtype *) 0
	Then	passed = True;
	Fi

	If current_menu == Popupmenu && ! (keyshift & alt_mask)
	Then	passed = True;
	Fi

	For i = 0 While i < arrlen (Pulldownmenu) Step i ++
	Do
		If Pulldownmenu [i].menuitems == current_menu
		Then	passed = True;
			If keyshift & alt_mask
			Then	break;
			Fi
		Elsif passed
		Then	openmenu (i);
			return True;
		Fi
	Done

	If current_menu == Popupmenu
	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;
			If keyshift & alt_mask
			Then	break;
			Fi
		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 True;
		Fi
	Done

	If passed
	Then	openmenu (0);
		return True;
	Else	return False;
	Fi
}


local FLAG
	open_prev_menu (current_menu)
		menuitemtype * current_menu;
{
	int i;
	FLAG passed = False;

	If current_menu == (menuitemtype *) 0
	Then	passed = True;
	Fi

	If current_menu == Popupmenu && ! (keyshift & alt_mask)
	Then	passed = True;
	Fi

	For i = arrlen (Pulldownmenu) - 1 While i >= 0 Step i --
	Do
		If Pulldownmenu [i].menuitems == current_menu
		Then	passed = True;
			If keyshift & alt_mask
			Then	break;
			Fi
		Elsif passed
		Then	openmenu (i);
			return True;
		Fi
	Done

	If current_menu == Popupmenu
	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;
			If keyshift & alt_mask
			Then	break;
			Fi
		Elsif	passed && Flagmenu [i].menutitle != NIL_PTR
			&& * ((* Flagmenu [i].dispflag) ()) != ' '
		Then	flag_menu (i);
			return True;
		Fi
	Done

	If passed
	Then	openmenu (arrlen (Pulldownmenu) - 1);
		return True;
	Else	return False;
	Fi
}




/***********************************************************************\
	putblock () writes a block graphics character
\***********************************************************************/
local void
	putblock (c)
		char c;
{
	If horizontal_bar_width == 2
	Then	/* workaround for xterm remaining background glitch */
		putstring ("  ");
	Fi

	If in_menu_border
	Then	putblockchar (c);
	Else	menuborder_on ();
		putblockchar (c);
		menuborder_off ();
	Fi
}


/***********************************************************************\
	putnstr () writes a string to screen at adjusted length
		used for menu items or menu borders
	putnstr_mark () does the same and marks given item (word)
	putnstr_tag () does the same and marks last column with tag
	putnstr_fill () does the same as subheading and fills the line
\***********************************************************************/

#define dont_debug_item_marking
#ifdef debug_item_marking
#define trace_item_marking(params)	printf params
#else
#define trace_item_marking(params)	
#endif

local void
	tagdisp_on ()
{
	/*unidisp_on ();*/
	menuitem_on ();
}

local void
	tagdisp_off ()
{
	/*unidisp_off ();*/
	menuitem_off ();
}


local char *
	putnstr_tag_mark (s, extratag, l, mark_item, fill_line, selected)
		char * s;	/* item string */
		char * extratag;	/* end item with this tag (highlight) */
		int l;		/* width of item contents, incl. extratag, excl. flag */
		int mark_item;	/* item to be marked in pick list (character selection menu) */
		FLAG fill_line;	/* fill line for sub-heading */
		FLAG selected;	/* mark line as selected menu item */
{
	int i = 0;		/* column counter */
	char * spoi = s;
	int utfcount;
	unsigned long unichar;
	int mark_index = 0;	/* subitem counter for marking */
	voidfunc mark_state = 0;
	FLAG title_active = True;
	FLAG lookup_show_info = True;
	char * show_char = NIL_PTR;
	trace_item_marking (("putnstr <%s>\n", s));

	If extratag && * extratag
	Then	l -= extratag_length;
	Fi

	If mark_item == 0
	Then
		trace_item_marking (("border on @ %d\n", i));

		If use_graphic_borders
		Then	/* workaround for graphics/colour attribute quirk */
			/*reverse_on ();*/
		Fi

		If * spoi >= '0' && * spoi <= '9'
		Then	/* normal subitem index label */
			reverse_off ();
			mark_on ();
			mark_state = mark_on;
		Else	/* R/S stroke count prefix */
			reverse_on ();
			mark_state = reverse_on;
		Fi
	Elsif mark_item > 0
	Then
		trace_item_marking (("header on @ %d\n", i));

		reverse_on ();
		mark_state = reverse_on;
	Fi
	Dowhile i < l
	Do
		If * spoi == '\0'
		Then	If mark_index == mark_item
			Then	/* end marked item */
				trace_item_marking (("end string @ %d\n", i));

				menuitem_off ();
				reverse_on ();
				mark_state = reverse_on;
			Fi
			mark_index ++;
			If fill_line
			Then
				If title_active
				Then	reverse_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 == ' '
		Then
			If mark_index == mark_item
			Then	/* end marked item */
				trace_item_marking (("end marked item @ %d\n", i));

				menuitem_off ();
				reverse_on ();
				mark_state = reverse_on;
			Fi
			mark_index ++;

			/* workaround for stateless colour attribute quirk */
			If mark_item >= 0 && selected && ! utf8_screen
			Then	/* restore background screen attribute */
				disp_normal ();
				reverse_on ();
			Fi

			putchar (* spoi ++);
			i ++;

			If mark_index == mark_item
			Then	/* begin marked item */
				trace_item_marking (("begin marked item @ %d\n", i));

				reverse_off ();
				mark_on ();
				mark_state = mark_on;
			Fi
		Elsif mark_state == mark_on && * spoi == ':'
		Then	putchar (* spoi ++);
			i ++;

			mark_off ();
			menuitem_on ();
			mark_state = menuitem_on;
		Elsif mark_item == 0 && mark_index == 0
			&& mark_state == reverse_on && (* spoi & 0x80) == 0
		Then	putchar (* spoi ++);
			i ++;

			/* late begin marked item for R/S menu */
			trace_item_marking (("late begin marked item @ %d\n", i));

			reverse_off ();
			mark_on ();
			mark_state = mark_on;
		Else	utf8_info (spoi, & utfcount, & unichar);
			If mark_index == mark_item && lookup_show_info && unichar >= 0x2E80
			Then	/* found Han character within marked item;
				   remember for returning, so the caller 
				   can display Han info for it */
				show_char = spoi;
				lookup_show_info = False;
			Fi

			If ! utf8_text
			Then	/* workaround for width handling problem */
				utf8_text = True;
				put_unichar (unichar);
				utf8_text = False;
			Else	put_unichar (unichar);
			Fi
			advance_utf8_scr (& spoi, & i, s);

			/* workaround for stateless colour attribute quirk */
			If selected && 
			   (   ((cjk_term || mapped_term) && no_char (mappedtermchar (unichar)))
			    || (! utf8_screen && ! cjk_term && ! mapped_term && unichar >= 0x100)
			   )
			Then	/* restore screen attribute of selected item 
				   after highlighted replacement display */
				If mark_state
				Then	disp_normal ();
					(* mark_state) ();
				Else	disp_selected (True, False);
				Fi
			Fi
		Fi
	Done
	If mark_state == reverse_on
	Then
		trace_item_marking (("header off @ %d\n", i));

		reverse_off ();
	Elsif mark_state == mark_on || mark_state == menuitem_on
	Then
		trace_item_marking (("border off @ %d\n", i));

		menuitem_off ();
		reverse_off ();
	Fi

	If extratag && * extratag
	Then	tagdisp_on ();
		put_unichar (utf8value (extratag));
		tagdisp_off ();
	Fi

	return show_char;
}


local char *
	putnstr_mark (s, l, mark_item, fill_line, selected)
		char * s;
		int l;
		int mark_item;
		FLAG fill_line;
		FLAG selected;
{
	return putnstr_tag_mark (s, NIL_PTR, l, mark_item, fill_line, selected);
}


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


local void
	putnstr_tag (s, extratag, l, selected)
		char * s;
		char * extratag;
		int l;
{
	(void) putnstr_tag_mark (s, extratag, 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 horizontal_bar_width == 2
			Then	/* workaround for xterm bold glitch */
				menuheader_off ();
			Fi

			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
			If horizontal_bar_width == 2
			Then	/* workaround for xterm bold glitch */
				menuheader_off ();
			Fi

			putblock ('t');

			For i = 2 While i < width Step i += horizontal_bar_width
			Do	putblock ('q');
				If horizontal_bar_width == 2
				Then	/* workaround 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 horizontal_bar_width == 2
		Then	/* workaround for xterm bold glitch */
			menuheader_off ();
		Fi

		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	/* workaround 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
}


/***********************************************************************\
	number_menus () determines the number of pull-down menus
\***********************************************************************/
local int
	number_menus ()
{
	return arrlen (Pulldownmenu);
}


/***********************************************************************\
	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 width = minwidth;
	For i = 0 While i < menulen Step i ++
	Do
		char * iname;
		int itemlen;

		item = & (menu [i]);

		itemlen = 2 + 2 * menumargin;
		If item->itemfu == separator
		Then	itemlen -= isflagmenu;
		Fi

		iname = hop_flag > 0 && item->hopitemname && * item->hopitemname
			? item->hopitemname
			: item->itemname;
		If iname && * iname
		Then	itemlen += utf8_col_count (iname);
		Fi
		/* add display length of extra tag if present */
		If item->extratag
		Then	itemlen += extratag_length;
		Fi

		/* increase maximum if this width is greater */
		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 cjk_width_data_version && (width & 1)
	Then	width ++;
	Fi
	return width;
}


local void
	calcmenuvalues ()
{
	int flags_gap;

	If cjk_width_data_version && (! use_vt100_block_graphics || xterm_version > 0)
	Then	horizontal_bar_width = 2;
	Fi

	/* determine positions for menus and flags display */
	pulldownmenu_width = XMAX - arrlen (Flagmenu) - 4;
	If pulldownmenu_width <= 0
	Then	pulldownmenu_width = 0;
	Else	pulldownmenu_width /= number_menus ();
	Fi

	popupmenumargin = menumargin;

	If pulldownmenu_width < 10
	Then	menumargin = 0;
	Fi

	/* determine popup menu width: */
	If (width_of_menu (Popupmenu, arrlen (Popupmenu), 0, 0, 0) < 15)
	Then	popupmenumargin = 0;
	Fi

	/* determine position and length of displayed flags */
	flags_pos = XMAX - 2 - arrlen (Flagmenu);
	flags_gap = flags_pos - number_menus () * pulldownmenu_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
	displaymenuheader () displays one menu header
	cleargap () clears the gap between menu headers and flags
	displayflags () displays the menu flags
	displaymenuline () displays the menu line
\***********************************************************************/
local void
	displaymenuheader (meni)
		int meni;
{
	set_cursor (meni * pulldownmenu_width, -1);
	putchar (' ');
	menudisp_on ();
	putnstr ("", menumargin);
	putnstr (Pulldownmenu [meni].menuname, pulldownmenu_width - 2 - 2 * menumargin);
	putnstr ("", menumargin);
	menudisp_off ();
	putchar (' ');
}

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 pulldownmenu_width < 3
	Then	return;
	Fi

	If standout_glitch
	Then	set_cursor (starti * pulldownmenu_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);
}


local void
	cleargap ()
{
	int i;
	int gappos = number_menus () * pulldownmenu_width;

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


global void
	displayflags ()
{
	int i;
	char * flags;
	FLAG is_reverse;

	If ! MENU
	Then	return;
	Fi

	calcmenuvalues ();

	If pulldownmenu_width < 3
	Then	return;
	Fi

	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;
}


global void
	displaymenuline ()
{
	If ! MENU
	Then	return;
	Fi

	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;
	LINE * menu_line = * menu_line_poi;

	If menu_line == NIL_LINE || menu_line == bot_line
	Then	* menu_line_poi = NIL_LINE;
	Else	* menu_line_poi = menu_line->next;
	Fi

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

	If menu_line == tail || menu_line == NIL_LINE
	Then	return;
	Elsif bidi_screen
	Then	For cp = menu_line->text
		While * cp != '\0'
		Step advance_char (& cp)
		Do	If is_right_to_left (unicodevalue (cp))
			Then	break;
			Fi
		Done
		If * cp != '\0'
		Then	set_cursor (0, line - 1);
			clear_eol ();
		Fi
	Fi
}




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

	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;

	/* save sub-menu reference */
	last_menucolumn = column;

	/* check if menu headers overwritten */
	If line == 0
	Then	menuline_dirty = True;
	Fi

	/* 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, menuwidth, 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
		If hop_flag > 0 && item->hopitemname != NIL_PTR && item->hopitemname [0] != '\0'
		Then	putborder_middle (column, line + 1 + i, menuwidth, item->hopitemname);
		Else	putborder_middle (column, line + 1 + i, menuwidth, item->itemname);
		Fi
	    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) & 1 /* mask extra flags */
			Then	put_menu_marker ();
			Else	putchar (' ');
			Fi
		Fi

		If hop_flag > 0 && item->hopitemname != NIL_PTR && item->hopitemname [0] != '\0'
		Then	putnstr_tag (item->hopitemname, item->extratag, 
				menuwidth - isflagmenu - 2 - 2 * popupmenumargin, 
				False);
		Else	putnstr_tag (item->itemname, item->extratag, 
				menuwidth - isflagmenu - 2 - 2 * popupmenumargin, 
				False);
		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, menuwidth, scroll < maxscroll);

	If line > 0 && title != NIL_PTR
	Then	/* flag menu */
		set_cursor (column + menuwidth - 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;
		If first_dirty_line < 0
		Then	first_dirty_line = 0;
		Fi
	Fi
	If line - 1 + menulen + 2 > last_dirty_line
	Then	last_dirty_line = line - 1 + menulen + 2;
	Fi

	last_menulen = menulen;
	last_menuwidth = menuwidth;
	last_disp_only = disp_only;
	last_menutitle = title;

	popupmenu_active = True;

	last_fullmenu = fullmenu;
}


/***********************************************************************\
	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;
	FLAG coloured = index < 0;
	int unicode_menubar_mode = use_unicode_menubar ();

	/* save sub-menu reference */
	last_menuline = line + i;

	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 && unicode_menubar_mode > 0
	Then	If coloured
		Then	disp_selected (True, True);
		Else	reverse_on ();
		Fi
		If unicode_menubar_mode > 1
		Then	put_unichar (0x258E);	/* ▎ LEFT ONE QUARTER 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 && coloured
	Then	disp_selected (True, False);
	Else	If standout_glitch
		Then	menuheader_off ();	/* disp_normal (); ? */
		Fi
	Fi
	putnstr ("", popupmenumargin);

	item = & (menu [i]);

	If isflagmenu
	Then	If (* item->itemon) (item, i) & 1 /* mask extra flags */
		Then	put_menu_marker ();
			If selected && coloured
			Then	disp_selected (True, False);
			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_tag (item->hopitemname, item->extratag, 
			menu_width - isflagmenu - 2 - 2 * popupmenumargin, 
			selected);
	Else	putnstr_tag (item->itemname, item->extratag, 
			menu_width - isflagmenu - 2 - 2 * popupmenumargin, 
			selected);
	Fi

	putnstr ("", popupmenumargin);
	/* switch to border */
	If selected && coloured
	Then	disp_normal ();
	Fi
	/* draw right border element */
	If selected && unicode_menubar_mode > 0
	Then	If coloured
		Then	disp_selected (False, True);
		Fi
		If unicode_menubar_mode > 1
		Then	put_unichar (0x258A);	/* ▊ LEFT THREE QUARTERS BLOCK */
		Else	put_unichar (0x258B);	/* ▋ LEFT FIVE EIGHTHS BLOCK */
		Fi
	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
	Elsif selected
	Then	disp_normal ();
		If ! can_reverse_mode || ! can_hide_cursor
		Then	/* only needed if terminal cannot standout / reverse: */
			set_cursor (column, line + i);
		Fi
	Fi
}


/***********************************************************************\
	clean_menus () wipes menus which are still displayed
	clear_menus () clears menus which are still displayed
\***********************************************************************/
global void
	clean_menus ()
{
	If popupmenu_active
	Then
		LINE * start_line = proceed (top_line, first_dirty_line);
		int ypos = y;

		menu_mouse_mode (False);

		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

		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
	If menuline_dirty
	Then	displaymenuline ();
	Fi
	popupmenu_active = False;
}

local void
	clear_menus ()
{
	clean_menus ();
}


/***********************************************************************\
	redrawmenu () redraws an open menu after screen refresh
\***********************************************************************/
global void
	redrawmenu ()
{
	If popupmenu_active
	Then
		int item = last_popup_item;
		display_menu (last_fullmenu, last_menulen, last_menuwidth, 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_fullmenu + last_scroll, 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
	used with mouse hilite tracking mode (xterm)
\***********************************************************************/
global int
	is_menu_open ()
{
	return 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;

	key = case_convert (key, 1);	/* normalise to upper case */

	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
			char * label;
			unsigned long letter;

			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

			/* look for first letter in menu label */
			letter = utf8value (label);
			Dowhile letter != 0 && ! isLetter (letter)
			Do	advance_utf8 (& label);
				letter = utf8value (label);
			Done

			/* look for further words;
			   check each word whether starting with key */
			Dowhile letter != 0
			Do
				If case_convert (letter, 1) == key
				Then	return i;
				Fi

				/* skip current word */
				Dowhile letter != 0 && isLetter (letter)
				Do	advance_utf8 (& label);
					letter = utf8value (label);
				Done
				/* look for next word */
				Dowhile letter != 0 && ! isLetter (letter)
				Do	advance_utf8 (& label);
					letter = utf8value (label);
				Done
			Done
		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;
}


/***********************************************************************\
	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

#ifdef debug_menu_scroll
#define trace_menu_scroll(params)	printf params
#else
#define trace_menu_scroll(params)
#endif


/**
   Calculate new menu position and set values.
   Return True if menu has to be scrolled.
 */
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
		Dowhile fullmenu [* scrolloffset + * itemno - 1].itemfu == separator
		Do	* itemno += delta;
			trace_menu_scroll (("   itemno sep ++ %d + %d\n", * itemno, * scrolloffset));
			If * itemno > menulen
			Then	If * scrolloffset == maxscrolloffset
				Then	/* cycle at end */
					* scrolloffset = 0;
					* itemno = 1;
					trace_menu_scroll (("   itemno sep ++ --> %d + %d\n", * itemno, * scrolloffset));
				Else	/* scroll 1 item */
					* scrolloffset += delta;
					* itemno -= delta;
					trace_menu_scroll (("   itemno sep ++ -> %d + %d\n", * itemno, * scrolloffset));
				Fi
			Fi
		Done
	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
		Dowhile fullmenu [* scrolloffset + * itemno - 1].itemfu == separator
		Do	* 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
		Done
	Elsif delta > 0
	Then	* itemno += delta;
		trace_menu_scroll (("   itemno %d + %d\n", * itemno, * scrolloffset));
		Dowhile * itemno > menulen
		Do	* scrolloffset += menulen;
			* itemno -= menulen;
			trace_menu_scroll (("-> itemno %d + %d\n", * itemno, * scrolloffset));
		Done

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

		If fullmenu [* scrolloffset + * itemno - 1].itemfu == separator
		Then	If * itemno < menulen
			Then	* itemno += 1;
			Else	* itemno -= 1;
			Fi
			trace_menu_scroll (("!> itemno %d + %d\n", * itemno, * scrolloffset));
		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 minline;
	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;
	FLAG continue_menu = True;

	keyshift = 0;

	If line == -1	/* suppressed display of menu header line */
	Then	line = 0;
	Fi

	/* 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;


	/* calculate general and specific menu geometry */
	calcmenuvalues ();
	menu_width = width_of_menu (fullmenu, fullmenulen, 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 is_flag_menu
	Then	minline = 1;
	Else	If MENU
		Then	minline = 0;
		Else	minline = 1;
		Fi
	Fi
	If startline < minline
	Then	maxscrolloffset = minline - startline;
		menulen = menulen - maxscrolloffset;
		startline = minline;
	Fi


	/* initial selection */
	If select_keys == NIL_PTR
	Then	itemno = 0;
	Else	itemno = 1;
		select_index = 0;
		sticky_index = 0;
	Fi

	If menu_reposition
	Then	(void) menu_scroll (& menu, menulen, previous_menuline + previous_scroll + 1 - startline, fullmenu, & scrolloffset, maxscrolloffset, & itemno);

		/* reset repositioning token */
		menu_reposition = False;
	Fi

	/* remember previous menu in case this sub-menu returns to it */
	previous_fullmenu = last_fullmenu;
	previous_menuline = last_menuline;
	previous_scroll = last_scroll;

stayinmenu:
	continue_menu = True;

	/* initial display */
	display_menu (fullmenu, menulen, menu_width, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
	/* this also sets menu_mouse_mode (True); */
	If itemno > 0
	Then	popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
	Fi

	/* for display-only menu, skip interactive part */
	If disp_only
	Then	menu_mouse_mode (False);
		return ret;
	Fi

	/* interactive menu handling */
	Dowhile continue_menu
	Do
	    flush ();
	    c = readcharacter_allbuttons ();

#ifdef debug_mouse
	    If command (c) == MOUSEescape
	    Then
		trace_mouse ("popup_menu");
	    Fi
#endif

	    If command (c) == RDwin
	    Then
#define winchg_quits_menu
#ifdef winchg_quits_menu
		popupmenu_active = False;
		RDwin ();
		clear_menus ();
		menu_mouse_mode (False);
		return ret;
#else
		int fullitemno = itemno + scrolloffset;

		RDwin_nomenu ();

		/* recalculate general and specific menu geometry */
		calcmenuvalues ();
		menu_width = width_of_menu (fullmenu, fullmenulen, 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 ();
			clear_menus ();
			return ret;
		Fi

		/* reset menu geometry to full menu for recalculation */
		menulen = fullmenulen;
		maxscrolloffset = 0;

		/* recalculate partial display */
		startline = line + 1;
		If startline > YMAX - menulen - 1
		Then	startline = YMAX - menulen - 1;
		Fi
		/* setup partial menu values for menu scrolling */
		If is_flag_menu
		Then	minline = 1;
		Else	minline = 0;
		Fi
		If startline < minline
		Then	maxscrolloffset = minline - startline;
			menulen = menulen - maxscrolloffset;
			startline = minline;
		Fi
#ifdef debug_resize_open_menus
	printf ("%dx%d start %d len %d max %d\n", YMAX, XMAX, startline, menulen, maxscrolloffset);
#endif

		/* reset and adjust menu scroll */
#define no_winchg_full_reset
#ifdef winchg_full_reset
		menu = fullmenu;
		scrolloffset = 0;
		itemno = 0;
		If fullitemno > 0
		Then	(void) menu_scroll (& menu, menulen, fullitemno, fullmenu, & scrolloffset, maxscrolloffset, & itemno);
		Fi
#else
		(void) menu_scroll (& menu, menulen, 0, fullmenu, & scrolloffset, maxscrolloffset, & itemno);
#endif

		/* redisplay */
		display_menu (fullmenu, menulen, menu_width, startcolumn, startline, title, is_flag_menu, disp_only, minwidth, scrolloffset, maxscrolloffset);
		If itemno > 0
		Then	popupmenselected (menu, menu_width, True, startcolumn, startline, itemno - 1, select_index, is_flag_menu);
		Fi
#endif

	    Elsif (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 (fullmenu, fullmenulen, 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 < minline || startcolumn < 0 || menu_width < 3
		Then	ring_bell ();
			return ret;
		Fi
		/* display the menu */
		display_menu (fullmenu, menulen, menu_width, 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 == releasebutton && mouse_prevbutton == middlebutton
	    Then	/* ignore */
	    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	display_menu (fullmenu, menulen, menu_width, 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	display_menu (fullmenu, menulen, menu_width, 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	display_menu (fullmenu, menulen, menu_width, 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);
	    Elsif command (c) == ENDkey || command (c) == smallENDkey
	    Then
		int prev_itemno = itemno;
		If menu_scroll (& menu, menulen, maxscrolloffset + menulen, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
		Then	display_menu (fullmenu, menulen, menu_width, 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);
	    Elsif command (c) == PD
	    Then
		int prev_itemno = itemno;
		If menu_scroll (& menu, menulen, menulen, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
		Then	display_menu (fullmenu, menulen, menu_width, 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	display_menu (fullmenu, menulen, menu_width, 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) == MOUSEescape && (mouse_shift & control_button) &&
		  (mouse_button == wheeldown || mouse_button == wheelup)
	    Then	continue_menu = False;
	    Elsif command (c) == MDN
		   || (c == ' ' && selection_space == SPACE_NEXTROW)
		   || (command (c) == MOUSEescape && mouse_button == wheeldown)
	    Then
			int prev_itemno = itemno;
			If menu_scroll (& menu, menulen, 1, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
			Then	display_menu (fullmenu, menulen, menu_width, 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
		   || (command (c) == MOUSEescape && mouse_button == wheelup)
	    Then
			int prev_itemno = itemno;
			If menu_scroll (& menu, menulen, - 1, fullmenu, & scrolloffset, maxscrolloffset, & itemno)
			Then	display_menu (fullmenu, menulen, menu_width, 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) == MOUSEescape
			&& mouse_button == releasebutton && ignore_releasebutton
	    Then	ignore_releasebutton = False;
	    Elsif command (c) == MOUSEescape
			&& (mouse_button == leftbutton
			    || mouse_button == movebutton
			    || mouse_button == rightbutton
			   )
	    Then
		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
		If mouse_button == leftbutton
		Then	continue_menu = False;
		Fi
	    Elsif command (c) == MOUSEescape
			&& mouse_button == releasebutton && mouse_prevbutton == rightbutton
	    Then	continue_menu = False;
	    Elsif select_keys == NIL_PTR && c != FUNcmd && c > (character) ' '
	    Then	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	display_menu (fullmenu, menulen, menu_width, 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);
	    Else	continue_menu = False;
	    Fi

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

	If command (c) == MOUSEescape
		&& mouse_button != wheelup
		&& mouse_button != wheeldown
	Then	/* mouse click */
		int menu_xpos = mouse_xpos - startcolumn;
		int menu_ypos = mouse_ypos - startline;

		If menu_ypos + startline == -1
		Then	clear_menus ();
			displaymenuheaders ();
			openmenuat (menu_xpos + startcolumn);
		Elsif menu_xpos >= 0 && menu_xpos < menu_width
		   && menu_ypos >= 0 && menu_ypos < menulen
		Then
		    If select_keys != NIL_PTR
		    Then
			/* determine mouse position in menu line */
			/* used for keyboard mapping selection */
			select = menu [menu_ypos].itemname;
			cpoi = select;
			ret = 0;
			scol = 1;
			Dowhile scol < menu_xpos && * cpoi != '\0'
			Do	If * cpoi == ' '
				Then	ret ++;
				Fi
				advance_utf8_scr (& cpoi, & scol, select);
			Done
			If ret < strlen (select_keys)
			Then	ret = (scrolloffset + menu_ypos)
					* strlen (select_keys)
					+ ret;
			Else	ret = -1;
			Fi
		    Elsif menu [menu_ypos].itemfu != separator
		    Then
			popupmenselected (menu, menu_width, True, startcolumn, startline, menu_ypos, select_index, is_flag_menu);
			menu_mouse_mode (False);
			(* menu [menu_ypos].itemfu) (menu, menu_ypos);
			displayflags ();

			/* handle STAYINMENU feature */
			If * menu [menu_ypos].itemon
			Then	int on = (* menu [menu_ypos].itemon) (menu, menu_ypos);
				If on & STAYINMENU
				Then	ignore_releasebutton = True;
					goto stayinmenu;
				Fi
			Fi
		    Fi
		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 ();

			/* handle STAYINMENU feature */
			If * menu [itemno - 1].itemon
			Then	int on = (* menu [itemno - 1].itemon) (menu, itemno - 1);
				If on & STAYINMENU
				Then	ignore_releasebutton = True;
					goto stayinmenu;
				Fi
			Fi
		Fi
	Elsif select_keys != NIL_PTR && c != FUNcmd && c > (character) ' '
	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 ! input_active && (
		command (c) == MLF
		|| (command (c) == MOUSEescape
		    && mouse_button == wheelup && (mouse_shift & control_button))
		)
	Then	clear_menus ();
		If ! open_prev_menu (fullmenu)
		Then	/* set token to position to previously selected item */
			menu_reposition = True;
			/* find previous menu in list of all menus to invoke */
			that_menu (previous_fullmenu);
		Fi
	Elsif ! input_active && (
		command (c) == MRT
		|| (command (c) == MOUSEescape
		    && mouse_button == wheeldown && (mouse_shift & control_button))
		)
	Then	clear_menus ();
		If ! open_next_menu (fullmenu)
		Then	/* find one menu right from previous menu */
			If ! open_next_menu (previous_fullmenu)
			Then	ring_bell ();
				error2 ("Internal error: ", "Menu not found");
			Fi
		Fi
	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);
}


/***********************************************************************\
	Invoking menus explicitly
	that_menu (m) opens that menu at its native position
	this_menu_here (m) open given menu at given position
	openmenuat (c) opens the menu at screen column c
	openmenu (i) opens the i-th menu
\***********************************************************************/
local void
	that_menu (m)
		menuitemtype * m;
{
	int i;
	For i = 0 While i < arrlen (Pulldownmenu) Step i ++
	Do
		If Pulldownmenu [i].menuitems == m
		Then
			ignore_1releasebutton = True;
			action_menu (Pulldownmenu [i].menuitems, 
				Pulldownmenu [i].menulen, 
				i * pulldownmenu_width, -1, 
				Pulldownmenu [i].menuname, False);
			return;
		Fi
	Done
	For i = 0 While i < arrlen (Flagmenu) Step i ++
	Do
		If Flagmenu [i].menu == m
		Then
			ignore_1releasebutton = True;
			action_menu (Flagmenu [i].menu, 
				Flagmenu [i].menulen, 
				- (flags_pos + i - 1), 0, 
				Flagmenu [i].menutitle, True);
			return;
		Fi
	Done
	If Popupmenu == m
	Then
		ignore_1releasebutton = True;
		QUICKMENU ();
		return;
	Fi
	ring_bell ();
	error2 ("Internal error: ", "Menu not found");
}

local void
	this_menu_here (menu, menulen, menutitle)
		menuitemtype * menu;
		int menulen;
		char * menutitle;
{
	ignore_1releasebutton = True;
	(void) popup_menu (menu, menulen, 
		last_menucolumn + 9, last_menuline, 
		menutitle, 
		False, 
		False, NIL_PTR);
}

global void
	openmenuat (column)
		int column;
{
	int meni;

	calcmenuvalues ();

	If pulldownmenu_width < 3
	Then	return;
	Fi
	meni = column / pulldownmenu_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 == leftbutton || mouse_button == middlebutton
			Then	(* Flagmenu [meni].toggle) (column);
			Elsif Flagmenu [meni].menutitle != NIL_PTR
			Then	If * ((* Flagmenu [meni].dispflag) ()) != ' '
				Then	action_menu (
						Flagmenu [meni].menu, 
						Flagmenu [meni].menulen, 
						- (column - 1), 
						0, 
						Flagmenu [meni].menutitle, 
						True);
				Fi
			Fi
			displayflags ();
			set_cursor_xy ();
		Fi
	Else
		If mouse_button == middlebutton && hop_flag == 0
		Then	toggle_HOP ();
		Fi
		that_menu (Pulldownmenu [meni].menuitems);
	Fi
}

local void
	openmenu (i)
		int i;
{
	openmenuat (i * pulldownmenu_width);
}


/***********************************************************************\
	Invoking specific menus
\***********************************************************************/

global void
	FILEMENU ()
{
	openmenu (0);
}

global void
	EDITMENU ()
{
	openmenu (1);
}

global void
	SEARCHMENU ()
{
	openmenu (2);
}

global void
	PARAMENU ()
{
	openmenu (3);
}

global void
	OPTIONSMENU ()
{
	openmenu (4);
	/*that_menu (Optionsmenu);*/
}

global void
	QUICKMENU ()
{
	action_menu (Popupmenu, arrlen (Popupmenu), x, y, NIL_PTR, False);
}

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


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


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


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


local void
	handleInfomenu ()
{
	that_menu (infomenu);
}

local void
	handleBuffermenu ()
{
	that_menu (buffermenu);
}

local void
	handleJustifymenu ()
{
	that_menu (justifymenu);
}

local void
	handleParagraphmodemenu ()
{
	that_menu (paragraphmodemenu);
}


local void
	handleScreensizemenu ()
{
	this_menu_here (Screensizemenu, arrlen (Screensizemenu), "Screen size");
}

#ifdef more_menu_handlers
local void
	handleCombiningmenu ()
{
	that_menu (combiningmenu);
}

local void
	handleTextmenu ()
{
	that_menu (textmenu);
}

local void
	handleIndentmenu ()
{
	that_menu (autoindentmenu);
}
#endif


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