Location: PHPKode > scripts > Dialog for PHP > dialog-for-php/dialog.php
<?php

/**
 * PHP Wrapper around dialog command
 *
 * @todo dialogrc
 * @package dialog4php
 * @version 0.1
 * @author Ivan Andrus <hide@address.com>
 * @license PHP
 */

/**
 * Abstract from which all other dialogs inherit.
 *
 * @package dialog4php
 */
class Dialog {

   // properties
   
   /**
    * General options as associative array.  See various {@link get}
    * and {@link set} member functions.
    * @access private
    */
   var $_options = array("clear" => true);
   
   /**
    * User input from dialog.
    * @see getOutput
    * @access private
    */
   var $_output = array();
   /**
    * Whether the Dialog returned successfully.
    * @see getReturnValue
    * @access private
    */
   var $_ret_val = 0;

   /**
    * A dialog "parent" used to inherit settings.
    * @access private
    */
   var $_parent = null;
   
   /**
    * Height of the dialog
    * @see GetHeight
    * @see SetHeight
    * @access private
    */
   var $_height = 0;

   /**
    * Width
    * @see GetWidth
    * @see SetWidth
    * @access private
    */
   var $_width = 0;

   /**
    * Generic Text (usu. instructions), for the dialog to display.
    *
    * @access private
    */
   var $_text;

   /**
    * What type of dialog box to create, e.g., menubox, yesno, ...
    * @access private
    */
   var $_command_name;
   /**
    * The name of the command to run, e.g., dialog, whiptail, ...
    * @access private
    */
   var $_program_name="dialog";

   /**
    * This is a generic constructor.
    *
    * <pre>
    * The Constructor expects 1 to 2 arguments:
    * - The first argument is the text of the dialog to be displayed
    * - The second argument is another Dialog object from which to inherit
    * </pre>
    *
    * @param string $text The text (usu. instructions) string
    * @param mixed $parent Dialog from which to inherit settings
    */ 
   function Dialog($text, $parent=null)
      {
         $this->_text=$text;
         $this->_parent = $parent;
      }
   
   /**
    * Return type-specific options.
    *
    * Returns an associative array of options specific to the type of
    * Dialog that it is.  This is for creating the commandline to run.
    *
    * @access private
    *
    * @return array
    */
   function getTypeSpecific() 
      {
         return array( $this->_command_name, $this->_text, $this->_height, $this->_width);
      }
   
   /**
    * Run the dialog.
    *
    * Shows the dialog and returns the return status of the dialog.
    *
    * @see getReturnValue
    * @see getOutput
    * @return bool
    */
   function Show()
      {
         $cmdLine = $this->_program_name;
         // $cmdLine = "whiptail";
         
         foreach ( $this->getOptions() AS $k => $v ) {
            if ( $v !== null && $v !== false ) {
               if ( $v === true ) {
                  $cmdLine .= " --$k";
               } else {
                  $cmdLine .= " --$k \"" . addslashes($v) . "\"";
               }
            }
         }
         
         foreach(($this->getTypeSpecific()) AS $arg)
            {
               // Perhaps not escape file paths??
               $cmdLine .= " \"" . addslashes($arg) . "\"";
            }
         $cmdLine .= " 2>&1";
         
         //exec( $cmdLine, $this->_output, $this->_ret_val );
         //system( $cmdLine, $this->_ret_val );
         print $cmdLine;

         return $this->getReturnValue();
      }

   /**
    * Get the return value of the last show().
    *
    * `true` means okay or yes was pressed, `false` means no or
    * cancel, and `null` means escape was pressed or some error
    * occured. The user input from the dialog can be obtained with
    * getOutput().
    *
    * @see show
    * @return bool
    */
   function getReturnValue()
      {
         if( $this->_ret_val == 1){
            return false;
         } elseif( $this->_ret_val == 0){
            return true;
         } else {
            return null;
         }
      }
   
   /**
    * Returns true if escape was pressed during the last show().
    *
    * @see show
    * @see getReturnValue
    * @return bool
    */
   function wasEscaped()
      {
         return ( $this->_ret_val == -1);
      }

   /**
    * Returns true if the dialog was cancelled (not escape was
    * pressed) during the last show().
    *
    * @see show
    * @see getReturnValue
    * @return bool
    */
   function wasCancelled()
      {
         return ( $this->_ret_val == 1 );
      }
   
   /**
    * Return user input.
    *
    * Returns the user input of the last show(), specific to what type
    * of Dialog it is.  e.g. YesNo will return a boolean and CheckList
    * will return an array of strings.
    *
    * The default is to return only one value as a string
    *
    * @return mixed
    */
    function getOutput()
      {
         return implode("\n", $this->_output);
      }
    
   /**
    * Return the dimensions of the screen.
    * 
    * @return array(int) $dims array(int height, int width)
    */
   function getMaxDims()
      {
         $dims = explode(" ", `dialog --print-maxsize 2>&1`);
         return array( (int)$dims[1], (int)$dims[2]);
      }
   
   /**
    * Sets the dimensions of the Dialog to the maximum screen size.
    * 
    */
   function setMaxDims()
      {
         $dims = $this->getMaxDims();
         $this->_height = $dims[0];
         $this->_width  = $dims[1];
      }
   
   
   // The setting of many options
   /**
    * Set the main text of the dialog--usually the instructions.
    *
    * @param string $text The text to use as the main text
    */
   function setMainText( $text )    { $this->_text = $text; }
   /**
    * Set the height of the dialog in lines.
    *
    * A value of 0 means that dialog should auto-size the dialog.
    * Unfortunately whiptail segfaults when passed height and width of 0
    * (on my system).
    *
    * @see getHeight
    * @param int $height The height in lines 0 = automatic
    */
   function setHeight( $height )      { $this->_height = (int)$height; }
   /**
    * Set the width of the dialog in characters.
    *
    * A value of 0 means that dialog should auto-size the dialog.
    * Unfortunately whiptail segfaults when passed height and width of
    * 0 (on my system).
    *
    * @see getHeight
    * @param int $width The width in lines 0 = automatic
    */
   function setWidth( $width )       { $this->_width  = (int)$width; }

   /**
    * When auto-sizing, it represents width/height ratio, i.e. how
    * many characters wide it should be for every line high.  The
    * default is 9.
    *
    * @param int $aspect The aspect
    */
   function setAspect( $aspect )   { $this->_options["aspect"]       = (int)$aspect; }
   /**
    * The title of the dialog which is placed on the background
    *
    * @param string $title The title
    */
   function setBackTitle( $title )   { $this->_options["backtitle"]    = $title; }
   /**
    * Sound an alarm each time the screen is refreshed?
    *
    * @param bool $beep Whether to sound the alarm
    */
   function setBeep( $beep )        { $this->_options["beep"]         = (bool)$beep; }
   /**
    * Sound an alarm if the input is interrupted (e.g. ctrl-c)?
    *
    * @param bool $beepafter Whether to beep
    */
   function setBeepAfter( $beepafter )   { $this->_options["beep-after"]   = (bool)$beepafter; }
   /**
    * Set the position of the upper left corner of the dialog.
    *
    * It is measured from the upper left corner of the screen.
    * Warning: This does not actually work at the moment!
    *
    * @todo fix get/setBegin
    *
    * @param int $down Lines down
    * @param int $right Characters right
    */
   function setBegin( $down, $right )     { $this->_options["begin"] = $down . " ". $right; }
   /**
    * Whether the screen shoud be cleared after the dialog.
    *
    * This is true by default unlike the dialog command itself, where
    * it defaults to false.
    *
    * @param bool $clear Whether to clear the screen
    */
   function setClear( $clear )       { $this->_options["clear"]        = (bool)$clear; }
   /**
    * Whether to wrap carriage returns.
    * 
    * Whether to allow embedded new lines and \n to be converted to
    * actual new lines in the dialog text.  Defaults to no.
    *
    * @param bool $wrap
    */
  function setCRwrap( $wrap )      { $this->_options["cr-wrap"]      = (bool)$wrap; }

  /**
   * There is no support for the create-rc, print-maxsize, print-size,
   * print-version, separate-output, separate-widget, shadow,
   * size-err, sleep, stderr, stdout, or version options.  They didn't
   * make sense to implement, or weren't useful.
   */

  /**
   * Whether no should be the default
   *
   * Whether the default button should be no in a yes/no box.
   * Defaults to false (yes is the default button).
   *
   * @param bool $def_no
   */
   function setDefaultNo( $def_no )   { $this->_options["defaultno"]    = (bool)$def_no; }

  /**
    * Set the default item for a menu or radiolist.
    *
    * By default the first item is used.
    *
    * @param string $default_item The tag of the default item
    */
   function setDefaultItem( $default_item ) { $this->_options["default-item"] = $default_item; }

   /**
    * Whether to use help in menu, radiolist, or checklist.
    *
    * Help (if used) must be passed to these classes as an associative
    * array of the form <tt>tag => help text</tt>.
    *
    * @param bool $use_help
    */
   function setItemHelp( $use_help )    { $this->_options["item-help"]    = (bool)$use_help; }

   /**
    * Whether to suppress the shadow.
    * 
    * Whether to suppress the shadow that usually occurs around the
    * dialog.
    *
    * @param bool $no_shadow
    */
   function setNoShadow( $no_shadow )    { $this->_options["no-shadow"]    = (bool)$no_shadow; }

   /**
    * Whether to disallow cancel.
    *
    * Disallow the cancel button in checklist, inputbox, and
    * menuboxes.  This does <em>not</em> disallow the usage of escape
    * to cancel.
    *
    * @param bool $no_cancel
    */
   function setNoCancel( $no_cancel )    { $this->_options["no-cancel"]    = (bool)$no_cancel; }
   /**
    * Set title of the dialog
    *
    * The title of the dialog which is placed on the dialog itself
    * (i.e. not the background)
    *
    * @param string $title
    */
   function setTitle( $title )       { $this->_options["title"]        = $title; }

   /**
    * Set the length of a tab
    *
    * Sets the number of spaces to which a tab should be converted or,
    * if false, to not convert tabs to spaces.
    *
    * @param int $tab_width number of spaces per tab (or false to not convert)
    */
   function setTabLength( $tab_width ) {
      if($tab_width === false){
         $this->_options["tab-correct"] = false;
         $this->_options["tab-len"]     = null;
      } else {
         $this->_options["tab-correct"] = true;
         $this->_options["tab-len"]     = (int)$tab_width;
      }
   }

   /**
    * Use full rather than compact buttons.
    *
    * This only has an effect for whiptail.
    *
    * @param bool $full_buttons
    */
   function setFullButtons( $full_buttons ) { $this->_options["fb"]         = $full_buttons; }

   /**
    * Whether item descriptions should be surpressed.
    *
    * If descriptions are surpressed only the tags will be shown.
    * This only has an effect for whiptail.
    *
    * @param bool $no_item Whether to supress item descriptions
    */
   function setNoItem( $no_item )      { $this->_options["noitem"]     = $no_item; }
   /**
    * Whether to force the appearance of a scroll-bar
    *
    * This only has an effect for whiptail.
    *
    * @param bool $force_scroll
    */
   function setForceScroll( $force_scroll ) { $this->_options["scrolltext"] = $force_scroll; }
   
   /**
    * Set which program should be run.
    *
    * A low level command to set which program should be used.  Exercise caution.
    * @access private
    * @param string $command The command name
    */
   function setCommand( $command ) { $this->_program_name = $command; }
   /**
    * If the `dialog` command is available, use it.
    */
   function useDialog( )       { if(`which dialog`)   $this->_program_name = "dialog"; }
   /**
    * If the `xdialog` command is available, use it.
    */
   function useXDialog( )      { if(`which xdialog`)  $this->_program_name = "xdialog"; }
   /**
    * If the `whiptail` command is available, use it.
    */
   function useWhiptail( )     { if(`which whiptail`) $this->_program_name = "whiptail"; }
   
   /**
    * Returns array of options in effect for this Dialog instance
    *
    * This includes inherited options.  Note that inheritance is
    * inherited, i.e. if A inherits from B and B from * C, then A also
    * inherits from C.  It will lead to an infinite * loop if a Dialog
    * inherits from itself in some way.
    *
    * @see inheritSettingsFrom
    * @return array
    */
   function getOptions()
      {
         $opt = array();
         
         if( $this->_parent !== null ){
            $opt = $this->_parent->getOptions();
         }
         foreach ( $this->_options AS $k => $v ) {
            //print $k;
            //print $v;
            $opt[$k] = $v;
         }
         return $opt;
      }
        
   /**
    * @see setMainText
    * @return string
    */
   function getMainText()    { return $this->_text; }
   /**
    * @see setHeight
    * @return int
    */
   function getHeight( )     { return $this->_height; }
   /**
    * @see setWidth
    * @return int
    */
   function getWidth( )      { return $this->_width; }
   /**
    * @see setAspect
    * @return int
    */
   function getAspect()      { return $this->_options["aspect"]; }
   /**
    * @see setBackTitle
    * @return string
    */
   function getBackTitle()   { return $this->_options["backtitle"]; }
   /**
    * @see setBeep
    * @return bool
    */
   function getBeep()        { return $this->_options["beep"]; }
   /**
    * @see setBeepAfter
    * @return bool
    */
   function getBeepAfter()   { return $this->_options["beep-after"]; }
   /**
    * <strong>Warning:</strong> This does not actually work at the moment!
    * @see setBegin
    */
   function getBegin()       { return explode("\" \"", $this->_options["begin"]); }
   /**
    * @see setClear
    * @return bool
    */
   function getClear()       { return $this->_options["clear"]; }
   /**
    * @see setCRwrap
    * @return bool
    */
   function getCRwrap()      { return $this->_options["cr-wrap"]; }
   /**
    * @see setDefaultNo
    * @return bool
    */
   function getDefaultNo()   { return $this->_options["defaultno"]; }
   /**
    * @see setDefaultItem
    * @return string
    */
   function getDefaultItem() { return $this->_options["default-item"]; }
   /**
    * @see setItemHelp
    * @return bool
    */
   function getItemHelp()    { return $this->_options["item-help"]; }
   /**
    * @see setNoShadow
    * @return bool
    */
   function getNoShadow()    { return $this->_options["no-shadow"]; }
   /**
    * @see setNoCancel
    * @return bool
    */
   function getNoCancel()    { return $this->_options["no-cancel"]; }
   /**
    * @see setTitle
    * @return string
    */
   function getTitle()       { return $this->_options["title"]; }
   /**
    * @see setTabLength
    * @return mixed
    */
   function getTabLength() {
      if( $this->_options["tab-correct"]  === false ) {
         return false;
      } else {
         return $this->_options["tab-len"];
      }
   }
   
   /**
    * Set the Dialog item from which this instance should inherit settings.
    *
    * A dialog can inherit from a Dialog of a different type, e.g. a
    * YesNo can inherit from a MessageBox.  Not all of the
    * implications have been accounted for, so use with caution.
    * Several important characteristics are not currently inherited.
    * Inheritance is inherited, i.e. an item will check it's own
    * options first, then those of it's parent, then those of it's
    * grandparent, and so on.
    *
    * @todo they should inherit height, width etc. as well, at least when they are created
    * @see getOptions
    * @param mixed $parent Refence to a dialog from which to inherit settings
    */
   function inheritSettingsFrom( &$paren )
      {
         $this->_parent =& $paren;
      }
   
   /**#@+
    * @param string $text Text of the dialog
    */
   /**
    * Make child instances which inherit from the current object.
    *
    * Each takes all the parameters of the corresponding constructor
    * except the optional parent parameter.  All parameters are
    * optional and default to the parent's values.
    *
    * @param string $text Text of the dialog
    */
   function makeYesNoInstance($text = null)
      {
         if($text === null)
            $text = $this->_text;
         $temp = new YesNo($text);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   /**
    * @see makeYesNoInstance
    * @param string $text Text of the dialog
    */
   function makeMessageBoxInstance($text = null)
      {
         if($text === null)
            $text = $this->_text;
         $temp = new MessageBox($text);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   /**
    * @param string $text Text of the dialog
    */
   function makeInfoBoxInstance($text = null)
      {
         if($text === null)
            $text = $this->_text;
         $temp = new InfoBox($text);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   /**#@+
    * @param string $init Initial text of the dialog
    */
   function makeInputBoxInstance($text = null, $init=null)
      {
         if($text === null)
            $text = $this->_text;
         if($init === null)
            $init = $this->_init;
         $temp = new InputBox($text, $init);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   function makePasswordBoxInstance($text = null, $init=null)
      {
         if($text === null)
            $text = $this->_text;
         if($init === null)
            $init = $this->_init;
         $temp = new PasswordBox($text, $init);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   function makeFileSelectBoxInstance($text = null, $init=null)
      {
         if($text === null)
            $text = $this->_text;
         if($init === null)
            $init = $this->_init;
         $temp = new FileSelectBox($text, $init);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   /**#@-*/
   /**#@+
    * @param string $path The path to the file to view
    */
   function makeTextBoxInstance($path = null)
      {
         if($path === null)
            $path = $this->_text;
         $temp = new TextBox($text);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   function makeTailBoxInstance($path = null)
      {
         if($path === null)
            $path = $this->_text;
         $temp = new TailBox($text);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   /**#@-*/
   /**
    * @see Calendar
    *
    * @param int $day day
    * @param int $mon month
    * @param int $yr year
    */
   function makeCalendarInstance($text = null, $day=null, $mon=null, $yr=null)
      {
         if($text === null)
            $text = $this->_text;
         if($day === null)
            $day = $this->_time[0];
         if($mon === null)
            $mon = $this->_time[1];
         if($yr === null)
            $yr  = $this->_time[2];
         $temp = new Calendar($text, $day, $mon, $yr);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   /**
    * @param int $h hours
    * @param int $m minutes
    * @param int $s seconds
    */
   function makeTimeBoxInstance($text = null, $h=null, $m=null, $s=null)
      {
         if($text === null)
            $text = $this->_text;
         if($h === null)
            $h = $this->_time[0];
         if($m === null)
            $m = $this->_time[1];
         if($s === null)
            $s = $this->_time[2];
         $temp = new Calendar($text, $day, $mon, $yr);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }

   /**#@+
    * @param array $choices
    * @param array $help
    */
   function makeMenuInstance($text = null, $choices = null, $help=null)
      {
         if($text === null)
            $text = $this->_text;
         if($choices === null)
            $choices = $this->_choices;
         if($help === null)
            $help = $this->_help;
         $temp = new Menu($text, $choices);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   function makeRadioListInstance($text = null, $choices = null, $help=null)
      {
         if($text === null)
            $text = $this->_text;
         if($choices === null)
            $choices = $this->_choices;
         if($help === null)
            $help = $this->_help;
         $temp = new RadioList($text, $choices);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   function makeCheckListInstance($text = null, $choices = null, $selected = null, $help=null)
      {
         if($text === null)
            $text = $this->_text;
         if($choices === null)
            $choices = $this->_choices;
         if($selected === null)
            $selected = $this->_selected;
         if($help === null)
            $help = $this->_help;
         $temp = new CheckList($text, $choices);
         $temp->inheritSettingsFrom($this);
         return $temp;
      }
   /**#@-*/
   /**#@-*/
}


// Inherited classes which can be invoked.  There should be an easier
// way than overloading the constructor.

/**
 * A dialog for asking a Yes/No question
 * @package dialog4php
 */
class YesNo extends Dialog {

   /**
    * Constructor
    *
    * @param string $text Dialog text
    * @param mixed $parent Dialog from which to inherit settings
    */
   function YesNo($text, $parent=null)
      {
         $this->_command_name="--yesno";
         $this->_text=$text;
         $this->_parent=$parent;
      }
   
   /**
    * Return user's choice.
    *
    * We overload the getOutput function to return the same as
    * getReturnValue since there is no output per se.
    *
    * @return bool
    */
   function getOutput()
      {
         return $this->getReturnValue();
      }
}

/**
 * A dialog for showing a message and force user acknowledgement
 * @package dialog4php
 */
class MessageBox extends YesNo {
   
   /**
    * Constructor
    *
    * @param string $text Dialog text
    * @param mixed $parent Dialog from which to inherit settings
    */
   function MessageBox($text, $parent=null)
      {
         $this->_command_name="--msgbox";
         $this->_text=$text;
         $this->_parent=$parent;
      }
}

/**
 * A dialog for showing a message.  The dialog exits immediately, but
 * the screen in not cleared.
 * @package dialog4php
 */
class InfoBox extends YesNo {
   
   /**
    * Constructor
    *
    * @param string $text Dialog text
    * @param mixed $parent Dialog from which to inherit settings
    */
   function InfoBox($text, $parent=null)
      {
         $this->_command_name="--infobox";
         $this->_text=$text;
         $this->_parent=$parent;
      }
}


/**
 * A dialog for getting a string from the user.
 * @package dialog4php
 */
class InputBox extends Dialog {

   /**
    * The text which the user will initially edit
    */
   var $_init;
   
   /**
    * Constructor
    *
    * The initial string shouldn't have newlines because it does wierd
    * things.
    *
    * @param string $text Instruction text
    * @param string $initial Initial text of the textbox.
    * @param mixed $parent Dialog from which to inherit settings
    */
   function InputBox($text, $init="", $parent=null)
      {
         $this->_command_name="--inputbox";
         $this->_text=$text;
         $this->_init=$init;
         $this->_parent=$parent;
      }

   /**
    * We must add the init string to the list.
    * @access private
    */
   function getTypeSpecific() 
      {
         return array( $this->_command_name, $this->_text, $this->_height, $this->_width,
                       $this->_init);
      }
}

/**
 * Same as InputBox except that the string is not shown while the user
 * is editing it.
 * @package dialog4php
 */
class PasswordBox extends InputBox {

   /**
    * Constructor
    *
    * You should probably not pass an initial password since the user
    * cannot see it to.  Also it is not secure since it can be seen in
    * the process list.
    *
    * @param string $text Dialog text
    * @param string $init Initial password
    * @param  mixed $parent Dialog from which to inherit settings
    */
   function PasswordBox($text, $init="", $parent=null)
      {
         $this->_command_name="--passwordbox";
         $this->_text=$text;
         $this->_init=$init;    // Should perhaps enforce not having a default password
         $this->_parent=$parent;
      }
}

/**
 * Select a file
 * @package dialog4php
 */
class FileSelectBox extends InputBox {
   // Even though it's not much like and InputBox, it needs most of the same member functions
   /**
    * @param string $path Initial path
    * @param mixed $parent Dialog from which to inherit settings
    */
   function FileSelectBox($path, $parent=null)
      {
         $this->_command_name="--fselect";
         $this->_text=$path;
         $this->_parent=$parent;
      }
}


/**
 * Display a file in a text box
 * @package dialog4php
 */
class TextBox extends Dialog {
   /**
    * The text in this case is the path of the file to view.
    *
    * @param string $path Path to the file
    * @param mixed $parent Dialog from which to inherit settings
    */
   function TextBox($path, $parent=null)
      {
         $this->_command_name="--textbox";
         $this->_text=$path;
         $this->_parent=$parent;
      }
   /**
    * Access the return value since there is no output per se.
    * @access private
    * @return bool
    */
   function getOutput()
      {
         return $this->getReturnValue();
      }
}

/**
 * Like TextBox except that the textbox is updated like `tail -f`
 * @package dialog4php
 */
class TailBox extends TextBox {

   /**
    * Constructor
    *
    * @param string $path File path
    * @param mixed $parent Dialog from which to inherit settings
    */
   function TailBox($path, $parent=null)
      {
         $this->_command_name="--tailbox";
         $this->_text=$path;
         $this->_parent=$parent;
      }
   
   /**
    * Run in the background like `tail -f &`
    *
    * Though I can't think of any reason to invoke it in the
    * background (rather than just `tail -f &` I have included the
    * option since dialog does.
    */
   function Background()
      {
         // Not sure when this would be useful...
         $this->_command_name="--textboxbg";
      }
   /**
    * Run in the foreground (the default)
    */
   function Foreground()
      {
         $this->_command_name="--tailbox";
      }
}


/**
 * Get a date from the user
 * @package dialog4php
 */
class Calendar extends Dialog {
   /**
    * An array of ints: (day, month, year)
    * @access private
    */
   var $_time;
   /**
    * Constructor
    *
    * Accept day month and year all of which default to zero.  If the
    * year is zero, then dialog uses the current date as it's starting
    * place.
    *
    * @param string $text Dialog Text
    * @param int $day Day
    * @param int $mon Month
    * @param int $yr Year
    * @param mixed $parent
    */
   function Calendar($text, $day=0, $mon=0, $yr=0, $parent=null)
      {
         $this->_command_name="--calendar";
         $this->_text=$path;
         $this->_parent=$parent;
         $this->_time = array( (int)$day, (int)$mon, (int)$year);
         
      }

   /**
    * Return an array of ints of the day month and year selected.
    *
    * @return array(int) $date array(int day, int mon, int year)
    */
   function getOutput()
      {
         return explode("/", $this->_output[0]);
      }
   /**
    * Must add the date to the list
    * @access private
    */
   function getTypeSpecific()
      {
         return array( $this->_command_name, $this->_text, $this->_height, $this->_width,
                       $this->_time[0], $this->_time[1], $this->_time[2]);
      }
}

/**
 * Get a time from the user
 * @package dialog4php
 */
class TimeBox extends Calendar {

   /**
    * Constructor
    *
    * @param string $text
    * @param int $h hours
    * @param int $m minutes
    * @param int $s seconds
    * @param mixed $parent
    */
   function TimeBox($text, $h=0, $m=0, $s=0, $parent=null)
      {
         $this->_command_name="--timebox";
         $this->_text=$path;
         $this->_parent=$parent;
         $this->_time = array( (int)$h, (int)$m, (int)$s);
         
      }

   /**
    * Return an array of ints of the hour, minute, seconds selected.
    *
    * @return array(int) $time array(hour, minute, second)
    */
   function getOutput()
      {
         return explode(":", $this->_output[0]);
      }
}


/**
 * Allow the user to choose from a list of possible choices
 * @package dialog4php
 */
class Menu extends Dialog {

   /**
    * Keep track of a list of choices (tag => description)
    * @access private
    */
   var $_choices = array();
   /**
    * Keep track of any (optional) help text provided.
    * @access private
    */
   var $_help    = array();

   /**
    * Number of items to display before scrolling
    * @access private
    */
   var $_menu_height = 0;

   /**
    * Constructor
    *
    * The choices and help are both associative arrays, both indexed
    * by the same tags.
    *
    * @param string $text
    * @param array $choices
    * @param array $help
    * @param mixed $parent
    */
   function Menu($text, $choices, $help=array(), $parent=null)
      {
         $this->_text=$text;
         
         if( $this->_height !== 0)
         {
            $this->_menu_height = $this->_height - 6;
         } else {
            $this->_menu_height = 0;
         }
         
         $this->_parent = $parent;
         $this->_choices = $choices;
         $help->_help = $help;
      }

   /**
    * We must add all of the choices, and optionally help, to the list.
    * @access private
    */
   function getTypeSpecific() 
      {
         $arr = array( "--menu", $this->_text,
                       $this->_height, $this->_width, $this->_menu_height);
         
         foreach($this->_choices as $item => $descr)
            {
               array_push($arr, $item);
               array_push($arr, $descr);
               // I hope this treats empty help items correctly
               if( $this->_options["item-help"] ){
                  array_push($arr, $help[$item]);
               }
            }
         return $arr;
      }
   
   /**
    * Add a single menu item
    *
    * Add a menu item as tag, description, and option help
    *
    * @param string $tag A unique tag
    * @param string $descr The description
    * @param string $help Optional help
    */
   function addMenuItem($tag, $descr, $help="")
      {
         $this->_choices[$tag]=$descr;
         $this->_help[$tag]=$help;
      }

   /**
    * Remove a menu item one at a time by it's tag.
    *
    * @param string $tag The unique tag to remove
    */
   function removeMenuItem($tag)
      {
         $this->_choices[$tag] = null;
         $this->_help[$tag] = null;
      }
   /**
    * We must change the menu height as well as the dialog size 
    * @access private
    */
   function setMaxDims()
      {
         $dims = $this->getMaxDims();
         $this->_height = $dims[0];
         $this->_width  = $dims[1];
         $this->_menu_height  = $dims[0]-7;
      }
}

/**
 * Essentially the same as Menu
 * @package dialog4php
 */
class RadioList extends Menu {

   /**
    * Constructor
    *
    * @param string $text
    * @param array $choices
    * @param array $help
    * @param mixed $parent
    */
   function RadioList($text, $choices, $help=array(), $parent=null)
      {
         $this->_command_name="--radiolist";
         $this->_text=$text;
         $this->_parent = $parent;
         $this->_choices = $choices;
         $help->_help = $help;
    }

   /**
    * Put a status of "on" for the default item, "off" for the rest
    * @access private
    */
   function getTypeSpecific() 
      {
         $arr = array( $this->_command_name, $this->_text,
                       $this->_height, $this->_width, $this->_menu_height);
         
         foreach($this->_choices as $item => $descr)
            {
               array_push($arr, $item);
               array_push($arr, $descr);
               if( $this->_options["default-item"] == $item ){
                  array_push($arr, "on");
               } else {
                  array_push($arr, "off");
               }
               if( $this->_options["item-help"] ){
                  array_push($arr, $help[$item]);
               }

            }
         
         return $arr;
      }
}

/**
 * Allow the user to choose zero or more options from a list
 * @package dialog4php
 */
class CheckList extends Menu {

   /**
    * Those options which are initially selected.
    */
   var $_selected;

   /**
    * Constructor
    *
    * In addition to the arrays for choices and help, there is an
    * optional array for those which are initially selected.
    *
    * @param string $text
    * @param array $choices
    * @param array $selected
    * @param array $help
    * @param mixed $parent
    */
   function CheckList($text, $choices, $selected=array(), $help=array(), $parent=null)
      {
         $this->_command_name="--checklist";
         $this->_text=$text;
         $this->_parent = $parent;
         $this->_choices = $choices;
         $this->_help = $help;
         // Same keys and values, mostly so that we can easily look them up later
         foreach($selected as $k)
            {
               $this->_selected[$k] = $k;
            }
         $this->_options["separate-output"] = true;
      }
   
   /**
    * Must add each item with status (whether selected) and possibly help.
    *
    * @access private
    */
   function getTypeSpecific() 
      {
         $arr = array( $this->_command_name, $this->_text,
                       $this->_height, $this->_width, $this->_menu_height);
         
         foreach($this->_choices as $item => $descr)
            {
               array_push($arr, $item);
               array_push($arr, $descr);
               if( $this->_selected[$item] ){
                  array_push($arr, "on");
               } else {
                  array_push($arr, "off");
               }
               if( $this->_options["item-help"] ){
                  array_push($arr, $help[$item]);
               }
            }
         
         return $arr;
      }

   /**
    * Default items are now merely added to the list of those selected.
    *
    * @param string $tag The tag of the item
    * @param bool $checked Whether it should be selected initially
    */
   function setDefaultItem($tag, $checked=true)
      {
         $this->_selected[$tag] = $checked;
      }

   /**
    * Return the array of all those selected
    *
    * @return array $selected 
    */
   function getDefaultItem()
      {
         return $this->_selected;
      }
   
   /**
    * Return an array of values
    * @return array(string) $chosen The tags of the items which were chosen.
    */
   function getOutput()
      {
         return $this->_output;
      }
}



/*

making timebox and calendar instances will both inherit whatever time
the parent has (which may be the wrong type)


setInstructionText, and things instead of just at creation time

DIALOGRC


// calendar  day mon year=0 => current date  --> day/mon/year
// timebox -- h m s --> h:m:s

// inputbox init -- init initializes the string -- how does it work
// password -- like input except "secure"

gauge     [percent] percentages from stdin

//menu
checklist list-height [tag item status]
radiolist [tag item status] -- like menu status=on means selected

// yesno
// msgbox -- like yes/no without no
// infobox -- like msgbox except it exits immediately and doesn't clear


// fselect  text = filepath
// tailbox -- text=file
// textbox -- ssa except not tail -f i.e. adds more data as file grows
// textboxbg -- ssa except background



manpage for whiptail
http://www.annodex.net/cgi-bin/man/man2html?whiptail+1
segfaults with 0's for height and width 


xdialog
http://www.die.net/doc/linux/man/man1/xdialog.1.html

--wmclass <name> 
--rc-file <gtkrc filename> 
--backtitle <backtitle> 
--title <title> 
--allow-close | --no-close 
--screen-center | --under-mouse | --auto-placement 
--center | --right | --left | --fill 
--no-wrap | --wrap 
--cr-wrap | --no-cr-wrap 
--stderr | --stdout 
--separator <character> | --separate-output 
--buttons-style default|icon|text

 
--fixed-font 
This option is for use with --tailbox, --textbox and --editbox. 
--password 
This option may be repeated 2 or 3 times before --2inputsbox or --3inputsbox. 
--editable 
This option is for use with --combobox. 
--time-stamp | --date-stamp 
This option is for use with --logbox. 
--reverse 
This option is for use with --logbox. 
--keep-colors 
This option is for use with --logbox. 
--interval <timeout> 
This option may be used with input(s) boxes, combo box, range(s) boxes, spin(s) boxes, list boxes, menu box, treeview, calendar and timebox widgets. 
--no-tags 
This option is for use with --menubox, --checklist and --radiolist. 
--item-help 
When this option is used, the <helpN> parameters of the --menubox, --checklist, --radiolist, --buildlist and --treeview options must be specified (while they must be omitted if --item-help is not specified). 
--default-item <tag> 
This option is for use with --menubox. 
--icon <xpm filename> 
This option is for use with any widget accepting a <text> parameter. 
--no-ok 
This option is for use with --tailbox and --logbox. 
--no-cancel 
This option may be used with all but --infobox, --gauge and --progress. 
--no-buttons 
This option may be used with --textbox, --tailbox, --logbox, --infobox, --fselect and --dselect. 
--default-no 
This option may be used with all widgets with a "Cancel" or "No" button. Note that it is ignored when the --wizard option is in force. 
--wizard 
This option may be used with all but --msgbox, --infobox, --gauge and --progress. 
--help <help> 
This option may be used with all but --infobox, --gauge and --progress. 
--print <printer> 
This option is for use with --tailbox, --textbox and --editbox. 
--check <label> 
This option may be used with all but --infobox, --gauge and --progress. 
--ok-label <label> 
This option may be used with all widgets with a "OK" or "Yes" button. Note that it is ignored when the --wizard option is in force. 
--cancel-label <label> 
This option may be used with all widgets with a "Cancel" or "No" button. Note that it is ignored when the --wizard option is in force. 
--beep 
This option may be used with all widgets. 
--beep-after 
This option may be used with all widgets. 
--begin <Yorg> <Xorg> 
This option may be used with all widgets. 
--ignore-eof 
This option may be used with --infobox and --gauge. 
--smooth 
This option is for use with --tailbox and --logbox.


 
--yesno         <text> <height> <width> 
--msgbox                <text> <height> <width> 
--infobox              <text> <height> <width> [<timeout>] 

--gauge                 <text> <height> <width> [<percent>] 
--progress     <text> <height> <width> [<maxdots> [[-]<msglen>]] 

--inputbox     <text> <height> <width> [<init>] 
--2inputsbox   <text> <height> <width> <label1> <init1> <label2> <init2> 
--3inputsbox   <text> <height> <width> <label1> <init1> <label2> <init2> <label3> <init3> 

--combobox     <text> <height> <width> <item1> ... <itemN> 
--rangebox     <text> <height> <width> <min value> <max value> [<default value>] 
--2rangesbox   <text> <height> <width> <label1> <min1> <max1> <def1> <label2> <min2> <max2> <def2> 
--3rangesbox   <text> <height> <width> <label1> <min1> <max1> <def1> <label2> <min2> <max2> <def2> <label3> <min3> <max3> <def3> 
--spinbox              <text> <height> <width> <min> <max> <def> <label> 
--2spinsbox    <text> <height> <width> <min1> <max1> <def1> <label1> <min2> <max2> <def2> <label2> 
--3spinsbox    <text> <height> <width> <min1> <max1> <def1> <label1> <min2> <max2> <def2> <label2> <min3> <max3> <def3> <label3> 
--textbox              <file> <height> <width> 
--editbox              <file> <height> <width> 
--tailbox              <file> <height> <width> 
--logbox                <file> <height> <width> 
--menubox              <text> <height> <width> <menu height> <tag1> <item1> {<help1>}... 
--checklist    <text> <height> <width> <list height> <tag1> <item1> <status1> {<help1>}... 
--radiolist    <text> <height> <width> <list height> <tag1> <item1> <status1> {<help1>}... 
--buildlist    <text> <height> <width> <list height> <tag1> <item1> <status1> {<help1>}... 
--treeview     <text> <height> <width> <list height> <tag1> <item1> <status1> <item_depth1> {<help1>}... 
--fselect              <file> <height> <width> 
--dselect              <directory> <height> <width> 
--calendar     <text> <height> <width> <day> <month> <year> 
--timebox              <text> <height> <width>



ENVIRONMENT VARIABLES 

XDIALOG_HIGH_DIALOG_COMPAT 
When set to 1 or true (case insensitive), this environment variable turns the (c)dialog high compatibility mode on. The net effect is that: 
- a fixed (monospacing) font is used in all Xdialog widgets (including labels, tags, menu/list items and buttons); 
- the --left common option is used as the default justification option instead of the --center one; 
- the <text> wrapping is always in force (--wrap) and the --fill option is ignored. 
- the --no-cr-wrap common option is used as the default instead of the --cr-wrap one (but it does not apply to the <backtitle> in this case). 
- regardless of the specified box size, the auto-sizing feature of Xdialog is forced when XDIALOG_FORCE_AUTOSIZE environment variable is set it to 1 or true. This makes for the sizing problems of some menus (e.g. when the specified box size is actually too small for the number of specified menu/list entries to fit). 
- the Cancel button is not displayed into the tailbox and textbox widgets; 
- the infobox widget is turned into a msgbox unless the XDIALOG_INFOBOX_TIMEOUT environment variable is set (in 1/1000s) and greater than 0, in which case an actual infobox without button is used; 
- the --version special option returns the same string as --print-version.

XDIALOG_FORCE_AUTOSIZE XDIALOG_INFOBOX_TIMEOUT 
See the notes about XDIALOG_HIGH_DIALOG_COMPAT above.

*/
?>

Return current item: Dialog for PHP