#**************************************************************************** # perlmenu.pm -- Perl Menu Support Facility # # Version: 4.0 # # Author: Steven L. Kunz # Networked Applications # Iowa State University Computation Center # Ames, IA 50011 # # Major Contributors (Version 3.0, 3.1): # Chris Candreva (chris@westnet.com) # WestNet Internet Services of Westchester # # Alan Cunningham # NASA Spacelink Project # # Official PerlMenu WWW home page: # http://www.cc.iastate.edu/perlmenu/ # # Official Package Distributions: # ftp://ftp.iastate.edu/pub/perl # # Bugs: skunz@iastate.edu # Cheers: skunz@iastate.edu # # Date: Version 1.0 -- May, 1992 -- Original version # Version 1.1 -- Aug, 1992 -- Minor enhancements, bugfixes # Version 1.2 -- Nov, 1992 -- Selection bugfix # Version 1.3 -- Dec, 1992 -- "top" and "latch" functions added # Version 1.4 -- Apr, 1993 -- "r=refresh" added to bottom line # Version 2.0 -- Sep, 1993 -- Radio-button, Multiple-selection, # shell-escape, new "hot-keys", and # "menu_getstr" routine. # Version 2.1 -- Oct, 1993 -- Bug fixes # Version 2.2 -- Mar, 1994 -- Menu sub-titles # Version 2.3 -- Jun, 1994 -- Bug fixes # Version 3.0 -- Jan, 1995 -- Templates, lots of new options on # many calls, Perl5 interfacing. # Version 3.1 -- Mar, 1995 -- Bug fixes, new "menu_template_setexit" # call, new menu_pref. # Version 3.2 -- Jun, 1995 -- Bug fixes, template "required field" # support, template Control-L refresh. # Version 3.3 -- Feb, 1996 -- Bug fixes, help routines, templates # from arrays ("menu_load_template_array") # Version 4.0 -- Feb, 1997 -- Converted to "pm" module, highlighted # selection cursor pref, Multiple-column # menus pref, bug fixes # # Notes: Perl4 - Requires "curseperl" # (distributed with perl 4.36 in the usub directory) # Perl5 - Requires "Curses" extension available from any CPAN # site (http://www.perl.com/CPAN/CPAN.html). # # Put the following at top of your code: # # BEGIN { $Curses::OldCurses = 1; } # use Curses; # # Use: # &menu_init(1,"title"); # &menu_item("Topic 1","got_1"); # &menu_item("Topic 2","got_2"); # ... # &menu_item("Topic n","got_n"); # $sel_text = &menu_display("Select using arrow keys"); # # PerlMenu - Perl library module for curses-based menus & data-entry templates # Copyright (C) 1992-97 Iowa State University Computation Center # Ames, Iowa (USA) # # This Perl library module is free software; you can redistribute it # and/or modify it under the terms of the GNU Library General Public # License (as published by the Free Software Foundation) or the # Artistic License. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Library General Public License for more details. # # You should have received a copy of the GNU Library General Public # License along with this library; if not, write to the Free # Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # #**************************************************************************** package perlmenu; #%PERL5ONLY% DO NOT REMOVE OR CHANGE THIS LINE BEGIN { $Curses::OldCurses = 1; } use Curses; require Exporter; @ISA = qw(Exporter); @EXPORT = qw( menu_curses_application menu_prefs menu_template_prefs menu_shell_command menu_quit_routine menu_init menu_paint_file menu_setexit menu_getexit menu_item menu_display menu_display_radio menu_display_mult menu_getstr menu_template_setexit menu_load_template menu_load_template_array menu_overlay_clear menu_overlay_template menu_display_template ); #%PERL5ONLY% DO NOT REMOVE OR CHANGE THIS LINE # PERL5 ONLY (GETCAP PROBLEMS) # Uncomment these statements if you DON'T have "getcap()" OR # if the demo doesn't appear to work (there's a bug in some getcap's). # #if ($] >= 5.001) { # Perl5 ONLY! #package Perl5::Menu_PL::Compat; # Don't pollute perlmenu.pm namespace #require Term::Cap; # Get Tgetent package #$term = Tgetent Term::Cap { OSPEED => 9600 }; # Define entry #sub perlmenu::getcap { $term->{"_" . shift()} }; # Define local subroutine #} # PERL4 ONLY (GETCAP PROBLEMS) # Uncomment these statements if you DON'T have "getcap()" OR # if the demo doesn't appear to work (there's a bug in some getcap's). # #if (($] >= 4.0) && ($] < 5.0)) { # Perl4 ONLY! #package simgetcap; # Don't pollute menu.pl namespace #$ispeed = $ospeed = 13; # Set old-style "9600"; #require "termcap.pl"; # Get Tgetent package #&Tgetent($ENV{'TERM'}); # Load $TC array #sub main'simgetcap { $TC{shift}; }; # Define local subroutine #} # Preferences (set by "menu_pref") $curses_application = 0; # Application will do initscr, endwin $center_menus = 0; # Center menus $gopher_like = 0; # More gopher-like arrow keys $disable_quit = 0; # Disable "Quit" hot-key $quit_prompt = ""; # Override Quit prompt $quit_default = "y"; # Quit default response $multiple_column = 0; # Multiple column menus $highlight = 0; # Highlight selection points $prompt_for_quit = 1; # Enable quit prompt $menu_exit_routine = "main'clear"; $menu_generic_help_routine = "menu_default_show_help"; $menu_item_help_routine = ""; $menu_shell_text = ""; $menu_is_first_one = 1; $menu_is_top_one = 0; $menu_top_activated = 0; $finding_top = 0; @menu_sel_text = (); # Menu item selection text @menu_sel_action = (); # Menu item actions @selected_action = (); # Menu item selected flags (multiple-selection menus) @menu_sub_title = (); # Top Sub-title strings $menu_sub_titler = ""; # Top Sub-title builder routine @menu_bot_title = (); # Bottom title strings $menu_bot_titler = ""; # Bottom title builder routine $did_initterm = 0; # We already got escape sequences for arrows, etc. $window = 0; # Base window $xrow = $xcol = 0; $first_line = $last_line = $item_lines_per_screen = 0; $items_per_screen = $items_per_line = 0; $arrow_col = $arrow_line = 0; $max_sel_line = $max_sel_col = 0; $menu_top_item = 0; $arrow_spec_row = $arrow_spec_col = 0; @menu_exitkeys = (); # Exit-key strings $menu_lastexit = ""; # Exit-key string that caused the last exit $show_mail = ""; $max_item_len = 0; # Length of longest selection text $left_margin = 0; # Leftmost column of menu (for centering) $prepend_len = 0; # Length of text we prepend to each selection $column_width = 0; # Width of each item in column $ku = $ansi_ku = $kd = $ansi_kd = ""; $field = 0; # Count of loaded template fields $template_exit_active = 0; # Currently processing a template user exit @template_exitkeys = (); # Exit-key strings @menu_template_line = (); # Template text line @menu_template_row = (); # Data entry rows @menu_template_col = (); # Data entry cols @menu_template_len = (); # Data entry lengths @menu_template_type = (); # Data entry type # (0=alpha-numeric, 1=numeric, 2=no-show) $last_window_cpos = 0; # Storage to last windows cursor position. @menu_overlay_row = (); # Template overlay row @menu_overlay_col = (); # Template overlay col @menu_overlay_text = (); # Template overlay text @menu_overlay_rend = (); # Template overlay rendition @menu_overlay_stick = (); # Template overlay "sticky" flags $req_lmark_set = "*"; # Marker for required fields - left (set) $req_lmark_clear = " "; # Marker for required fields - left (clear) $req_lmark_attr = 0; # Marker attribute - left (0=normal,1=standout) $req_rmark_set = ""; # Marker for required fields - right (set) $req_rmark_clear = ""; # Marker for required fields - right (clear) $req_rmark_attr = 0; # Marker attribute - right (0=normal,1=standout) @req_mark_row = (); # Required field markers - rows @req_lmark_col = (); # Required field markers - left cols @req_rmark_col = (); # Required field markers - right cols # Simple emacs-style editing key definitions $begin_of_line = "\cA"; $end_of_line = "\cE"; $next_char = "\cF"; $prev_char = "\cB"; $next_field = "\cN"; $prev_field = "\cP"; $redraw_screen = "\cL"; $delete_right = "\cD"; $kill_line = "\cK"; # Normally yank_line would be "\cY" (C-y), unfortunately both C-z and C-y are # are used to send the suspend signal in our environment. Bind it to # C-u for a lack of anything better. $yank_line = "\cU"; # buffer $kill_buffer = ""; ###### VARS ADDED BY LEE FOR CUSTOM KEY CHECKING... %custom_keys = (); #********** # MENU_CURSES_APPLICATION # # Function: Indicate application is using curses calls. If called, # the menu routines will not do initscr and endwin calls # (the application must do them). # # Call format: &menu_curses_application(window); # # Arguments: The main window (gotten from an initscr call) # # Returns: Main window (either passed in or gotten here) #********** sub menu_curses_application { ($window) = @_; $curses_application = 1; # Sanity check. If no window, get one. if (!$window) { $window = &initscr(); } $window; } #********** # MENU_PREFS # # Function: Establish general default preferences for menu style. # # Call format: &menu_pref(center_menus,gopher_like,quit_flag,quit_prompt, # quit_def_resp,mult_col_menu,highlight_sel_text); # # Arguments: - Boolean flag (0=left justified menus, 1=centered menus) # - Boolean flag (0=normal, 1=more gopher-like) # - Boolean flag (0=allow quit, 1=disable quit) # - String with default "Quit" prompt # - String with "Quit" default response character # - Boolean flag (0=single column menus, 1=multiple column menus # - Boolean flag (0=arrow in front of selection text, # 1=highlight selection text (no arrow) # # Returns: Nothing #********** sub menu_prefs { ($center_menus,$gopher_like,$disable_quit,$quit_prompt,$quit_default, $multiple_column,$highlight,$prompt_for_quit) = @_; # Don't allow bad default characters. if (($quit_default ne "Y") && ($quit_default ne "y") && ($quit_default ne "N") && ($quit_default ne "n")) { $quit_default = "y"; } } #********** # MENU_TEMPLATE_PREFS # # Function: Establish general default preferences for templates. # # Call format: &menu_template_pref(mark-set-left,mark-clear-left,left-attr, # mark-set-right,mark-clear-right,right-attr); # # Arguments: - Required field marker flag string (for left of field) # - Required field marker flag clear string (for left of field) # - Attribute for left marker (0=normal,1=standout) # - Required field marker flag string (for right of field) # - Required field marker flag clear string (for right of field) # - Attribute for right marker (0=normal,1=standout) # # Returns: Nothing #********** sub menu_template_prefs { ($req_lmark_set,$req_lmark_clear,$req_lmark_attr, $req_rmark_set,$req_rmark_clear,$req_rmark_attr) = @_; } #********** # MENU_SHELL_COMMAND # # Function: Enable "!" as shell escape from a menu and indicate the # command that should be issued. # # Call format: &menu_shell_command("shell-path command"); # # Arguments: Shell path (such as "/bin/csh") to start shell spawn-off. # If null value is supplied, shell escape is disabled. # # Returns: Nothing #********** sub menu_shell_command { ($menu_shell_text) = @_; } #********** # MENU_QUIT_ROUTINE # # Function: Specify a "cleanup" routine to be called before a "quit" # from the application is processed. # # Call format: &menu_quit_routine("string"); # # Arguments: String containing name of exit routine to call. # # Returns: Nothing. # #********** sub menu_quit_routine { $menu_exit_routine = "main'@_"; } #********** # MENU_HELP_ROUTINE # # Function: Specify a "help" routine to be called when "h" or "H" # is pressed during menu display. # # Call format: &menu_help_routine("string"); # # Arguments: String containing name of help routine to call. # If a null string is provided, the default routine is used. # # Returns: Nothing. # #********** sub menu_help_routine { local($rtn) = @_; if ($rtn eq "") { $menu_generic_help_routine = "menu_default_show_help"; } else { $menu_generic_help_routine = "main'$rtn"; } } #********** # MENU_INIT # # Function: Initialize menu type (numbered or unnumbered), arrays, # title, and "top" flags. # # Call format: &menu_init([0|1],"Top Title",[0|1],"Sub Titles"); # # Arguments: Boolean flag indicating whether or not a arrows and numbers # are desired (0=no, 1=yes) and title text (for the top line # of menu), an optional boolean top-menu indicator, and an # optional sub-title. # # Returns: Window value from "initscr" call. # # Notes: 1) If the title string begins with a "-" the title is not # presented in reverse-video ("standout") representation. # 2) Optional sub-titles have two controls characters (which # are order dependant. A "-" functions as in the title. # A "<" or ">" as the next character (or first, if "-" # is not used) performs left or right justification. # 3) If this is the FIRST menu_init call and the optional # third opernd is "1", it is the "top" menu. #********** sub menu_init { ($menu_numbered,$menu_top_title,$menu_is_top_one,$sub_titles,$bot_titles, $item_help) = @_; local($i,$justify); # Perform initscr if not a curses application if (!$curses_application && !$window) { $window = &initscr(); } # Load "magic sequence" array based on terminal type if (!$did_initterm) { # Get terminal info (if we don't have it). &defbell() unless defined &bell; # Method 1 (getcap) # Uncomment if you have "getcap" # $ku = &getcap('ku'); # Cursor-up # $kd = &getcap('kd'); # Cursor-down # $kr = &getcap('kr'); # Cursor-right # $kl = &getcap('kl'); # Cursor-left # $cr = &getcap('cr'); # Carriage-return # $nl = &getcap('nl'); # New-line # Method 2 (tigetstr) # Uncomment if you have tigetstr (Solaris) instead of "getcap" # $ku = &tigetstr('kcuu1'); # Cursor-up # $kd = &tigetstr('dcud1'); # Cursor-down # $kr = &tigetstr('kcuf1'); # Cursor-right # $kl = &tigetstr('kcub1'); # Cursor-left # $cr = &tigetstr('cr'); # Carriage-return # $nl = &tigetstr('nl'); # New-line # Method 3 (tput) # Uncomment if you have terminfo (and tput) instead of "getcap" $ku = `tput kcuu1`; # Cursor-up $kd = `tput kcud1`; # Cursor-down $kr = `tput kcuf1`; # Cursor-right $kl = `tput kcub1`; # Cursor-left $cr = `tput kent`; # Carriage-return # HP-UX 9.05 users: try $cr = `tput cr` if # "tput kent" gives errors $nl = `tput nel`; # New-line $ansi_ku = "\033[A"; # Ansi cursor-up (for DEC xterm) $ansi_kd = "\033[B"; # Ansi cursor-down (for DEC xterm) $ansi_kr = "\033[C"; # Ansi cursor-right (for DEC xterm) $ansi_kl = "\033[D"; # Ansi cursor-left (for DEC xterm) @magic_seq = ($ku,$ansi_ku,$kd,$ansi_kd,$kl,$ansi_kl,$kr,$ansi_kr, $cr,$nl,"\n", "n","N"," ","p","P","b","B","e","E", $begin_of_line, $end_of_line, $next_char, $prev_char, $next_field, $prev_field, $redraw_screen, $delete_right, $kill_line, $yank_line, "a","A","c","C","m","M","=","/","h","H","?","!",'^W'); push(@magic_seq, $_) foreach (keys %custom_hotkeys); $did_initterm = 1; } # Check for title format character. $menu_top_title_attr = 0; if (substr($menu_top_title,0,1) eq '-') { $menu_top_title = substr($menu_top_title,1); $menu_top_title_attr = 1; } # Center top title if (length($menu_top_title) >= $main'COLS) { $menu_top_title = substr($menu_top_title,0,$main'COLS-1); $menu_top_title_col = 0; } else { $menu_top_title_col = int($main'COLS/2) - int(length($menu_top_title)/2); } # Process any sub-titles like the title. @menu_sub_title = (); $menu_sub_titler = ""; @menu_bot_title = (); $menu_bot_titler = ""; $first_line = 2; # Assume no sub-titles for now $last_line = $main'LINES - 3; # Assume no bottom-titles for now if ($sub_titles ne "") { if ($sub_titles =~ /^&/) { $menu_sub_titler = "main'".substr($sub_titles,1); # Run-time routine } else { $first_line += &proc_titles($sub_titles,*menu_sub_title, *menu_sub_title_attr,*menu_sub_title_col); } } if ($bot_titles ne "") { if ($bot_titles =~ /^&/) { $menu_bot_titler = "main'".substr($bot_titles,1); # Run-time routine } else { $last_line -= &proc_titles($bot_titles,*menu_bot_title, *menu_bot_title_attr,*menu_bot_title_col); $last_line--; # Blank line between menu and bottom titles } } $item_lines_per_screen = $last_line - $first_line + 1; $arrow_line = $first_line; # Arrow on top item by default $arrow_col = 0; # Arrow on leftmost item by default # Process item help routine. $menu_item_help_routine = ""; if ($item_help) { $menu_item_help_routine = "main'$item_help"; } # Enable "top menu" functions if first menu is a top menu. if ($menu_is_first_one && $menu_is_top_one) { $menu_top_activated = 1; } $menu_is_first_one = 0; # Init selection array @menu_sel_text = (); # Selection text for each item @menu_sel_action = (); # Action text for each item @selected_action = (); # Selected items (multiple selection menus) $menu_index = 0; # Reset flags # Init some other key variables $menu_top_item = 0; # First item is the top item by default $last_menu_top_item = -1; # Force drawing of menu items $max_item_len = 0; # Reset max length # Return window value from "initscr" call. $window; } #*********** # MENU_PAINT_FILE # # Function: Define a text file to display with a menu # # Call format: &menu_paint_file("name_of_file",top_bottom_flag); # # Arguemnts: - File name to display # - Location (0=sub-title area, 1=bottom-title area # # Returns: 0=Success, 1=Cannot open file #*********** sub menu_paint_file { ($paint_file,$top_bot) = @_; local($concat_string,$i); open(INFILE,$paint_file) || return(1); # Reset appropriate array and first or last line if ($top_bot == 0) { @menu_sub_title = (); $menu_sub_titler = ""; $first_line = 2; # Changing first menu line } else { @menu_bot_title = (); $menu_bot_titler = ""; $last_line = $main'LINES - 3; # Changing last menu line } # Suck up file into a title string $i = 0; $concat_string = ""; while () { $concat_string .= $_; } close(INFILE); # Process title string as normal into top/bottom titles if ($top_bot == 0) { $first_line += &proc_titles($concat_string,*menu_sub_title, *menu_sub_title_attr,*menu_sub_title_col); } else { $last_line -= &proc_titles($concat_string,*menu_bot_title, *menu_bot_title_attr,*menu_bot_title_col); $last_line--; # Blank line between menu and bottom titles } # Re-calculate some key variables $arrow_line = $first_line; # Arrow on top item by default $arrow_col = 0; 0; } #********* # MENU_SETEXIT # # Function: Set the keys for exiting MENU_GETSTR # # Call format: &menu_setexit(@exit_key_array); # OR # &menu_setexit("exit_seq1","exit_seq2",...); # # Arguments: exit_key_array - the keys to end menu_getstr on #********** sub menu_setexit { @menu_exitkeys = @_; } #********** # MENU_GETEXIT # # Function: Returns the key last used to exit menu_getstr # #********** sub menu_getexit { return($menu_lastexit); } #*********** # PROC_TITLES # # Function: Process title string into sub-title/bottom title arrays. # # Call format: &proc_titles("String",*string_array,*attr_array,*col_array); # # Arguments: String presented in menu, array to load. # # Returns: Number of items loaded into the array. #*********** sub proc_titles { local($title_strings,*temp_title,*temp_title_attr,*temp_title_col) = @_; local($i); # If the title is a function, we will process it at display time. if ($title_strings =~ /^&/) { $temp_title[0] = substr($title_strings,1); $temp_title_col[0] = -1; $temp_title_attr[0] = -1; return(1); } @temp_title = split('\n',$title_strings); if ($#temp_title < 0) { return(0); } for ($i = 0; $i <= $#temp_title; $i++) { # Figure out rendition. $temp_title_attr[$i] = 0; if (substr($temp_title[$i],0,1) eq '-') { $temp_title[$i] = substr($temp_title[$i],1); $temp_title_attr[$i] = 1; } # Figure out justification. $justify = substr($temp_title[$i],0,1); if (($justify eq "<") || ($justify eq ">")) { $temp_title[$i] = substr($temp_title[$i],1); } else { $justify = ""; } if (length($temp_title[$i]) >= $main'COLS-1) { $temp_title[$i] = substr($temp_title[$i],0,$main'COLS-1); $temp_title_col[$i] = 0; } else { if ($justify eq "<") { $temp_title_col[$i] = 0; } elsif ($justify eq ">") { $temp_title_col[$i] = $main'COLS - length($temp_title[$i]) - 1; } else { $temp_title_col[$i] = int($main'COLS/2) - int(length($temp_title[$i])/2); } } } $i; # Return number of items processed } #*********** # MENU_ITEM # # Function: Add an item to the active menu. # # Call format: &menu_item("What you see","test_rtn",pre_set_flag); # # Arguments: - String presented in menu. Required. # - String returned if selected. Optional. # - Value to pre-set multiple selection menu. Optional. # (neg=lockout selection,0=allow selection,1=preset selection) # # Returns: Number of items currently in the menu. #*********** sub menu_item { local($item_text,$item_sel,$item_set,$item_attr,$item_preview) = @_; # Sanity check if ($item_lines_per_screen <= 0) { return(0); } if (!$item_text) { return($menu_index); } if (!$item_set) { $item_set = 0; } # Adjust max length value (for centering menu) $_ = length($item_text); if ($_ > $max_item_len) { $max_item_len = $_; } # Load into arrays and adjust index $menu_sel_text[$menu_index] = $item_text; $menu_sel_action[$menu_index] = $item_sel; $selected_action[$menu_index] = $item_set; $menu_sel_attributes[$menu_index] = $item_attr; $menu_sel_preview[$menu_index] = $item_preview; ++$menu_index; } #********** # MENU_DISPLAY # # Function: Display items in menu_sel_text array, allow selection, and # return appropriate selection-string. # # Call format: $sel = &menu_display("Prompt text",$arrow_line,$top_item); # # Arguments: - Prompt text (for the bottom line of menu). # - Line number offset for the arrow (defaults to zero). # - Index of top item on screen (defaults to zero). # - Column number offset for the arrow (multiple-column mode # only, defaults to zero). # # Returns: Selected action string (from second param on &menu_item) # %UP% -- "u"|"U" pressed (or "t"|"T" and looking for top) # %EMPTY% -- Nothing in menu to display # # Notes: 1) This routine ALWAYS sets "nocbreak" and "echo" terminal # modes before returning. # 2) This routine exits directly (after calling the optional # "quit" routine) if "q"|"Q" is pressed. #********** sub menu_display { ($menu_prompt,$arrow_spec_row,$menu_top_item,$arrow_spec_col) = @_; local($ret); # Check for no "menu_item" calls. $total_items = $#menu_sel_text + 1; if ($total_items <= 0) { &nocbreak(); # ALWAYS turn off "cbreak" mode &echo(); # ALWAYS turn on "echo" return("%EMPTY%"); } &clear(); $xrow = $xcol = 0; $last_menu_top_item = -1; # Force drawing of menu items $ret = &menu_display_internal(0,$menu_prompt,0); if ($#_ > 0) { $_[1] = $arrow_spec_row; } if ($#_ > 1) { $_[2] = $menu_top_item; } if ($#_ > 2) { $_[3] = $arrow_spec_col; } # &menu_return_prep(); $ret; } #********** # MENU_DISPLAY_RADIO # # Function: Display items in "radio-button" style. # # Call format: $sel = &menu_display_radio("Prompt text","current","Done"); # # Arguments: - Prompt text (for the bottom line of menu). # - Current setting (one of the "action-texts" that is the # current setting. # - Text for "done with setting" top menu item # Optional - defaults to "(Accept this setting)" # # Returns: Selected action string (from second param on &menu_item) # %UP% -- "u"|"U" pressed (or "t"|"T" and looking for top) # %EMPTY% -- Nothing in menu to display # # Notes: 1) This routine ALWAYS sets "nocbreak" and "echo" terminal # modes before returning. # 2) This routine exits directly (after calling the optional # "quit" routine) if "q"|"Q" is pressed. #********** sub menu_display_radio { ($menu_prompt,$current,$done_text) = @_; # Check for no "menu_item" calls. $total_items = $#menu_sel_text + 1; if ($total_items <= 0) { &nocbreak(); # ALWAYS turn off "cbreak" mode &echo(); # ALWAYS turn on "echo" return("%EMPTY%"); } &clear(); $xrow = $xcol = 0; $last_menu_top_item = -1; # Force drawing of menu items $arrow_spec_row = $arrow_spec_col = $menu_top_item = 0; # Reset # Insert our top selection item (and adjust max length value) if ($done_text eq "") { $done_text = "(Accept this setting)"; } $_ = length($done_text); if ($_ > $max_item_len) { $max_item_len = $_; } unshift(@menu_sel_text,$done_text); unshift(@menu_sel_action,"%DONE%"); $menu_index++; while (1) { $ret = &menu_display_internal(1,$menu_prompt,$current); last if $ret eq "%DONE%"; if ($ret eq "%UP%") { &menu_return_prep(); return($ret); } $current = $ret; } &menu_return_prep(); $current; } #********** # MENU_DISPLAY_MULT # # Function: Display items in "multiple selection" format. # # Call format: $sel = &menu_display_mult("Prompt text","Done"); # # Arguments: - Prompt text (for the bottom line of menu) # - Text for "done with selections" top menu item # Optional - defaults to "(Done with selections)" # # Returns: Selected action string (from second param on &menu_item) # %UP% -- "u"|"U" pressed (or "t"|"T" and looking for top) # %NONE% -- No items selected # %EMPTY% -- Nothing in menu to display # # Notes: 1) This routine ALWAYS sets "nocbreak" and "echo" terminal # modes before returning. # 2) This routine exits directly (after calling the optional # "quit" routine) if "q"|"Q" is pressed. #********** sub menu_display_mult { ($menu_prompt,$done_text) = @_; local($i,$ret) = 0; # Check for no "menu_item" calls. $total_items = $#menu_sel_text + 1; if ($total_items <= 0) { &nocbreak(); # ALWAYS turn off "cbreak" mode &echo(); # ALWAYS turn on "echo" return("%EMPTY%"); } &clear(); $xrow = $xcol = 0; $last_menu_top_item = -1; # Force drawing of menu items $arrow_spec_row = $arrow_spec_col = $menu_top_item = 0; # Reset # Insert our top selection item (and adjust max length value) if ($done_text eq "") { $done_text = "(Done with selections)"; } $_ = length($done_text); if ($_ > $max_item_len) { $max_item_len = $_; } unshift(@menu_sel_text,$done_text); unshift(@menu_sel_action,"%DONE%"); unshift(@selected_action,0); $menu_index++; # Loop, allowing toggle of selections, until done. while (1) { $ret = &menu_display_internal(2,$menu_prompt,0); last if $ret eq "%DONE%"; if (($ret eq "%UP%") || ($ret eq "%EMPTY%")) { @selected_action = (); &menu_return_prep(); return($ret); } if ($selected_action[$item] >= 0) { if ($selected_action[$item]) { $selected_action[$item] = 0; } #Toggle off else { $selected_action[$item] = 1; } # Toggle on } else { &bell(); } } # Format the return string based on selections. $ret = ""; $i = 1; while ($i < $menu_index) { if ($selected_action[$i] > 0) { $ret .= "$menu_sel_action[$i],"; } $i++; } chop($ret); # Remove final comma @selected_action = (); if ($ret eq "") { $ret = "%NONE%"; } &menu_return_prep(); $ret; } #********** # MENU_GETSTR # # Function: Prompt for a string (allowing editing) from one row of the # screen. # # Call format: $string = &menu_getstr(row,col,"Prompt text",clr_flag, # "Initial value",maxlen,noshow, # data-type,window); # # Arguments: - Row,Col for prompt and data-input. Required. # - Prompt text. Optional. Default=""; # - Boolean flag (0=leave line on exit, 1=clear line on exit) # Optional. Default=0. # - Initial value. Optional. Default="". # - Max length of input field. Optional. # - Boolean flag (0=show,1=hidden) (use "hidden" for passwords) # Optional. Default=0. # - Data-type (0=alphanumeric,1=numeric). Optional. Default=0. # - Window value. Optional. Default=main window. # - Cursor position (not offset). Optional. # Default=end of "Initial value". # # Returns: String (can be null) # # Notes: 1) This routine ALWAYS sets "nocbreak" and "echo" terminal # modes before returning. # 2) This routine checks for the right edge of the screen. #********** sub menu_getstr { local($row,$col,$prompt,$cleanup,$default,$maxlen,$noshow,$dtype,$win,$cpos) = @_; local($prompt_col,$left_col,$action,$string,$i,$overstrike); local ($stars); if (!$win) { $win = $window; } # Use main window by default # Set cbreak, noecho. &cbreak(); &noecho(); # Make sure the default is not longer than the maximum lengths if (($maxlen > 0) && (length($default) > $maxlen)) { $default = substr($default,0,$maxlen); } # Clear our area. Place prompt and any default on the screen &wmove($win,$row,$col); # &wclrtoeol($win); if ($prompt ne "") { &waddstr($win,$prompt); } if ($default ne "") { if ($noshow) { for ($i = 0; $i < length($default); $i++) { &waddstr($win,"*"); } } else { &waddstr($win,$default); } } # Position cursor for data input $prompt_col = $col; $left_col = $prompt_col + length($prompt); if (!$cpos) { $col = $left_col + length($default); } else { $col = $cpos - 1; } &wmove($win,$row,$col); &wrefresh($win); # Set max col to right edge of screen if maxlen passed. # If single character field, allow "overstrike" mode. if ($maxlen == 0) { $maxcol = $main'COLS; } else { $maxcol = $prompt_col + length($prompt) + $maxlen; } if ($maxcol - $left_col == 1) { $overstrike = 1; } else { $overstrike = 0; } $string = $default; if (!$cpos) { $i = length($string); } else { $i = $cpos - 1; } $menu_lastexit = "\n"; # Perform editing until "Return" pressed. while (1) { $action = &collect_seq($win); if ($#menu_exitkeys >= 0) { &check_exit_keys(); } # Check any exit keys if (($action eq $kr) || ($action eq $ansi_kr) || ($action eq $next_char) ) { # Right-arrow if ($i+1 > length($string)) { &bell(); } else { $col++; $i++; &wmove($win,$row,$col); } } elsif (($action eq $kl) || ($action eq $ansi_kl) || ($action eq $prev_char) ) { # Left-arrow if ($i-1 < 0) { &bell(); } else { $col--; $i--; &wmove($win,$row,$col); } } elsif (($action eq "\177") || ($action eq "\010")) { # Delete/BS if ($i-1 < 0) { &bell(); } else { $col--; $i--; &wmove($win,$row,$col); &wdelch($win); $string = substr($string,0,$i).substr($string,$i+1); } } elsif ($action eq $delete_right) { # Delete right if ($i < length($string)) { &wdelch($win); $string = substr($string,0,$i).substr($string,$i+1); } else { &bell(); } } elsif (($action eq $cr) || ($action eq $nl) || ($action eq "\n")) { # Terminate $last_window_cpos = $col + 1; # Save current column (not offset) if ($cleanup) { &wmove($win,$row,$prompt_col); # Clear our stuff &wclrtoeol($win); &wrefresh($win); } &nocbreak(); # ALWAYS turn off "cbreak" mode &echo(); # ALWAYS turn on "echo" return($string); } elsif (($action eq $ku) || ($action eq $ansi_ku) || ($action eq $next_field) || ( $action eq $prev_field ) || ($action eq $kd) || ($action eq $ansi_kd)) { ; # Ignore } elsif ($action eq $begin_of_line) { # Go to begin-of-line $col = $prompt_col + length($prompt); $i=0; &wmove($win,$row,$col); } elsif ($action eq $end_of_line) { # Go to end-of-line $col = $prompt_col + length($prompt) + length($string); $i = length($string); &wmove($win,$row,$col); } elsif ($action eq $kill_line) { # Delete to end-of-line if ($i != length($string)) { # Not at end of line &wclrtoeol($win); $kill_buffer = substr($string,$i); $string = substr($string,0,$i) } else { &bell(); } } elsif ($action eq $yank_line) { # Paste killed text # Now it "does the right thing" in numeric-only # and hidden fields if (length($kill_buffer) != 0) { # Check for non-numeric kill_buffer in numeric-only field if ($dtype == 1 && $kill_buffer !~ m/^\d+$/ ) { &bell(); } elsif ($overstrike) { # If single-character field cannot yank a multi-character field if (length($kill_buffer) != 1) { &bell(); } else { # Delete single-character field &wmove($win,$row,$col); &wdelch($win); # Yank single-character kill_buffer &winsch($win,ord($kill_buffer)); $string = $kill_buffer; } } elsif ($left_col + length($string) + length($kill_buffer) > $maxcol) { # Yanking kill_buffer will make field too long &bell(); } else { if ($noshow) { $stars = '*' x length($kill_buffer); # Draw yanked text &waddstr($win,$stars."\0"); # Draw rest of string $stars = '*' x length(substr($string,$i)); &waddstr($win,$stars."\0"); } else { # Draw yanked text &waddstr($win,$kill_buffer."\0"); # Draw rest of string &waddstr($win,substr($string,$i)."\0"); } $string = substr($string,0,$i) . $kill_buffer . substr($string,$i); $col += length($kill_buffer); $i += length($kill_buffer); &wmove($win,$row,$col); } } } else { # Any other character if ($overstrike) { # Delete only char on single-char field &wmove($win,$row,$col); &wdelch($win); $string = ""; } if ($left_col + length($string) + 1 > $maxcol) { &bell(); } else { if (($dtype == 1) && (index("0123456789",$action) < 0)) { &bell(); } else { &wmove($win,$row,$col); # Insert the character on the screen if ($noshow) { &winsch($win,ord("*")); } else { &winsch($win,ord($action)); } $string = substr($string,0,$i).$action.substr($string,$i); if (!$overstrike) { $col++; $i++; &wmove($win,$row,$col); } } } } &wrefresh($win); } } #********** # MENU_DISPLAY_INTERNAL # # Function: Display items in menu_sel_text array, allow selection, and # return appropriate selection-string. # # Call format: $sel = &menu_display_internal([0|1|2],"Prompt text", # $arrow_pos,$top_item,"current"); # # Arguments: - Menu type (0=simple, 1=radio, 2=multiple-select). # - Prompt text (for the bottom line of menu). # - Current selection (radio only). # # Returns: Selected action string (from second param on &menu_item) # %UP% -- "u"|"U" pressed (or "t"|"T" and looking for top) # %EMPTY% -- Nothing in menu to display # %DONE% -- Done with selections (radio and mult only) #********** sub menu_display_internal { ($menu_type,$menu_prompt,$radio_sel) = @_; local($i,$search,$low_sel,$do_scroll,$move_amt); # If looking for top menu, return with "%UP%". if ($finding_top) { if ($menu_is_top_one) { $finding_top = 0; } else { return("%UP%"); } } # Check for no "menu_item" calls. $total_items = $#menu_sel_text + 1; if ($total_items <= 0) { &nocbreak(); # ALWAYS turn off "cbreak" mode &echo(); # ALWAYS turn on "echo" return("%EMPTY%"); } &cbreak(); # cbreak mode (each character available) &noecho(); # Menus are always "noecho" # Compute prepend length (for stuff we prepend to each selection text) $prepend_len = 0; # Assume nothing if (!$highlight && $menu_numbered) { $prepend_len += 2; } # Adjust for "->" if ($menu_numbered) { $prepend_len += 6; } # Adjust for "nnnn) " if ($menu_type) { $prepend_len += 4; } # Adjust for "[X] " # Calculate items per line and items per screen (based on mult-column pref) if ($multiple_column) { $column_width = $prepend_len + $max_item_len + 1; # Always pad one blank $items_per_line = int(($main'COLS-1)/$column_width); if ($items_per_line <= 0) { $items_per_line = 1; } } else { $column_width = $prepend_len + $max_item_len; $items_per_line = 1; } $items_per_screen = ($last_line - $first_line + 1) * $items_per_line; if ($total_items <= $items_per_screen) { $menu_single_page = 1; } else { $menu_single_page = 0; } if ($menu_prompt eq "") { $menu_prompt = "h)elp"; if ($menu_item_help_routine ne "") { $menu_prompt .= " ?)item-help"; } if (!$disable_quit) { $menu_prompt .= " q)uit"; } $menu_prompt .= " u)p"; $menu_prompt .= " m)atch-text"; if ($menu_top_activated) { if (! $menu_is_top_one) { $menu_prompt .= " t)op"; } } if ($menu_type == 2) { $menu_prompt .= " a)ll c)lear"; } if (! $menu_single_page) { $menu_prompt .= " N)ext-pg P)rev-pg"; } $menu_prompt .= " b)egin e)nd"; if (length($menu_prompt)+9 < $main'COLS - 7) { $menu_prompt .= " re(f)resh"; } } if (length($menu_prompt) > $main'COLS - 7) { $menu_prompt = substr($menu_prompt,0,$main'COLS - 7); } # Validate/adjust paramaters. $arrow_line = $arrow_spec_row + $first_line; if ($menu_top_item + 1 > $total_items) { $menu_top_item = $total_items - 1; } if ($arrow_line < $first_line) { $arrow_line = $first_line; } if ($arrow_line > $last_line) { $arrow_line = $last_line; } $arrow_col = $arrow_spec_col; if ($arrow_col < 0) { $arrow_col = 0; } elsif ($arrow_col > $items_per_screen - 1) { $arrow_col = $items_per_screen - 1; } # Compute leftmost column (for left-justified or centered menus) $left_margin = 0; # Assume left-justified menu (no centering) if ($center_menus) { $left_margin = int($main'COLS/2) - int($column_width/2) * items_per_line; } # Clear screen and add top title and bottom prompt &menu_top_bot(); $move_amt = 0; $number = 0; $menu_lastexit = "\n"; while (1) { $number_shown = $menu_top_item + $items_per_screen; if ($number_shown > $total_items) { $number_shown = $total_items; } $percent = int($number_shown * 100 /$total_items); &menu_page(); # Display current page # Collect key sequences until something we recoginize # (or we know we don't care) $action = &collect_seq($window); if ($#menu_exitkeys) { &check_exit_keys(); } # Check any exit keys # If trying to be more "gopher-like", translate now. if ($gopher_like && ($items_per_line == 1)) { if (($action eq $kr) || ($action eq $ansi_kr)) { $action = "\n"; } if (($action eq $kl) || ($action eq $ansi_kl)) { $action = "u"; } } # Perform action based on keystroke(s) received $move_amt = 0; if ($action ne "") { $last_arrow_line = $arrow_line; $last_arrow_col = $arrow_col; if (($action eq $kd) || ($action eq $ansi_kd)) { # Down-arrow $number = 0; $do_scroll = 1; if ($items_per_line > 1) { if (($arrow_line+1 >= $max_sel_line) && ($arrow_col > $max_sel_col)) { $do_scroll = 0; } } if ($do_scroll) { if ($arrow_line < $max_sel_line) { $arrow_line++; } else { if ($gopher_like) { $move_amt = $items_per_screen; $arrow_line = $first_line; } else { if ($arrow_line == $last_line) { $move_amt = $items_per_line; } } } } } if (($action eq $next_field) || ($action eq $kr) || ($action eq $ansi_kr || $action eq 'n')) { # Right-arrow $number = 0; $do_scroll = 1; if ($items_per_line > 1) { if (($arrow_line == $max_sel_line) && ($arrow_col == $max_sel_col)) { $item = $menu_top_item+($arrow_line-$first_line)*$items_per_line+$arrow_col; if (($item == $menu_index-1) && !$gopher_like) { $do_scroll = 0; } } else { if ($arrow_col + 1 < $items_per_line) { $arrow_col++; $do_scroll = 0; } } } if ($do_scroll) { $arrow_col = 0; if ($arrow_line < $max_sel_line) { $arrow_line++; } else { if ($gopher_like) { $move_amt = $items_per_screen; $arrow_line = $first_line; } else { if ($arrow_line == $last_line) { $move_amt = $items_per_line; } } } } } elsif (($action eq $ku) || ($action eq $ansi_ku)) { # Up-arrow $number = 0; if ($arrow_line > $first_line) { $arrow_line--; } else { if ($gopher_like) { $move_amt = -$items_per_screen; if ($menu_top_item + $move_amt < 0) { # Moving before 1st item if ($menu_top_item > 0) { # Not currently on 1st page $arrow_line = $menu_top_item + $arrow_line - 1; # Adjust arrow } else { $arrow_line = $last_line; } } else { $arrow_line = $last_line; } } else { $move_amt = -$items_per_line; } } } elsif (($action eq $prev_field) || ($action eq $kl) || ($action eq $ansi_kl || $action eq 'p')) { # Left-arrow $number = 0; $do_scroll = 1; if ($items_per_line > 1) { if ($arrow_col > 0) { $arrow_col--; $do_scroll = 0; } else { if (($arrow_line > $first_line) || ($menu_top_item > 0)) { $arrow_col = $items_per_line - 1; } else { if ($gopher_like) { $arrow_col = $max_sel_col; } else { $do_scroll = 0; } } } } if ($do_scroll) { if ($arrow_line > $first_line) { $arrow_line--; } else { if ($gopher_like) { $move_amt = -$items_per_screen; if ($menu_top_item + $move_amt < 0) { # Moving before 1st item if ($menu_top_item > 0) { # Not currently on 1st page $arrow_line = $menu_top_item + $arrow_line - 1; # Adjust arrow } else { $arrow_line = $last_line; } } else { $arrow_line = $last_line; } } else { $move_amt = -$items_per_line; } } } } elsif (($action eq "n") || ($action eq "N") || # Next ($action eq " ")) { $number = 0; $move_amt = $items_per_screen; } elsif (($action eq "b") || ($action eq "B")) { # Begin $menu_top_item = 0; $arrow_line = $first_line; $arrow_col = 0; $number = 0; } elsif (($action eq "e") || ($action eq "E")) { # End $number = 0; if (! $menu_single_page) { $menu_top_item = $menu_index - $items_per_screen; } $arrow_line = $last_line; $arrow_col = $max_sel_col; } elsif (($action eq "p") || ($action eq "P")) { # Previous $number = 0; $move_amt = -$items_per_screen; } elsif (($action eq "a") || ($action eq "A")) { # Select all if ($menu_type == 2) { $i = 1; while ($i < $menu_index) { if ($selected_action[$i] >= 0) { $selected_action[$i] = 1; } $i++; } } } elsif (($action eq "c") || ($action eq "C")) { # Clear all if ($menu_type == 2) { $i = 1; while ($i < $menu_index) { if ($selected_action[$i] >= 0) { $selected_action[$i] = 0; } $i++; } } } elsif (($action eq "m") || ($action eq "M") || # Match string ($action eq "=") || ($action eq "/")) { # if ($menu_type == 2) { $search = &menu_getstr($last_line+1,0,"Search string: ",1); &cbreak(); # menu_getstr turned this off &noecho(); # menu_getstr turned this on if ($search) { # Toggle selections # $search =~ tr/A-Z/a-z/; # $i = 1; $item = $menu_top_item+($arrow_line-$first_line)*$items_per_line+$arrow_col; @order = ((($item + 1) .. $#menu_sel_text), (0 .. $item)); foreach $i (@order) { if ($menu_sel_text[$i]=~ /$search/i) { if ($i > $menu_top_item && $i <= $menu_bot_item) { $arrow_line = ($i - $menu_top_item + $first_line), last; } $old_arrow_line = $arrow_line; $arrow_line = $first_line; $move_amt = ($i - $item + ($old_arrow_line - $first_line)), last; } } # while ($i < $menu_index) { # if ($selected_action[$i] >= 0) { # print "$i "; # $low_sel = $menu_sel_text[$i]; # $low_sel =~ tr/A-Z/a-z/; # if (index($low_sel,$search) >=0) { $selected_action[$i] = 1; } # } # $i++; # } # } } } elsif (($action eq "f") || ($action eq "F") || ($action eq $redraw_screen)) { # Refresh # &clear(); # &menu_top_bot(); # $last_menu_top_item = -1; # &menu_page(); return('refresh'); } elsif (($action eq "\177") || ($action eq "\010")) { # Delete/BS num-reset $number = 0; $arrow_line = $first_line; &menu_page(); } elsif ((($action eq "Q")||($action eq "q")) && !$disable_quit) { # Quit if ($prompt_for_quit) { &clear(); $xrow = $xcol = 0; &move(0,0); if ($quit_prompt eq "") { $ch = "Do you really want to quit?"; } else { $ch = $quit_prompt; } $ch .= " $quit_default"; &addstr($ch); &move(0,length($ch) - 1); &refresh(); $ch = &getch(); if ($ch eq "") { &menu_hangup_proc(); } if (($ch eq $cr) || ($ch eq $nl) || ($ch eq "\n")) { $ch = $quit_default; } $ch =~ tr/A-Z/a-z/; if ($ch eq "y") { &menu_return_prep(); if ($menu_exit_routine ne "") { &$menu_exit_routine(); } exit(0); } &clear(); $xrow = $xcol = 0; &menu_top_bot(); # Re-display current page $last_menu_top_item = -1; &menu_page(); } else { if ($menu_exit_routine ne "") { &$menu_exit_routine(); } &menu_return_prep(); exit; } } elsif (($action eq "U") || ($action eq "u")) { # Up unless ($menu_is_top_one) { $finding_top = 0; return("%UP%"); } } elsif (($action eq "T") || ($action eq "t")) { # Top if ($menu_top_activated && !$menu_is_top_one) { $finding_top = 1; return("%UP%"); } } elsif (($action eq "h") || ($action eq "H") || # Help ($action eq "?")) { if (($action eq "?") && ($menu_item_help_routine ne "")) { if ($number) { $item = $number - 1; } else { $item = $menu_top_item+($arrow_line-$first_line)*$items_per_line+$arrow_col; } if (($item < $menu_top_item) || ($item > $menu_bot_item)) { &bell(); $number = 0; } else { &$menu_item_help_routine($menu_sel_text[$item],$menu_sel_action[$item]); &clear(); &refresh(); } } else { &$menu_generic_help_routine(); # Show generic help page &clear(); $xrow = $xcol = 0; &refresh(); } &menu_top_bot(); # Clear and re-display the current page $last_menu_top_item = -1; &menu_page(); } elsif ($action eq "!") { # Shell escape if ($menu_shell_text ne "") { &clear(); $xrow = $xcol = 0; # Clear screen &refresh(); &nocbreak(); # ALWAYS turn off "cbreak" mode &echo(); # ALWAYS turn on "echo" $xrow = $xcol = 0; &print_nl("Entering command shell via \"$menu_shell_text\".",1); &print_nl("Return here via shell exit (normally Control-D).",1); &refresh(); &endwin(); system($menu_shell_text); &cbreak(); # cbreak mode (each character available) &noecho(); # Menus are always "noecho" &clear(); $xrow = $xcol = 0; # Clear screen &menu_top_bot(); # Re-display the current page $last_menu_top_item = -1; &menu_page(); } } elsif (($action eq $cr) || ($action eq $nl) || ($action eq "\n")) { # RETURN if ($number) { $item = $number - 1; } else { $item = $menu_top_item+($arrow_line-$first_line)*$items_per_line+$arrow_col; } if (($item < $menu_top_item) || ($item > $menu_bot_item)) { &bell(); $number = 0; } else { $arrow_spec_row = $arrow_line - $first_line; $arrow_spec_col = $arrow_col; return($menu_sel_action[$item]); } } else { # die (keys %custom_keys); foreach (keys %custom_keys) { if ($action eq $_) { # die "help me\n"; if ($number) { $item = $number - 1; } else { $item = $menu_top_item+($arrow_line-$first_line)*$items_per_line+$arrow_col; } if (($item < $menu_top_item) || ($item > $menu_bot_item)) { &bell(); $number = 0; } else { $arrow_spec_row = $arrow_line - $first_line; $arrow_spec_col = $arrow_col; return("***$custom_keys{$_} $menu_sel_action[$item]"); } } } foreach (keys %custom_hotkeys) { if ($action eq $_) { # die (unpack "B*", "$action"); return("$custom_hotkeys{$_}"); } } $digit_val = index("0123456789",$action); # Number if ($digit_val >= 0) { $number = $number * 10 + $digit_val; if ($number >= $menu_top_item + 1) { if (($number <= $menu_bot_item + 1) && ($number <= $total_items)) { if ($items_per_line > 1) { $i = $number - $menu_top_item - 1; $arrow_line = int($i/$items_per_line) + $first_line; $arrow_col = $i % $items_per_line; } else { $arrow_line = $number - $menu_top_item + $first_line - 1; } } else { &bell(); $number = 0; $arrow_line = $first_line; $arrow_col = 0; } &menu_page(); } } } # Check for paging/scrolling of the menu text. # Key variable to set is "menu_top_item". if ($move_amt != 0) { if ($move_amt < 0) { # Move backward if ($menu_top_item + $move_amt < 0) { # Moving before 1st item if ($gopher_like) { if ($menu_top_item > 0) { # Not currently on 1st page $menu_top_item = $menu_top_item + $move_amt; } else { # Currently on 1st page $menu_top_item = $total_items - $items_per_screen; } if ($menu_top_item < 0) { $menu_top_item = 0; } } else { $menu_top_item = 0; } } else { $menu_top_item = $menu_top_item + $move_amt; } } else { # Move forward if (($menu_top_item + $move_amt < $total_items) && ($menu_bot_item + 1 < $total_items)) { $menu_top_item = $menu_top_item + $move_amt; } else { if ($gopher_like) { $menu_top_item = 0; } } } } if ($preview) { $item = $menu_top_item+($last_arrow_line-$first_line)*$items_per_line+$last_arrow_col; &move($first_line-2,0); &clrtoeol(); &attron(A_BOLD); &addstr("$menu_sel_preview[$item]"); &attroff(A_BOLD); } # Reset last selection to normal rendition or clear last arrow if ($highlight) { $item = $menu_top_item+($last_arrow_line-$first_line)*$items_per_line+$last_arrow_col; $i = $left_margin+$prepend_len+$last_arrow_col*($column_width); if ($menu_type && !$item) { $i -= 4; } # No "[X] " on first item &move($last_arrow_line,$i); attron($menu_sel_attributes[$item]); &addstr($menu_sel_text[$item]); attroff($menu_sel_attributes[$item]); &move($last_arrow_line,$i); } else { if ($menu_numbered) { &move($last_arrow_line,$left_margin+$last_arrow_col*($column_width)); &addstr(" "); } } } } } #********* # CHECK_EXIT_KEYS # # Function: Check for user-define exit keys on menu and getstr. # # Input: Nothing # # Returns: Nothing (action and lastexit set) # #********* sub check_exit_keys { local($j); for ($j = 0; $j <= $#menu_exitkeys; $j++) { if ($action eq $menu_exitkeys[$j]) { $menu_lastexit = $action; $action = "\n"; last; } } } #********** # COLLECT_SEQ -- Collect characters until a sequence we recognize (or we # know it cannot possibly fit any "magic" sequences. #********** sub collect_seq { local($cwin) = @_; local($i,$possible); local($collect,$action) = ""; $possible = $#magic_seq; # Set number of possible matches seq_seek: while ($possible > 0) { $ch = &wgetch($cwin); if ($ch eq "") { &menu_hangup_proc(); } $collect = $collect.$ch; $i = 0; $possible = 0; $action = $ch; try: while ($i <= $#magic_seq) { if (length($collect) > length($magic_seq[$i])) { $i++; next try; } if (substr($magic_seq[$i],0,length($collect)) eq $collect) { $possible++; if ($collect eq $magic_seq[$i]) { $action = $magic_seq[$i]; last seq_seek; } } $i++; } # end while } $action; } #********** # MENU_TOP_BOT -- Display top and bottom lines of current menu #********** sub menu_top_bot { local($i,$j,$temp); # Main top title &move(0,$menu_top_title_col); if ($menu_top_title_attr == 0) { &standout(); } &addstr($menu_top_title); if ($menu_top_title_attr == 0) { &standend(); } # Top sub-titles if ($menu_sub_titler ne "") { $temp = &$menu_sub_titler; # Expand title string $first_line = 2; # Assume no sub-titles for now $last_line = $main'LINES - 3; # Assume no bottom-titles for now if ($temp ne "") { $first_line += &proc_titles($temp,*menu_sub_title, *menu_sub_title_attr,*menu_sub_title_col); } else { @menu_sub_title = (); } } if ($#menu_sub_title >= 0) { for ($i = 0; $i <= $#menu_sub_title; $i++) { &move($i+1,$menu_sub_title_col[$i]); if ($menu_sub_title_attr[$i] == 0) { &standout(); } &addstr($menu_sub_title[$i]); if ($menu_sub_title_attr[$i] == 0) { &standend(); } } } # Bottom sub-titles if ($menu_bot_titler ne "") { $temp = &$menu_bot_titler; if ($temp ne "") { $last_line = $main'LINES - 3; $last_line -= &proc_titles($temp,*menu_bot_title, *menu_bot_title_attr,*menu_bot_title_col); $last_line--; # Blank line between menu and bottom titles } else { @menu_bot_title = (); } } if ($#menu_bot_title >= 0) { $j = $main'LINES - $#menu_bot_title - 3; for ($i = 0; $i <= $#menu_bot_title; $i++) { &move($j+$i,$menu_bot_title_col[$i]); if ($menu_bot_title_attr[$i] == 0) { &standout(); } &addstr($menu_bot_title[$i]); if ($menu_bot_title_attr[$i] == 0) { &standend(); } } } $items_per_screen = ($last_line - $first_line + 1) * $items_per_line; &move($main'LINES - 1,7); &addstr($menu_prompt); } #********** # MENU_PAGE -- Display one page of menu selection items. #********** sub menu_page { local($i,$j) = 0; local($curr_line,$line,$fx,$iw); local($refresh_items) = 0; # Check for top item change (scrolling/paging of menu text). if ($menu_top_item != $last_menu_top_item) { $last_menu_top_item = $menu_top_item; $refresh_items = 1; } # Refresh all items on screen if ($refresh_items) { # Update percentage on bottom line &move($main'LINES-1,0); &standout(); if ($menu_single_page) { &addstr("(All) "); } else { &addstr(sprintf("\(%3d%%\)",$percent)); } &standend(); # Display current page of menu $item = $menu_top_item; $menu_bot_item = $menu_top_item; $curr_line = $first_line; $max_sel_line = $first_line; $max_sel_col = 0; while ($curr_line <= $last_line) { # Process lines on screen &move($curr_line,$left_margin); &clrtoeol(); $line = ""; if ($item < $total_items) { # If any items left to show ... $curr_col = 1; while ($curr_col <= $items_per_line) { # Add items to line if ($item < $total_items) { if ($menu_numbered) { $line .= &menu_add_number($item + 1); } if ($menu_type) { # Add selection boxes on mult/radio if ($item != 0) { if ($menu_type == 1) { if ($menu_sel_action[$item] eq $radio_sel) { $line .= "[X] "; } else { $line .= "[ ] "; } } else { # menu_type is 2 if ($selected_action[$item] > 0) { $line .= "[X] "; } elsif ($selected_action[$item] < 0) { $line .= "[-] "; } else { $line .= "[ ] "; } } } } $line .= $menu_sel_text[$item]; # Add the selection text $attribute = ''; $attribute .= $menu_sel_attributes[$item]; if ($items_per_line > 1) { # Pad out if multiple columns $i = 1; # Always pad one if ($menu_type && !$item) { $i += 4; } # Missing "[ ] " $line .= ' ' x ($max_item_len-length($menu_sel_text[$item])+$i); } $max_sel_col = $curr_col - 1; $item++; } $curr_col++; } if (length($line) > $main'COLS - 1) { # Truncate lines that would wrap $line = substr($line,0,$main'COLS - 1); } attrset(0); attron($attribute); &addstr("$line"); attroff($attribute); $max_sel_line = $curr_line; } $curr_line++; } $menu_bot_item = $item - 1; } # Refresh only selection tags on radio/multi-select menus elsif ($menu_type) { $item = $menu_top_item; $curr_line = $first_line; while ($curr_line <= $last_line) { $i = $left_margin + $prepend_len - 3; # First "X" on line if ($item < $total_items) { for ($j = 0; $j < $items_per_line; $j++) { if ($item) { &move($curr_line,$i); if ($menu_type == 1) { if ($menu_sel_action[$item] eq $radio_sel) { &addstr("X"); } else { &addstr(" "); } } else { # menu_type is 2 if ($selected_action[$item] > 0) { &addstr("X"); } elsif ($selected_action[$item] < 0) { &addstr("-"); } else { &addstr(" "); } } } $i += $column_width; # Next "X" on line $item++; # Next item } } $curr_line++; # Next line } } # Sanity checks for arrow if ($arrow_line < $first_line) { $arrow_line = $first_line; } if ($arrow_line > $max_sel_line) { $arrow_line = $max_sel_line; } # Highlight selection text or add selection arrow (based on prefs). # Position the cursor properly on the screen. if ($preview) { $item = $menu_top_item+($arrow_line-$first_line)*$items_per_line+$arrow_col; &move($first_line-2,0); &clrtoeol(); &attron(A_BOLD); &addstr("$menu_sel_preview[$item]"); &attroff(A_BOLD); } if ($highlight) { $item = $menu_top_item+($arrow_line-$first_line)*$items_per_line+$arrow_col; $i = $left_margin+$prepend_len+$arrow_col*($column_width); if ($menu_type && $item == 0) { $i -= 4; } # No "[X] " on first item $thing = $menu_sel_text[$item]; if (length($thing) > $main'COLS - 1) { # Truncate lines that would wrap $thing = substr($thing,0,$main'COLS - 1); } &move($arrow_line,$i); &standout(); attron($menu_sel_attributes[$item]); &addstr($thing); attroff($menu_sel_attributes[$item]); &standend(); &move($arrow_line,$i); } else { &move($arrow_line,$left_margin+$arrow_col*$column_width); if ($menu_numbered) { &addstr("->"); } } # Write out current menu page &refresh(); } #********** # MENU_ADD_NUMBER -- Format selection number. #********** sub menu_add_number { local($sel_num) = @_; local($sel_str) = ""; if (!$highlight) { $sel_str = " "; } # Place for "->" if ($sel_num < 1000) { $sel_str .= " "; } if ($sel_num < 100) { $sel_str .= " "; } if ($sel_num < 10) { $sel_str .= " "; } $sel_str .= "$sel_num) "; $sel_str; } #********** # MENU_RETURN_PREP -- Common return functions. #********** sub menu_return_prep { &nocbreak(); &echo(); &clear(); $xrow = $xcol = 0; &refresh(); if (!$curses_application) { &endwin(); } } #********** # MENU_HANGUP_PROC -- Hangup return functions #********** sub menu_hangup_proc { if ($menu_exit_routine ne "") { &$menu_exit_routine(); } exit(1); } #********** # MENU_DEFAULT_SHOW_HELP #********* sub menu_default_show_help { local($arrow_txt); &clear(); $xrow = $xcol = 0; &print_nl("---------------------------------------",1); &print_nl(" Menu Help (FATSI V1.0)",1); &print_nl("---------------------------------------",2); if ($items_per_line > 1) { $arrow_txt = "up/down/left/right"; } else { $arrow_txt = "up/down"; } if ($highlight) { &print_nl("- Use $arrow_txt arrow keys to highlight your selection.",1); } else { &print_nl("- Use $arrow_txt arrows to place \"->\" in front of your selection.",1); &print_nl("- Use right/left arrows to previous/next menus respectively.",1); } if ($menu_type == 1) { &print_nl("- Press Return (or Enter) to choose that selection.",1); &print_nl("- Select the first item when ready to continue.",2); } elsif ($menu_type == 2) { &print_nl("- Press Return (or Enter) to toggle the selection on/off.",1); &print_nl("- Select the first item when ready to continue.",2); } else { &print_nl("- Press Return (or Enter) when ready to continue.",2); } &print_nl("Available action-keys:",1); &print_nl("h - Show this help screen.",1); if ($menu_item_help_routine ne "") { if ($highlight) { &print_nl("? - Show help on the item with the \"->\" in front.",1); } else { &print_nl("? - Show help on the highlighted item.",1); } } if (!$disable_quit) { &print_nl("q - Quit entirely.",1); } &print_nl("u - Return to the previous menu or function.",1); if ($menu_top_activated && !$menu_is_top_one) { &print_nl("t - Return to the top menu.",1); } &print_nl("n - Next item in the list.",1); &print_nl("p - Previous item in the list.",1); if ($menu_type == 2) { &print_nl("a - Select all items.",1); &print_nl("m - Select based on a case-insensitive string match.",1); &print_nl("c - Clear all selections.",1); } if (! $menu_single_page) { &print_nl("N - Move forward to next page.",1); &print_nl("P - Move backward previous page.",1); } &print_nl("b - Move to the item at the beginning of the menu.",1); &print_nl("e - Move to the item at the end of the menu.",1); &print_nl("f - Refresh the screen.",1); if ($menu_shell_text ne "") { &print_nl("! = Enter command shell via \"$menu_shell_text\".",1); } &print_nl(" ",1); &addstr("[Press any key to continue]"); &refresh(); $ch = &getch(); if ($ch eq "") { &menu_hangup_proc(); } &clear(); $xrow = $xcol = 0; &refresh(); } sub print_nl { local($text,$skip) = @_; &addstr($text); if ($skip) { &nl($skip); } &refresh(); } sub nl { local($skip) = @_; $xrow += $skip; $xcol = 0; if ($xrow > $main'LINES - 1) { &clear(); $xrow = 0; $xcol = 0; &refresh(); } &move($xrow,$xcol); } sub defbell { eval q# sub bell { print "\007"; } #; } #********** # MENU_TEMPLATE_SETEXIT # # Function: Set alternative exit keys that may be used to exit a template # # Call format: &menu_template_setexit(@exit_key_array); # OR # &menu_template_setexit("exit_seq1","exit_seq2",...); # # Arguments: exit_key_array - the keys to end menu_display_template on # # Returns: Nothing #********* sub menu_template_setexit { @template_exitkeys = @_; } #********** # MENU_LOAD_TEMPLATE # # Function: Load screen-input template from a file for later processing. # # Input: Filename # # Returns: 0=Success, 1=Cannot open file #********** sub menu_load_template { local($filename) = @_; &menu_load_template_init_internal; # Load the template open(TEMPLATE,$filename) || return(1); while(