Listing of Source ../source/GOF/GofPushButtonIdentifier.java
package se.entra.phantom.server;

import java.util.StringTokenizer;
import java.util.Vector;

/**
 * This class identifies push button controls for the Gui-on-the-fly, from unused GofHostFields.
 * @author J. Bergström
 */
public class GofPushButtonIdentifier extends GofControlIdentifierAdapter implements PhantomControlType
{
  // ------------------
  // INSTANCE VARIABLES
  // ------------------

  /**
   * A vector holding the identification strings.
   */
  private Vector<String> identStrings = new Vector<String>( );

  /*
   * Flag indicating if combined pushbutton definitions should be looked for.
   */
  //private boolean doFindCombined = false;

  /**
   * The equal sign to be used together with the identification strings.
   */
  private String equalSign;

  /**
   * The character used as separator for the push button combinations.
   */
  private String combSign;

  /**
   * Indicates the way the pushbutton layout will be created.
   * Valid values are:
   * <pre>
   *    DEFAULT
   *    SIZE
   *    FONT
   *    SIZEANDFONT
   *    CLONE
   * </pre>
   */
  private String layout;

  /**
   * The value for the setting in the configuration file for the <code>nontemplatebutton</code>.
   */
  private String nonTemplateButton;

  /**
   * The menu to use for non-template buttons. The default menu is <i>Functions</i>.
   */
  private String menuName = "~Functions";

  /**
   * Menu items created for a panel. Will be emptied for each new panel.
   */
  private Vector<PhantomMenuItem> menuItems;

  // ----------------
  // INSTANCE METHODS
  // ----------------

  /**
   * Loads setting for the control from the server ini-file.
   * @param confFile The server ini file.
   * @param subsection The name of the GOF subsection that we are currently loading settings from.
   */
  @Override
  public void getControlSettings( IniFile confFile, String subsection )
  {
    String pushButtonIdent = confFile.getData( subsection, "pushbuttonident" );
    parsePushButtonIdent( pushButtonIdent );
    
    //String pushButtonCombination = confFile.getData( subsection, "pushbuttoncombination" );
    //if( pushButtonCombination.equals( "1" ) == true )
    //  doFindCombined = true;

    equalSign = confFile.getData( subsection, "pushbuttonequalsign" );

    combSign = confFile.getData( subsection, "pushbuttoncombsign" );

    layout = confFile.getData( subsection, "pushbuttonlayout" );
    if( layout == null )
      layout = "DEFAULT";

    nonTemplateButton = confFile.getData( subsection, "nontemplatebutton" );

    String value = confFile.getData( subsection, "nontemplatemenu" );
    if( value != null && value.equals( "" ) == false )
      menuName = value;
  }

  /**
   * Identifies all the pushbutton controls from the <code>GofHostFields</code>. This is a
   * little complicated since one GofHostField may correspond to one or several pushbuttons.
   * @param gofRuntime        The GuiOnTheFlyRuntime instance.
   * @param areaIdentifier    The areaIdentifier in which the entry fields should be identified..
   * @param phantomHostScreen The Phantom host screen corresponding to the host screen.
   * @param hostScreen        The host screen that we are trying to build a GOF panel for.
   * @param newPanel          The newly created Gui-on-the-fly runtime panel.
   */
  @Override
  public void identifyCtrls( GuiOnTheFlyRuntime gofRuntime,
                             GofHostAreaIdentifier areaIdentifier, 
                             PhantomHostScreen phantomHostScreen, 
                             HostScreen hostScreen,
                             PhantomPanelData templPanel, 
                             PhantomPanelData newPanel,
                             int offsetX,
                             int offsetY )
  {
    this.templPanel = templPanel;

    Vector<GofHostField> gofHostFields = areaIdentifier.getAreasGofHostFields( );

    Vector<GofPushButtonElement> pbElements = new Vector<GofPushButtonElement>( );
    menuItems = new Vector<PhantomMenuItem>( );

    for( int i = 0, s = gofHostFields.size( ); i < s; i++ )
    {
      GofHostField gofHostField = gofHostFields.elementAt( i );
      if( gofHostField.hasBeenProcessed == false && gofHostField.isProtected( ) == true )
      {
        if( gofHostField.isEmpty( ) == false )
        {
          String text = gofHostField.getText( ).trim( );
          if( text.equals( "" ) == false )
          {
            pbElements = findPushButtonElements( text );
            if( pbElements.size( ) > 0 )
            {
              gofHostField.hasBeenProcessed = true;
              createPushButtons( areaIdentifier,
                                 pbElements, 
                                 text, 
                                 gofHostField, 
                                 phantomHostScreen,
                                 hostScreen, 
                                 newPanel,
                                 offsetX,
                                 offsetY );
            }
          }
        }
      }
      pbElements.removeAllElements( );
    }

    int s = menuItems.size( );
    if( s > 0 )
    {
      PhantomMenuItem[] items = new PhantomMenuItem[s];
      for( int i = 0; i < s; i++ )
      {
        items[i] = menuItems.elementAt( i );
      }
      PhantomMenu menu = new PhantomMenu( newPanel, 1000, items, menuName );
      newPanel.addMenu( menu );
    }
  }

  /**
   * Parses the configuration setting for the pushbutton identifications, and splits
   * it up into the identification strings, which are stored in a Vector.
   * @param pushButtonIdent The setting from the configuration file.
   */
  private void parsePushButtonIdent( String pushButtonIdent )
  {
    if( pushButtonIdent == null )
      return;

    StringTokenizer st = new StringTokenizer( pushButtonIdent, ", " );
    while( st.hasMoreTokens( ) )
    {
      String s = st.nextToken( ).trim( );
      if( s.equals( "" ) == false )
        identStrings.addElement( s );
    }
  }

  /**
   * Finds all the push button elements in the text.
   * @param text The text to search in.
   */
  private Vector<GofPushButtonElement> findPushButtonElements( String text )
  {
    Vector<GofPushButtonElement> pbElements = new Vector<GofPushButtonElement>( );
    
    for( int i = 0, s = text.length( ); i < s; i++ )
    {
      for( int j = 0, sj = identStrings.size( ); j < sj; j++ )
      {
        String ident = identStrings.elementAt( j );
        int idLen = ident.length( );
        if( i + idLen > text.length( ) )
           break;
        String textPart = text.substring( i, i + idLen );
        textPart = changeToWC( textPart );
        if( ident.equals( textPart ) == true )
        {
          GofPushButtonElement gpbe = null;
          if( i > 0 )
          {
            int identEnd = i + textPart.length( );
            String nxtChar = "";
            if( identEnd < text.length( ) )
              nxtChar = text.substring( identEnd, identEnd + 1 );
            
            if( text.substring( i - 1, i ).equals( combSign ) == true )
            {
              gpbe = new GofPushButtonElement( i, ident + equalSign );
              gpbe.combType = 2;
            }
            else if( nxtChar.equals( combSign ) )
            {
              gpbe = new GofPushButtonElement( i, ident + equalSign );
              gpbe.combType = 1;
            }
            else if( nxtChar.equals( equalSign ) )
              gpbe = new GofPushButtonElement( i, ident + equalSign );
          }
          else
          {
            int identEnd = i + textPart.length( );
            String nxtChar = "";
            if( identEnd < text.length( ) )
              nxtChar = text.substring( identEnd, identEnd + 1 );
            
            if( nxtChar.equals( combSign ) )
            {
              gpbe = new GofPushButtonElement( i, ident + equalSign );
              gpbe.combType = 1;
            }
            else if( nxtChar.equals( equalSign ) )
              gpbe = new GofPushButtonElement( i, ident + equalSign );
          }
          if( gpbe != null )
            pbElements.addElement( gpbe );
          i = i + idLen - 1;
          break;
        }
      }
    }
    
    return pbElements;
  }
  
  private String changeToWC( String text )
  {
    char[] textc = text.toCharArray( );
    for( int i = 0, s = textc.length; i < s; i++ )
    {
      if( textc[i] >= '0' && textc[i] <= '9' )
        textc[i] = '#';
    }
    return new String( textc );
  }
  
  /*
   * Will search for a single pushbutton identification string in a text. During
   * the search it will check if there is a numeric character where the identification
   * string has a numeric wildcard.
   * <p>
   * The numeric wildcard character in the identification string must be the hashmark '#'.
   * @param text     The text to search.
   * @param ident    The identification string to search for.
   * @param startpos The startpos to search from.
   * @return If a match was found, the startpos for the text matching the identification 
   *         string, or -1 if no match.
   *
  private int findSubstrWithWildcards( String text, String ident, int startpos )
  {
    boolean isEqual = true;
    int retVal = -1;

    String sc = ident.substring( 0, 1 );
    if( sc.equals( "#" ) == false )
    {
      int pos = text.indexOf( sc, startpos );
      while( pos > -1 )
      {
        for( int i = 0, s = ident.length( ); i < s; i++ )
        {
          if( pos + i >= text.length( ) )
          {
            // End of text reached.
            isEqual = false;
            break;
          }
          char c1 = ident.charAt( i );
          char c2 = text.charAt( pos + i );
          if( c1 == '#' )
          {
            // Check for numeric character.
            if( c2 < 0x30 || c2 > 0x39 )
            {
              isEqual = false;
              break;
            }
          }
          else if( c1 != c2 )
          {
            isEqual = false;
            break;
          }
        }

        if( isEqual == true )
        {
          // Substring found, stop searching for more.
          retVal = pos;
          break;
        }
        pos = text.indexOf( sc, pos + 1 );
        isEqual = true;
      }
    }
    else
    {
      // NOTYET! ident string starting with wildcard.
    }
    return retVal;
  }*/

  /**
   * Creates push buttons or menu items from push button elements.
   * This class takes every unused protected host field, and check if it has, what is 
   * called a pushbutton identification string. The pushbutton identification strings 
   * are text strings that identify a function key definition. The pushbutton 
   * identification strings are defined in the configuration file. When a host field is 
   * found to have one or more pushbutton identification string in it's text, a 
   * pushbutton element is created for each of them.
   * <p>
   * The pushbuttons elements text will also be analyzed for the right send key.
   * <p>
   * For each of the pushbutton elements, a push button or a menu item will be created. 
   * There are settings in the configuration file that determines which type of control 
   * that will be created.
   * @param areaIdentifier    The area identifier who's pushbutton elements are analyzed.
   * @param pbElements        A vector containing all the pbElements found in the currently processed hostfield.
   * @param text              The hostfield text.
   * @param gofHostField      The Gui-on-the-fly host field that is processed.
   * @param phantomHostScreen The Phantom host screen corresponding to the host screen.
   * @param hostScreen        The host screen that we are trying to build a GOF panel for.
   * @param newPanel          The newly created Gui-on-the-fly runtime panel.
   */
  private void createPushButtons( GofHostAreaIdentifier areaIdentifier, 
                                  Vector<GofPushButtonElement> pbElements, 
                                  String text, 
                                  GofHostField gofHostField, 
                                  PhantomHostScreen phantomHostScreen,
                                  HostScreen hostScreen, 
                                  PhantomPanelData newPanel,
                                  int offsetX,
                                  int offsetY )
  {
    int prevCombEnd = -1;
    
    for( int i = 0, s = pbElements.size( ); i < s; i++ )
    {
      GofPushButtonElement curPBElement = pbElements.elementAt( i );
      GofPushButtonElement nxtPBElement;
      if( i < pbElements.size( ) - 1 )
        nxtPBElement = pbElements.elementAt( i + 1 );
      else
        nxtPBElement = null;
      GofPushButtonElement nxtPBElement2;
      if( i < pbElements.size( ) - 2 )
        nxtPBElement2 = pbElements.elementAt( i + 2 );
      else
        nxtPBElement2 = null;
      
      int x, cx, combPos, equalPos;
      int y = gofHostField.getY( );
      String key, keyText;
      int txtStart = curPBElement.startpos;
      int txtEnd = txtStart;
      switch( curPBElement.combType )
      {
        case 1:
          x = gofHostField.getX( ) + txtStart;
          if( nxtPBElement2 != null )
            txtEnd = nxtPBElement2.startpos;
          else
            txtEnd = text.length( );
          txtEnd = getPrematureEnd( text, txtStart, txtEnd );
          
          combPos = text.indexOf( combSign, txtStart );
          if( combPos > txtStart )
            key = text.substring( txtStart, combPos );
          else
            key = "";
          
          equalPos = text.indexOf( equalSign, combPos );
          combPos = text.lastIndexOf( combSign, txtEnd );
          if( combPos > -1 && combPos < txtEnd )
            keyText = text.substring( equalPos + 1, combPos );
          else
            keyText = "";
              
          curPBElement.setSendKeyAndText( key + equalSign + keyText );
          cx = keyText.length( ) + 4;
          prevCombEnd = x + cx;
          break;
        case 2:
          if( nxtPBElement2 != null )
            txtEnd = nxtPBElement2.startpos;
          else
            txtEnd = text.length( );
          txtEnd = getPrematureEnd( text, txtStart, txtEnd );
          
          equalPos = text.indexOf( equalSign, txtStart );
          if( equalPos > txtStart )
            key = text.substring( txtStart, equalPos );
          else
            key = "";
          
          combPos = text.lastIndexOf( combSign, txtEnd );
          if( combPos > -1 && combPos < txtEnd )
            keyText = text.substring( combPos + 1, txtEnd );
          else
            keyText = "";
          
          curPBElement.setSendKeyAndText( key + equalSign + keyText );
          cx = keyText.length( ) + 4;
          if( prevCombEnd > -1 )
          {
            x = prevCombEnd + 1;
            prevCombEnd = -1;
          }
          else
            x = txtEnd - cx;
          break;
        default:
          x = gofHostField.getX( ) + txtStart;
          if( nxtPBElement != null )
            txtEnd = nxtPBElement.startpos;
          else
            txtEnd = text.length( );
          txtEnd = getPrematureEnd( text, txtStart, txtEnd );
          curPBElement.setSendKeyAndText( text.substring( txtStart, txtEnd ).trim( ) );
          cx = txtEnd - txtStart;
          break;
      }

      // Create the PhantomHostField.

      PhantomHostField phf = new PhantomHostField( phantomHostScreen, hostScreen, x, y, cx );

      // Create the base control and pushbutton or menuitem.

      createPushButtonOrMenuItem( areaIdentifier, newPanel, curPBElement, phf, x, y, cx, offsetX, offsetY );
    }
  }

  /**
   * Sometimes the end of the last pushbutton elements text will contain text that is not 
   * connected to the push button. This methods implements a simple algorithm to check for
   * a premature end, by checking for at least two consecutive spaces in the text.
   * @param text  The text to search for a premature end.
   * @param start The position to start the search from.
   * @param end   The position to stop the search from.
   * @return The position of the premature end, or the stop position for the search if 
   *         no premature end was found.
   */
  private int getPrematureEnd( String text, int start, int end )
  {
    int p = text.indexOf( "  ", start );
    if( p > -1 && p < end )
      return p;
    else
      return end;
  }

  /**
   * Creates a push button or menu item from a single GofPushButtonElement.
   * <p>
   * There are several settings that will be taken from the configuration file, 
   * that are needed during the identification of the pushbutton controls.
   * <p>
   * There is one setting that should contain the pushbutton identification strings. 
   * This setting is a comma-separated list, with all the identification strings. This 
   * means, that the identification strings can't contain commas. The identification 
   * strings will be combined with the value of the pushbuttonequalsign setting, to 
   * create the real identification string to search for. The reason for that the equal 
   * sign is specified separately, is that if push button combinations are allowed, this 
   * class has to have the identification string without the equal sign.
   * <p>
   * It is possible to use a wildcard for numeric characters in the identification strings. 
   * The character used as a wildcard is the hash mark ('#').
   * <p>
   * The pushbuttonequalsign setting will usually be a equal sign ('=') or a colon (':').
   * <p>
   * Below is an example of pushbutton identification strings with wildcards, and a equal 
   * sign as the pushbuttonequalsign.
   * <pre>
   *    pushbuttonident=PF#,PF##,F#,F##
   *    pushbuttonequalsign==
   * </pre>
   * Remember that the host field's text will be searched for matches in the same order as 
   * the identification strings are placed in the setting.
   * <p>
   * There is also a setting that specifies if push button combinations should be searched for. 
   * A pushbutton combination is when to push button definitions have been nestled together, 
   * separated by separator character. An example of a pushbutton combination could be: 
   * <pre>
   *    PF7/PF8=Previous/Next
   * </pre>
   * If the setting pushbuttoncombination is set to 1 (one), then this type of combination is searched 
   * for. This also requires that the pushbuttoncombsign be set to the separator character. In 
   * the above example, the separator character is the slash ('/'). Below these two setting are 
   * set to handle the above example:
   * <pre>
   *    pushbuttoncombination=1
   *    pushbuttoncombsign=/
   * </pre>
   * <p>
   * This class also has a setting that affects the look of the entry fields. This is the 
   * pushbuttonlayout setting. Valid values for this setting are:
   * <pre>
   *    entryfieldlayout=DEFAULT
   *    entryfieldlayout=SIZE
   *    entryfieldlayout=FONT
   *    entryfieldlayout=SIZEANDFONT
   *    entryfieldlayout=CLONE
   * </pre>
   * <p>
   * DEFAULT means that no settings are taken from the template panel. Size will be taken 
   * from the size used by the definition on the host screen. The panels default font will 
   * be used.
   * <p>
   * SIZE means that the size will be taken from the template panel, from a pushbutton with 
   * the id=PB_X. The panels default font will be used. If this control cannot be found, 
   * default values will be used.
   * <p>
   * FONT means that the font is taken from template panel, from a pushbutton with the id=PB_X. 
   * If this control cannot be found, default values will be used.
   * <p>
   * SIZEANDFONT means that the font and size are taken from template panel, from a pushbutton 
   * with the id=PB_X. If this control cannot be found, default values will be used.
   * <p>
   * CLONE means that the button on the template panel is cloned. Cloned means that the size, 
   * font, and an icon, if it exists, are taken from a pushbutton with the id=PB_Fnn (nn is 
   * one or two numeric characters, an exact match must be found). If this control cannot be 
   * found, default values will be used.
   * <p>
   * Any other value will be treated as DEFAULT.
   * <p>
   * There is a possibility to let pushbuttons not found in the template file be created as 
   * menu items instead. This is only possible when the layout is cloned from the template file, 
   * because pushbuttons not found there would get a different look from the cloned ones. For 
   * this there are two settings. The <code>nontemplatebutton</code> setting turns this option 
   * on and the <code>nontemplatemenu</code> setting specifies the menu name to use for the menu 
   * items. The value for the menu name can have a shortcut specified by placing a tilde ('~') in 
   * front of the character to be used as a shortcut. Below is an example of this setting.
   * <pre>
   *    nontemplatebutton=MENU
   *    nontemplatemenu=~Functions
   * </pre>
   * @param areaIdentifier The area identifier who's pushbutton elements are analyzed.
   * @param newPanel       The newly created Gui-on-the-fly runtime panel.
   * @param pbElement      The pushbutton element that should be turned into a pushbutton or menuitem.
   * @param phf            The PhantomHostField that the current pushbutton element is part of.
   * @param x              The x-position.
   * @param y              The y-position.
   * @param cx             The length of the push button element in characters.
   */
  private void createPushButtonOrMenuItem( GofHostAreaIdentifier areaIdentifier, 
                                           PhantomPanelData newPanel, 
                                           GofPushButtonElement pbElement, 
                                           PhantomHostField phf, 
                                           int x, 
                                           int y, 
                                           int cx,
                                           int offsetX,
                                           int offsetY )
  {
    PhantomControlBase bc;
    PhantomCPushButton pb = null;
    int pbCx = cx * GOF_GUIUNITX - 2;
    int pbCy = GOF_GUIUNITY + 2;
    int font = -1;
    PhantomControl templateControl;

    if( layout.equals( "SIZE" ) )
    {
      templateControl = templPanel.getControlFromID( "PB_X" );
      if( templateControl != null && templateControl.controlBase.type == CTRLTYPE_BUTTON )
      {
        pbCx = templateControl.controlBase.cx;
        pbCy = templateControl.controlBase.cy;
      }
      bc = new PhantomControlBase( GOF_MARGINX + ( x - offsetX ) * GOF_STEPX,
                                   GOF_MARGINY + ( y - offsetY ) * GOF_STEPY - 1,
                                   pbCx,
                                   pbCy,
                                   CTRLTYPE_BUTTON );
      pb = new PhantomCPushButton( newPanel, bc, phf, pbElement.pbText );
      pb.sendKey = pbElement.sendKey;
    }
    else if( layout.equals( "FONT" ) )
    {
      templateControl = templPanel.getControlFromID( "PB_X" );
      if( templateControl != null && templateControl.controlBase.type == CTRLTYPE_BUTTON )
      {
        font = ( ( PhantomCPushButton )templateControl ).font;
      }
      bc = new PhantomControlBase( GOF_MARGINX + ( x - offsetX ) * GOF_STEPX,
                                   GOF_MARGINY + ( y - offsetY ) * GOF_STEPY - 1,
                                   pbCx,
                                   pbCy,
                                   CTRLTYPE_BUTTON );
      pb = new PhantomCPushButton( newPanel, bc, phf, pbElement.pbText );
      pb.font = font;
      pb.sendKey = pbElement.sendKey;
    }
    else if( layout.equals( "SIZEANDFONT" ) )
    {
      templateControl = templPanel.getControlFromID( "PB_X" );
      if( templateControl != null && templateControl.controlBase.type == CTRLTYPE_BUTTON )
      {
        pbCx = templateControl.controlBase.cx;
        pbCy = templateControl.controlBase.cy;
        font = ( ( PhantomCPushButton )templateControl ).font;
      }
      bc = new PhantomControlBase( GOF_MARGINX + ( x - offsetX ) * GOF_STEPX,
                                   GOF_MARGINY + ( y - offsetY ) * GOF_STEPY - 1,
                                   pbCx,
                                   pbCy,
                                   CTRLTYPE_BUTTON );
      pb = new PhantomCPushButton( newPanel, bc, phf, pbElement.pbText );
      pb.font = font;
      pb.sendKey = pbElement.sendKey;
    }
    else if( layout.equals( "CLONE" ) )
    {
      templateControl = templPanel.getControlFromID( pbElement.layoutId );
      if( templateControl != null && templateControl.controlBase.type == CTRLTYPE_BUTTON )
      {
        pb = ( PhantomCPushButton )( ( PhantomCPushButton )templateControl ).clone( );
        pb.controlBase.x = GOF_MARGINX + ( x - offsetX ) * GOF_STEPX;
        pb.controlBase.y = GOF_MARGINY + ( y - offsetY ) * GOF_STEPY - 1;
      }
      else if( templateControl == null && nonTemplateButton.equals( "MENU" ) )
      {
        PhantomMenuItem menuItem = new PhantomMenuItem( newPanel, 1000 + menuItems.size( ) + 1, pbElement.pbText, pbElement.sendKey );
        menuItems.addElement( menuItem );
      }
    }
    else
    {
      bc = new PhantomControlBase( GOF_MARGINX + ( x - offsetX ) * GOF_STEPX,
                                   GOF_MARGINY + ( y - offsetY ) * GOF_STEPY - 1,
                                   pbCx,
                                   pbCy,
                                   CTRLTYPE_BUTTON );
      pb = new PhantomCPushButton( newPanel, bc, phf, pbElement.pbText );
      pb.sendKey = pbElement.sendKey;
    }

    // Add the push button.

    if( pb != null )
    {
      newPanel.addControl( pb );
      areaIdentifier.addControl( pb );
    }
  }

  // ==================================================================
  // GofPushButtonElement subclass
  // ==================================================================

  /**
   * The GofPushButtonIdentifier will make use of a private inner class named 
   * GofPushButtonElement. This is used to store information about the push button 
   * element found during the matching with identification strings. This information 
   * is the used during the creation of the pushbutton control or the menu item.
   * <p>
   * The GofPushButtonElement also has a method to get the send key string that will 
   * be attached to the control.
   */
  private class GofPushButtonElement
  {
    /**
     * The startposition of this element's text in the host field text.
     */
    private int startpos;

    /**
     * The identification string that matched this push button element.
     */
    private String ident;

    /**
     * The push button element's send key.
     */
    private int sendKey;

    /**
     * The push button element's text.
     */
    private String pbText;

    /**
     * The layout id to search for in the template for cloning.
     */
    private String layoutId;
    
    private int combType = 0;

    // -----------
    // CONSTRUCTOR
    // -----------

    /**
     * Creates a new push button element.
     * @param startpos The startposition of the element's text in the host field text.
     * @param ident    The identification string that matched this push button element.
     */
    private GofPushButtonElement( int startpos, String ident )
    {
      this.startpos = startpos;
      this.ident = ident;
    }

    // ----------------
    // INSTANCE METHODS
    // ----------------

    /**
     * Sets the push button element's send key and the text for pushbutton.
     * @param text The original text for the push button element.
     */
    private void setSendKeyAndText( String text )
    {
      if( text == null || text.equals( "" ) )
        return;

      int numStart = ident.indexOf( "#" );
      int numEnd = ident.lastIndexOf( "#" );
      String num = text.substring( numStart, numEnd + 1 ).trim( );
      layoutId = "PB_F" + num;
      String sendString = null;
      try
      {
        int numVal = Integer.valueOf( num ).intValue( );
        if( numVal > 0 && numVal < 10 )
        {
          sendString = "@" + num;
        }
        else if( numVal >= 10 && numVal <= 24 )
        {
          char[] c = new char[2];
          c[0] = '@';
          c[1] = ( char )( 'a' + ( numVal - 10 ) );
          sendString = new String( c );
        }

        if( sendString != null )
        {
          for( int i = 0, s = HostSendKeys.phantomSendKey.length; i < s; i++ )
          {
            if( sendString.equals( HostSendKeys.phantomSendKey[i] ) )
            {
              sendKey = i + 1;
              break;
            }
          }
        }
      }
      catch( NumberFormatException e )
      {
      }

      pbText = ( text.substring( ident.length( ) ) ).trim( );
    }
  }
}