Listing of Source terminal/TerminalApplicationProperties.java
package se.entra.phantom.server;

import java.awt.Color;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.util.StringTokenizer;
import se.entra.phantom.common.TerminalKeys;

/**
 * This class handles the Terminal Application properties popup panel PROPS.
 */
public class TerminalApplicationProperties extends VirtualComponentAdapter implements TerminalKeys
{
  /**
   * Static list of available colors.
   */
  private static final String [] acolors =
    {
    "Background",
    "Dark blue",
    "Dark green",
    "Dark cyan",
    "Dark red",
    "Magenta",
    "Brown",
    "Pale gray",
    "Dark gray",
    "Bright blue",
    "Bright green",
    "Bright cyan",
    "Bright red",
    "Pink",
    "Yellow",
    "White"
    };

  /**
   * The table of key names mapped to real keys.
   */
  public final static int [] keyMap =
    {
    KeyEvent.VK_F1,            //  1
    KeyEvent.VK_F2,            //  2
    KeyEvent.VK_F3,            //  3
    KeyEvent.VK_F4,            //  4
    KeyEvent.VK_F5,            //  5
    KeyEvent.VK_F6,            //  6
    KeyEvent.VK_F7,            //  7
    KeyEvent.VK_F8,            //  8
    KeyEvent.VK_F9,            //  9
    KeyEvent.VK_F10,           // 10
    KeyEvent.VK_F11,           // 11
    KeyEvent.VK_F12,           // 12
    KeyEvent.VK_ESCAPE,        // 13
    KeyEvent.VK_TAB,           // 14
    KeyEvent.VK_CONTROL,       // 15
    KeyEvent.VK_PRINTSCREEN,   // 16
    KeyEvent.VK_SCROLL_LOCK,   // 17
    KeyEvent.VK_PAUSE,         // 18
    KeyEvent.VK_INSERT,        // 19
    KeyEvent.VK_HOME,          // 20
    KeyEvent.VK_PAGE_UP,       // 21
    KeyEvent.VK_DELETE,        // 22
    KeyEvent.VK_END,           // 23
    KeyEvent.VK_PAGE_DOWN,     // 24
    KeyEvent.VK_UP,            // 25
    KeyEvent.VK_LEFT,          // 26
    KeyEvent.VK_DOWN,          // 27
    KeyEvent.VK_RIGHT,         // 28
    KeyEvent.VK_ADD,           // 29
    KeyEvent.VK_ENTER,         // 30
    KeyEvent.VK_BACK_SPACE,    // 31
    KeyEvent.VK_SUBTRACT,      // 32
    KeyEvent.VK_CONTROL|0x1000,// 33
    KeyEvent.VK_ENTER|0x1000   // 34
    };
  
  /**
   * Table of keys that can be mapped to hot spots.
   */
  private final static int [] hotSpotKeys =  
    {
    TerminalKeys.KEY_PF1,   
    TerminalKeys.KEY_Enter,  
    TerminalKeys.KEY_Attention,  
    TerminalKeys.KEY_Clear,  
    TerminalKeys.KEY_Help,  
    TerminalKeys.KEY_PA1,  
    TerminalKeys.KEY_PA2,  
    TerminalKeys.KEY_PA3,  
    TerminalKeys.KEY_PageUp,  
    TerminalKeys.KEY_PageDown
    };
  
  private static final String HOTSPOT_DELIMITER = "§"; 
  
  ///
  
  /**
   * Displays and processes the host client properties for the server administration program.
   */
  public static void processAdminHostClientProperties(VirtualSessionManager vsm)
    {
    TerminalApplicationProperties tap=new TerminalApplicationProperties();
    TerminalSettings ts=new TerminalSettings();
    ts.getProperties(ClientSessionManager.getServerIni());
    tap.terminalSettings=ts;
    VirtualPanel vp=vsm.createPanel("PROPSADM",tap);
    vp.setEnabled("APPLY",false);
    VirtualPanel vp2=vp.getFirstNoteBookPagePanel(0); if ( vp2!=null ) vp2.setEnabled("CURRWIN",false);
    vp2=vp.getFirstNoteBookPagePanel("KEYBOARD"    ); if ( vp2!=null ) vp2.setEnabled("SRVKEY" ,false);
    vp2=vp.getFirstNoteBookPagePanel("HOTSPOT"     ); if ( vp2!=null ) vp2.setEnabled("SRVHS"  ,false);
    }

  ///

  /**
   * The currently edited colors.
   */
  private HostColors colors;
  
  /**
   * Is editor instance.
   */
  private boolean isEditor;

  /**
   * The color names.
   */
  private final String [] colorNames = new String [16];
  
  /**
   * The terminal settings.
   */
  private TerminalSettings terminalSettings;

  /**
   * The keyboard remapper.
   */
  private KeyboardRemapper keyboardRemapper;

  ///

  /**
   * Called to initialize the panel before it is displayed.
   *
   * <p>This function doesn't perform anything.
   */
  @Override
  public void onPanelCreate(VirtualPanel vp)
    {
    TerminalSettings ts=terminalSettings;
    if ( ts==null )
      {
      TerminalFunctions tf=vp.getVirtualSessionManager().getTerminalFunctions();
      ts=tf.settings;
      }
    
    // Set whether running in editor mode.
    isEditor = vp.getVirtualSessionManager().isEditor();
    boolean isServerAdmin = vp.getVirtualSessionManager().isServerAdmin;
    
    // Get currently defined colors.
    HostColors c; 
    /*if (isEditor)
      {
      IniFile ini = ClientSessionManager.getServerIni();
      String clr = ini.getData("host","TerminalHostColors","0000009191ff6eff6e80ffffff4040ff6effffff00e0e0e0aaaaaaccccffccffccccffffff8080ffccffffffccffffff");
      if ( clr!=null && clr.length()==96 )
        {
        // It's a hex string.
        colors=new HostColors();
        for ( int ii=0; ii<16; ++ii )
          colors.setColor(new Color(fromHex(clr,ii*6)),ii);
        }
      else
        System.out.println("#TerminalApplicationProperties: Editor terminal COLORS not found!");
      }
    else*/
      {
      c=ts.colors;
      if ( c==null )
        c=vp.getClientSession().getHostSessionManager().getCurrentHostColors();
      
      colors=new HostColors(c);
      }
    
    keyboardRemapper=(ts.keyboardRemapper!=null)?
                       new KeyboardRemapper(ts.keyboardRemapper):
                       KeyboardRemapper.cloneCurrentKeyboardRemapper();
    VirtualCNoteBook nb=vp.getFirstNoteBook();
    if ( nb==null )
      {
      System.out.println("#TerminalApplicationProperties: Notebook not found!");
      return;
      }

    // Cursor page (now renamed to General in the panel).
    VirtualPanel page=nb.getPagePanel("CURSOR");
    if ( page==null )
      System.out.println("#TerminalApplicationProperties: Notebook page CURSOR not found!");
    else
      {
      page.setListener(this);
      if ( !ts.dontOutlinePresentationSpace ) page.setChecked("OUTLINE" ,1);
      if ( ts.is3270BoxDrawingEnabled       ) page.setChecked("BOXDRAW" ,1);
      if ( ts.isCursorNonBlinking           ) page.setChecked("NONBLINK",1);
      if ( !ts.isHostAlarmEnabled           ) page.setChecked("DISALARM",1);
      if ( ts.doRuleCursor                  ) page.setChecked("RULECUR" ,1); else onCheckedChange(page,null,"RULECUR",0);
      if ( (ts.ruleCursorOrientation&1)!=0  ) page.setChecked("VERT"    ,1);
      if ( (ts.ruleCursorOrientation&2)!=0  ) page.setChecked("HORZ"    ,1);
      if      ( ts.ruleCursorLineStyle==0   ) page.setChecked("SOLID"   ,1);
      else if ( ts.ruleCursorLineStyle==2   ) page.setChecked("DASH1"   ,1);
      else if ( ts.ruleCursorLineStyle==3   ) page.setChecked("DASH2"   ,1);
      else if ( ts.ruleCursorLineStyle==4   ) page.setChecked("DASH3"   ,1);
      else                                    page.setChecked("DOT"     ,1);
      page.setText("CCY" ,Integer.toString(ts.cursorHeight));
      page.setText("CCY2",Integer.toString(ts.cursorHeightInsertMode));
      Rectangle r=new Rectangle();
      String s=ClientSessionManager.getServerIni().getData("host","windowBounds");
      try
        {
        StringTokenizer t=new StringTokenizer(s,",",false);
        int x =Integer.parseInt(t.nextToken());
        int y =Integer.parseInt(t.nextToken());
        int cx=Integer.parseInt(t.nextToken());
        int cy=Integer.parseInt(t.nextToken());
        r.x=x;
        r.y=y;
        r.width =cx;
        r.height=cy;
        }
      catch(NumberFormatException e) {}
      if ( !((ClientSession)vp.getClientSession()).isServerAdministration() )
        {
        TerminalFunctions tf=vp.getVirtualSessionManager().getTerminalFunctions();
        tf.getClientTerminalMark();
        Rectangle r2=new Rectangle(ts.windowBounds);
        if ( r2.width==0 )
          r2=new Rectangle(ts.currentWindowBounds);
        if ( r2.width!=0 )
          r=r2;
        }
      if ( r.height<0 )
        {
        page.setChecked("MAXIMIZE",1);
        r.height=-r.height;
        }
      page.setText("X" ,Integer.toString(r.x     ));
      page.setText("Y" ,Integer.toString(r.y     ));
      page.setText("CX",Integer.toString(r.width ));
      page.setText("CY",Integer.toString(r.height));
      }

    // Edit page.
    page=nb.getPagePanel("EDIT");
    if ( page==null )
      System.out.println("#TerminalApplicationProperties: Notebook page EDIT not found!");
    else
      {
      page.setListener(this);
      if ( ts.doSaveOriginalFieldSpacing )
        {
        page.setChecked("SPACING",1);
        page.setEnabled("FIELD" ,false);
        page.setEnabled("WORD"  ,false);
        page.setEnabled("PROMPT",false);
        }
      if      ( ts.cutCopyParsing==1     ) page.setChecked("WORD"    ,1);
      else if ( ts.cutCopyParsing==2     ) page.setChecked("DATAONLY",1);
      else                                 page.setChecked("FIELD"   ,1);
      if ( ts.doPromptOnParse            ) page.setChecked("PROMPT"  ,1);
      if ( ts.doPasteTextWrapping        ) page.setChecked("WRAP"    ,1);
      if ( ts.doKeyboardAutoReset        ) page.setChecked("AUTORES" ,1);
      if ( ts.isTypeAheadEnabled         ) page.setChecked("TYPEAH"  ,1);
      if ( ts.isSmartInsertModeEnabled   ) page.setChecked("SMARTINS",1);
      if ( ts.isPCInsertModeEnabled      ) page.setChecked("PCINS"   ,1);
      }

    // Font page.
    page=nb.getPagePanel("FONT");
    if ( page==null )
      System.out.println("#TerminalApplicationProperties: Notebook page FONT not found!");
    else
      {
      page.setListener(this);
      VirtualInterface list=page.getControlFromID("FONTS");
      if ( list==null )
        System.out.println("#TerminalApplicationProperties: Listbox FONTS not found in notebook page FONT!");
      else
        {
        TerminalFunctions tf=vp.getVirtualSessionManager().getTerminalFunctions();
        int sel=-1,defSel=-1;
        String fnDef=ts.fontName;
        if ( fnDef==null )
          fnDef="Monospaced";
        int cc=0;
        Vector<String> v=FileListContent.sortList(tf.getClientFixedFontList());
        for ( Enumeration<String> e=v.elements(); e.hasMoreElements(); )
          {
          String fn=e.nextElement();
          list.insertLine(fn,-1);
          if ( fn.equalsIgnoreCase("Monospaced") )
            defSel=cc;
          if ( fn.equalsIgnoreCase(fnDef) )
            sel=cc;
          ++cc;
          }
        if ( sel<0 )
          sel=defSel;
        if ( sel>=0 )
          list.setSelection(sel,true);
        if ( ts.isBoldFont        ) page.setChecked("BOLD"  ,1);
        if ( ts.doAntiAlias       ) page.setChecked("AA"    ,1);
        if ( ts.doKeepAspectRatio ) page.setChecked("ASPECT",1);
        if ( ts.fontSize==0 )
          {
          page.setChecked("AUTO",1);
          page.setEnabled("SIZE",false);
          page.setText("SIZE","14");
          }
        else
          {
          page.setEnabled("ASPECT",false);
          page.setText("SIZE",Integer.toString(ts.fontSize));
          }
        }
      }

    // Color page.
    if ( vp.getPanelID().startsWith("PROPS") )
      {
      page=nb.getPagePanel("COLORS");
      if ( page==null )
        {
        if (!isServerAdmin && !isEditor)
          System.out.println("#TerminalApplicationProperties: Notebook page COLORS not found!");
        }
      else
        {
        page.setListener(this);
        VirtualInterface list=page.getControlFromID("ACLR");
        if ( list==null )
          System.out.println("#TerminalApplicationProperties: User window ACLR not found in notebook page COLORS!");
        else
          {
          for ( int ii=0; ii<16; ++ii )
            {
            String s=page.getRuntime().getTextID("CLR"+ii);
            if ( s==null )
              s=acolors[ii];
            list.insertLine(Integer.toString(colors.getColorRGB(ii))+"\t"+s,-1);
            colorNames[ii]=s;
            }
          list.setSelection(0,true);
          }
        page.setText("TERMCLR",Integer.toString(colors.getColorRGB(0)));
        }
      }

    // Keyboard page.
    if ( vp.getPanelID().startsWith("PROPS") )
      {
      page=nb.getPagePanel("KEYBOARD");
      if ( page==null )
        {
        if (!isServerAdmin)
          System.out.println("#TerminalApplicationProperties: Notebook page KEYBOARD not found!");
        }
      else
        page.setListener(this);
      }

    // Hot spot page.
    page=nb.getPagePanel("HOTSPOT");
    if ( page==null )
      System.out.println("#TerminalApplicationProperties: Notebook page HOTSPOT not found!");
    else
      {
      page.setListener(this);

      page.setChecked("LINKS",ts.areLinksEnabled? 1: 0);
      page.setText("INITTEXT",ts.linkTexts);
      
      page.setChecked("FKEYS",ts.areKeyHotSpotsEnabled? 1: 0);     
      for ( int k: hotSpotKeys )
        {
        String s="|"+Integer.toString(k)+"|";
        String sl=(k==TerminalKeys.KEY_PF1?"Key PF#":KeyboardRemapper.getKeyName(k))+"\t";
        StringTokenizer t=new StringTokenizer(ts.keyHotSpotStrings,HOTSPOT_DELIMITER,false);
        while ( t.hasMoreTokens() )
          {
          String item=t.nextToken();
          if ( item.startsWith(s) )
            {
            sl+=item.substring(s.length());
            break;
            }
          }
        page.insertLine("LIST",sl,-1);
        }
      }
    }

  /**
   * Called to inform the listener that the panel is destroyed.
   * Here, we enable all menu items in the main terminal
   * panel.
   */
  @Override
  public void onPanelDestroy(VirtualPanel vp)
    {
    TerminalApplication ta=vp.getVirtualSessionManager().getTerminalApplication();
    if ( ta!=null )
      ta.enableTopMenuItems(true);
    }

  /**
   * Check boxes and radio buttons generates this event when state is changed on the client side.
   *
   * <p>The state is 0=unchecked, 1=checked and 2=third state (only for check boxes,
   * or PhantomCCheckBox.STATE_UNDETERMINED).
   */
  @Override
  public void onCheckedChange(VirtualPanel vp,VirtualControl control,String controlID,int state)
    {
    if ( controlID.equals("RULECUR") )
      {
      boolean curOn=(state==1);
      vp.setEnabled("VERT",curOn);
      vp.setEnabled("HORZ",curOn);
      vp.setEnabled("SOLID",curOn);
      vp.setEnabled("DOT",curOn);
      vp.setEnabled("DASH1",curOn);
      vp.setEnabled("DASH2",curOn);
      vp.setEnabled("DASH3",curOn);
      }
    else
    if ( controlID.equals("AUTO") )
      {
      boolean auto=(control.isChecked()!=0);
      vp.setEnabled("SIZE"  ,!auto);
      vp.setEnabled("ASPECT", auto);
      }
    else
    if ( controlID.equals("SPACING") )
      {
      boolean off=(state==0);
      if ( !off )
        {
        vp.setChecked("FIELD"   ,0);
        vp.setChecked("WORD"    ,0);
        vp.setChecked("DATAONLY",1);
        vp.setChecked("PROMPT"  ,0);
        }
      vp.setEnabled("FIELD" ,off);
      vp.setEnabled("WORD"  ,off);
      vp.setEnabled("PROMPT",off);
      }
    }

  /**
   * A control notifies the listener using this event when custom properties
   * needs to be handled.
   *
   * <p>The object is in general dependent of the property change event string.
   */
  @Override
  public void onPropertyChange(VirtualPanel vp,VirtualControl control,String controlID,String propertyName,Object property)
    {
    if ( controlID.equals("TERMCLR") )
      {
      // New color selected.
      String clr=control.getText();
      int line=vp.getNextSelection("ACLR",-1);
      vp.setLine("ACLR",clr+"\t"+colorNames[line],line);
      colors.setColor(new Color(Integer.parseInt(clr)),line);
      }
    else
    if ( controlID.equals("ACLR") )
      {
      // Change of available color.
      int line=vp.getNextSelection("ACLR",-1);
      vp.setText("TERMCLR",Integer.toString(colors.getColorRGB(line)));
      }
    }

  /**
   * Push buttons and menu items generates this event when selected.
   */
  @Override
  public void onAction(VirtualPanel vp,String controlID)
    {
    if      ( controlID.equals("CANCEL" ) )  close(vp);
    else if ( controlID.equals("OK"     ) )  performOK(vp);
    else if ( controlID.equals("APPLY"  ) )  performApply(vp);
    else if ( controlID.equals("DEFCLR" ) )  performDefaultColors(vp);
    else if ( controlID.equals("SRVCLR" ) )  performServerColors(vp);
    else if ( controlID.equals("CURRWIN") )  performCurrentWindowSizePos(vp);
    else if ( controlID.equals("SRVKEY" ) )  performKeysFromServer(vp);
    else if ( controlID.equals("DEFKEY" ) )  performKeysDefault(vp);
    else if ( controlID.startsWith("KEY") )  performKeyProperties(vp,controlID);
    else System.out.println("#TerminalApplicationProperties, Command ID "+controlID+" not handled");
    }

  /**
   * Closes this panel.
   */
  private void close(VirtualPanel vp)
    {
    if ( vp.isModal() )
      vp.dismissPanel(0);
    else
      vp.destroy();
    }

  /**
   * Called when the close button of a panel is pressed,
   * but before any other processing has been done. If this
   * function returns false, the application close object
   * will be called (if defined), otherwise a faked key-press
   * of the Escape key is performed. If these two actions
   * failed (because there is no close object or no button or
   * menu item is connected to the Escape key), then the
   * function <code>onPanelClose</code> is called.
   *
   * @return  true  to cancel all default processing.
   */
  @Override
  public boolean onPanelClosing(VirtualPanel vp)
    {
    close(vp);
    return true;
    }

  /**
   * Performs the OK change. 
   * 
   * <p>Encodes the following data (in a string):
   *    Links: List of patterns separated by , 
   *    Keys: |<Key>|<List of patterns separated by ,>§;
   * 
   * @param vp  panel reference
   */
  public void performOK(VirtualPanel vp)
    {
    // Get all dialog box data.
    if ( !performApply(vp) )
      return;
  
    // Server admin: save in server.ini.
    if (((ClientSession)vp.getClientSession()).isServerAdministration() || ((ClientSession)vp.getClientSession()).isEditor())
      {
      // Get the properties to a hash table.
      terminalSettings = vp.getVirtualSessionManager().getTerminalFunctions().settings;
      Hashtable<String,String> ht=new Hashtable<String,String>();
      terminalSettings.saveProperties(ht);
      
      // If this is an editor instance, make sure that subsequent clients started have the configured host colors.
      if (isEditor)
        HostSessionManager.getTerminalSettings().colors = colors;
      
      // Remove HostColors and KeyboardRemapper entries, they are stored differently.
      ht.remove("HostColors");
      ht.remove("KeyboardRemapper");
      try
        {
        IniFile ini=ClientSessionManager.getServerIni();
        TerminalSettings.saveProperties(ht, ini);
        ini.saveFile();
        }
      catch (IOException e)
        {
        String s="Error saving INI file: "+e;
        vp.messageBox(MB_OK,ICON_CRITICAL,s,"Save settings");
        EventManager.logEvent(s,EventID.EVENTCLASS_WARNING);
        return;
        }
      close(vp);
      return;
      }

    // Save the changes.
    String err=vp.getVirtualSessionManager().getTerminalFunctions().saveClientProperties();
    if ( err==null )
      {
      // Remove panel.
      close(vp);
      return;
      }

    // Log the event.
    EventManager.logEvent(null,err,EventID.EVENTCLASS_ERROR);
    }

  /**
   * Applies the changes.
   * 
   * @return  true for OK, false to cancel.
   */
  public boolean performApply(VirtualPanel vp)
    {
    TerminalFunctions tf=vp.getVirtualSessionManager().getTerminalFunctions();
    TerminalSettings ts=tf.settings;
    VirtualCNoteBook nb=vp.getFirstNoteBook();
    if ( nb==null )
      return false;
    
    // First check values (Cursor[General] page
    // and font page.
    int x=0,y=0,cx=0,cy=0;
    int ccy=100,ccy2=100;

    // Check Cursor page.
    VirtualPanel page=nb.getPagePanel("CURSOR");
    if ( page!=null )
      {
      String err="CCY";
      try
        {
        ccy=Integer.parseInt(page.getText("CCY"));
        if ( ccy<1 || ccy>100 )
          throw new NumberFormatException("");
        err="CCY2";
        ccy2=Integer.parseInt(page.getText("CCY2"));
        if ( ccy2<1 || ccy2>100 )
          throw new NumberFormatException("");
        err="X" ; x =Integer.parseInt(page.getText("X" ));
        err="Y" ; y =Integer.parseInt(page.getText("Y" ));
        err="CX"; cx=Integer.parseInt(page.getText("CX"));
        err="CY"; cy=Integer.parseInt(page.getText("CY"));
        if ( page.isChecked("MAXIMIZE")==1 )
          cy=-cy;
        }
      catch(NumberFormatException e)
        {
        nb.turnToPage("CURSOR");
        vp.setFocus(err);
        vp.getClientSession().soundAlarm();
        return false;
        }
      }

    // Font page.
    int fontSize=0;
    page=nb.getPagePanel("FONT");
    if ( page!=null )
      {
      if ( page.isChecked("AUTO")==0 )
        {
        try
          {
          fontSize=Integer.parseInt(page.getText("SIZE"));
          if ( fontSize<4 || fontSize>128 )
            throw new Exception();
          }
        catch(Exception e)
          {
          nb.turnToPage("EDIT");
          vp.setFocus("SIZE");
          vp.getClientSession().soundAlarm();
          return false;
          }
        }
      }

    // Check the hot spot strings in the list.
    page=nb.getPagePanel("HOTSPOT");
    String keyList="";
    if ( page!=null )
      {
      for ( int ii=0, cc=page.getLineCount("LIST"); ii<cc; ++ii )
        {
        String s=page.getCell("LIST",1,ii);
        if ( s.indexOf('|')>=0 )
          {
          nb.turnToPage("HOTSPOT");
          page.setSelection("LIST",ii,true);
          page.setFocus("LIST");
          vp.getClientSession().soundAlarm();
          return false;
          }
        
        String parts[] = s.split(",");
        for (int jj = 0; jj < parts.length; jj++)
          {
          if ( parts[jj].indexOf('|')>=0 || (parts[jj].indexOf('#') != -1 && parts[jj].indexOf('#') != parts[jj].lastIndexOf('#')))
            {
            nb.turnToPage("HOTSPOT");
            page.setSelection("LIST",ii,true);
            page.setFocus("LIST");
            vp.getClientSession().soundAlarm();
            return false;
            }
          }
        
        keyList+="|" + Integer.toString(hotSpotKeys[ii])+"|"+s+HOTSPOT_DELIMITER;
        }
      }

    // Cursor page.
    page=nb.getPagePanel("CURSOR");
    if ( page!=null )
      {
      ts.windowBounds.x              =x;
      ts.windowBounds.y              =y;
      ts.windowBounds.width          =cx;
      ts.windowBounds.height         =cy;
      ts.dontOutlinePresentationSpace=(page.isChecked("OUTLINE" )==0);
      ts.is3270BoxDrawingEnabled     =(page.isChecked("BOXDRAW" )!=0);
      ts.isHostAlarmEnabled          =(page.isChecked("DISALARM")==0);
      ts.isCursorNonBlinking         =(page.isChecked("NONBLINK")!=0);
      ts.cursorHeight                =ccy;
      ts.cursorHeightInsertMode      =ccy2;
      ts.doRuleCursor                =(page.isChecked("RULECUR" )!=0);
      ts.ruleCursorOrientation=(page.isChecked("VERT")!=0? 1: 0) | (page.isChecked("HORZ")!=0? 2: 0);
      int rc=1;
      if      ( page.isChecked("SOLID")!=0 ) rc=0;
      else if ( page.isChecked("DASH1")!=0 ) rc=2;
      else if ( page.isChecked("DASH2")!=0 ) rc=3;
      else if ( page.isChecked("DASH3")!=0 ) rc=4;
      ts.ruleCursorLineStyle=rc;
      }

    // Edit page.
    page=nb.getPagePanel("EDIT");
    if ( page!=null )
      {
      if      ( page.isChecked("DATAONLY")!=0 ) ts.cutCopyParsing=2;
      else if ( page.isChecked("WORD"    )!=0 ) ts.cutCopyParsing=1;
      else ts.cutCopyParsing=0;
      ts.doPromptOnParse           =(page.isChecked("PROMPT"  )!=0);
      ts.doSaveOriginalFieldSpacing=(page.isChecked("SPACING" )!=0);
      ts.doPasteTextWrapping       =(page.isChecked("WRAP"    )!=0);
      ts.doKeyboardAutoReset       =(page.isChecked("AUTORES" )!=0);
      ts.isTypeAheadEnabled        =(page.isChecked("TYPEAH"  )!=0);
      ts.isSmartInsertModeEnabled  =(page.isChecked("SMARTINS")!=0);
      ts.isPCInsertModeEnabled     =(page.isChecked("PCINS"   )!=0);
      }

    // Font page.
    page=nb.getPagePanel("FONT");
    if ( page!=null )
      {
      ts.fontName         =page.getLine("FONTS",page.getNextSelection("FONTS",-1));
      ts.isBoldFont       =(page.isChecked("BOLD"  )!=0);
      ts.doAntiAlias      =(page.isChecked("AA"    )!=0);
      ts.doKeepAspectRatio=(page.isChecked("ASPECT")!=0);
      ts.fontSize   =fontSize;
      }
    
    // Set the colors.
    ts.colors=colors;
    
    // Save keyboard remapper.
    ts.keyboardRemapper=keyboardRemapper;
    
    // Set the hot spots.
    page=nb.getPagePanel("HOTSPOT");
    if ( page!=null )
      {
      ts.areLinksEnabled=(page.isChecked("LINKS")==1);
      ts.linkTexts=page.getText("INITTEXT");
      ts.areKeyHotSpotsEnabled=(page.isChecked("FKEYS")==1);
      ts.keyHotSpotStrings=keyList;
      }

    // Update the client.
    tf.updateClientTerminalSettings();
    
    // Return OK.
    return true;
    }

  /**
   * Applies the default colors.
   */
  public void performDefaultColors(VirtualPanel vp)
    {
    applyColors(vp,new HostColors());
    }

  /**
   * Applies the server colors for the current session index.
   */
  public void performServerColors(VirtualPanel vp)
    {
    // Get the colors.
    HostColors colors=vp.getClientSession().getHostSessionManager().getCurrentHostColors();
    applyColors(vp,colors);
    }

  /**
   * Gets the current window position and size.
   */
  public void performCurrentWindowSizePos(VirtualPanel vp)
    {
    TerminalFunctions tf=vp.getVirtualSessionManager().getTerminalFunctions();
    TerminalSettings ts=tf.settings;
    tf.getClientTerminalMark();
    Rectangle r=new Rectangle(ts.currentWindowBounds);
    vp.setChecked("MAXIMIZE",(r.height<0)? 1: 0);
    vp.setText("X" ,Integer.toString(r.x     ));
    vp.setText("Y" ,Integer.toString(r.y     ));
    vp.setText("CX",Integer.toString(r.width ));
    vp.setText("CY",Integer.toString(Math.abs(r.height)));
    }

  /**
   * Applies the colors (updates the listbox).
   */
  public void applyColors(VirtualPanel vp,HostColors colors)
    {
    // Update the listbox.
    for ( int ii=0; ii<16; ++ii )
      {
      this.colors.setColor(colors.getColor(ii),ii);
      vp.setLine("ACLR",Integer.toString(colors.getColorRGB(ii)),ii);
      }

    // Set the current color.
    int line=vp.getNextSelection("ACLR",-1);
    vp.setText("TERMCLR",Integer.toString(colors.getColorRGB(line)));
    }

  /**
   * Gets the current window position and size.
   */
  public void performKeyProperties(VirtualPanel vp,String controlID)
    {
    new TerminalApplicationKeyProperties(vp,controlID);
    }

  /**
   * Sets the keys from the server configuration.
   */
  private void performKeysFromServer(VirtualPanel vp)
    {
    keyboardRemapper=KeyboardRemapper.cloneCurrentKeyboardRemapper();
    }

  /**
   * Sets the keys as default.
   */
  private void performKeysDefault(VirtualPanel vp)
    {
    keyboardRemapper=new KeyboardRemapper();
    }


  ////////////////////////////////////////////////
  /// --- TerminalApplicationKeyProperties --- ///
  ////////////////////////////////////////////////

  /**
   * Handles the Key properties dialog box.
   */
  class TerminalApplicationKeyProperties extends VirtualComponentAdapter
    {
    /**
     * The key being edited.
     */
    private int key;

    /**
     * The host key array being edited.
     */
    private int [] hostKeys;
    
    ///

    /**
     * Creates and displays the Key properties dialog box.
     */
    TerminalApplicationKeyProperties(VirtualPanel vp,String controlID)
      {
      // Save key name and get the key value.
      String name=vp.getText(controlID);
      key=-1;
      for ( int d=0; d<controlID.length(); ++d )
        if ( Character.isDigit(controlID.charAt(d)) )
          {
          try
            {
            key=keyMap[Integer.parseInt(controlID.substring(d))-1];
            }
          catch(Exception e) {}
          break;
          }
      if ( key<0 )
        return;

      // Get the currently assigned keys.
      hostKeys=keyboardRemapper.getSendKeys(key);

      // Create panel and get listbox.
      VirtualSessionManager vsm=vp.getVirtualSessionManager();
      if ( ((ClientSession)vp.getClientSession()).isServerAdministration() )
        vp=vsm.createPanel("KEYPROP",this);
      else
        vp=vsm.getTerminalApplication().createPanel(vsm,"KEYPROP",this);
      if ( vp==null )
        return;
      
      // Set-up panel.
      vp.setText("NAME",name);
      setHostKeys(vp);
      
      // For Control and Right Control, disable all non-Control
      // combinations as they are impossible: Control key is
      // ALWAYS pressed for this key!
      if ( (key&0xFFF)==KeyEvent.VK_CONTROL )
        {
        vp.setEnabled("PROP1"  ,false);
        vp.setEnabled("PROP2"  ,false);
        vp.setEnabled("PROP5"  ,false);
        vp.setEnabled("PROP6"  ,false);
        vp.setEnabled("KEYCHG1",false);
        vp.setEnabled("KEYCHG2",false);
        vp.setEnabled("KEYCHG5",false);
        vp.setEnabled("KEYCHG6",false);
        }
      }
    
    /**
     * Sets the host keys selected in the output
     * text controls.
     */
    private void setHostKeys(VirtualPanel vp)
      {
      VirtualRuntime rt=vp.getRuntime();
      for ( int ii=0; ii<8; ++ii )
        {
        String name="-";
        int hk=hostKeys[ii];
        if ( hk>=0 )
          {
          name=rt.getTextID("KEY"+KeyboardRemapper.getKeyIndex(hk));
          if ( name==null )
            name=KeyboardRemapper.getKeyName(hk);
          }
        vp.setText("PROP"+(ii+1),name);
        }
      }

    /**
     * Called when the close button of a panel is pressed,
     * but before any other processing has been done. If this
     * function returns false, the application close object
     * will be called (if defined), otherwise a faked key-press
     * of the Escape key is performed. If these two actions
     * failed (because there is no close object or no button or
     * menu item is connected to the Escape key), then the
     * function <code>onPanelClose</code> is called.
     *
     * @return  true  to cancel all default processing.
     */
    @Override
    public boolean onPanelClosing(VirtualPanel vp)
      {
      close(vp);
      return true;
      }

    /**
     * Push buttons and menu items generates this event when selected.
     */
    @Override
    public void onAction(VirtualPanel vp,String controlID)
      {
      if      ( controlID.equals("CANCEL"    ) )  close(vp);
      else if ( controlID.equals("OK"        ) )  performOK(vp);
      else if ( controlID.equals("DEFAULT"   ) )  performDefault(vp);
      else if ( controlID.startsWith("KEYCHG") )  performAssignKey(vp,controlID.substring(6));
      else System.out.println("#TerminalApplicationKeyProperties, Command ID "+controlID+" not handled");
      }

    /**
     * Displays the assign key dialog box for this
     * augmentation.
     */
    private void performAssignKey(VirtualPanel vp,String id)
      {
      try
        {
        int augmentation=Integer.parseInt(id)-1;
        new TerminalApplicationAssignKey(vp,hostKeys,augmentation);
        }
      catch(NumberFormatException e) {}
      }

    /**
     * Resets this key to the default host keys.
     */
    private void performDefault(VirtualPanel vp)
      {
      hostKeys=KeyboardRemapper.getDefaultSendKeys(key);
      setHostKeys(vp);
      }

    /**
     * Performs the OK action, saves the edited
     * key selection for the host key in the
     * keyboard remapper.
     */
    private void performOK(VirtualPanel vp)
      {
      keyboardRemapper.addKey(key,hostKeys);
      close(vp);
      }
    }


  ////////////////////////////////////////////
  /// --- TerminalApplicationAssignKey --- ///
  ////////////////////////////////////////////

  /**
   * Handles the assignment of a key in the Key properties dialog box.
   */
  class TerminalApplicationAssignKey extends VirtualComponentAdapter
    {
    /**
     * The host key array being edited.
     */
    private final int [] hostKeys;

    /**
     * The augmentation index being edited.
     */
    private final int augmentation;
    
    /**
     * The table of key names.
     */
    private final Vector<String> names = new Vector<String>();

    ///

    /**
     * Creates and displays the Assign key dialog box.
     */
    TerminalApplicationAssignKey(VirtualPanel vp,int [] hostKeys,int augmentation)
      {
      this.hostKeys=hostKeys;
      this.augmentation=augmentation;

      // Create panel and get listbox.
      VirtualSessionManager vsm=vp.getVirtualSessionManager();
      if ( ((ClientSession)vp.getClientSession()).isServerAdministration() )
        vp=vsm.createPanel("KEYSET",this);
      else
        vp=vsm.getTerminalApplication().createPanel(vsm,"KEYSET",this);
      if ( vp==null )
        return;
      VirtualCListBox lb=(VirtualCListBox)vp.getControlFromID("KEYLIST");
      if ( lb==null )
        return;

      // Use key names from text file, otherwise
      // the English texts.
      VirtualRuntime rt=vp.getRuntime();
      String n=rt.getTextID("KEYNONE");
      if ( n==null )
        n="<None>";
      lb.insertLine(n,-1);
      String [] namesUS=KeyboardRemapper.getKeyNames();
      for ( int ii=0, cc=namesUS.length; ii<cc; ++ii )
        {
        n=vp.getRuntime().getTextID("KEY"+ii);
        if ( n==null )
          n=namesUS[ii];
        names.addElement(n);
        }
      
      // Sort vector and insert result it in listbox.
      FileListContent.sortList(names,lb);

      // Select the key that matches.
      int line=0;
      int hk=hostKeys[augmentation];
      if ( hk>=0 )
        {
        n=names.elementAt(KeyboardRemapper.getKeyIndex(hk));
        for ( int ii=0, cc=lb.getLineCount(); ii<cc; ++ii )
          if ( lb.getLine(ii).equals(n) )
            {
            line=ii;
            break;
            }
        }
      lb.setSelection(line,true);
      }

    /**
     * Called when the close button of a panel is pressed,
     * but before any other processing has been done. If this
     * function returns false, the application close object
     * will be called (if defined), otherwise a faked key-press
     * of the Escape key is performed. If these two actions
     * failed (because there is no close object or no button or
     * menu item is connected to the Escape key), then the
     * function <code>onPanelClose</code> is called.
     *
     * @return  true  to cancel all default processing.
     */
    @Override
    public boolean onPanelClosing(VirtualPanel vp)
      {
      close(vp);
      return true;
      }

    /**
     * Push buttons and menu items generates this event when selected.
     */
    @Override
    public void onAction(VirtualPanel vp,String controlID)
      {
      if      ( controlID.equals("CANCEL" ) )  close(vp);
      else if ( controlID.equals("OK"     ) )  performOK(vp);
      else System.out.println("#TerminalApplicationAssignKey, Command ID "+controlID+" not handled");
      }

    /**
     * Performs the OK action, saves the edited
     * key selection for the host key and its
     * augmentation.
     */
    private void performOK(VirtualPanel vp)
      {
      // Look up the host key value.
      int line=vp.getNextSelection("KEYLIST",-1);
      if ( line<0 )
        return;
      int key=-1;
      String name="-";
      if ( line>0 )
        {
        name=vp.getLine("KEYLIST",line);
        for ( int ii=0, cc=names.size(); ii<cc; ++ii )
          if ( name.equals(names.elementAt(ii)) )
            {
            key=KeyboardRemapper.getHostKey(ii);
            break;
            }
        }
      
      // Save augmentation key.
      hostKeys[augmentation]=key;
      close(vp);

      // Update underlying panel with the new selection.
      vp.getPanelSession().getTopmostPanel().setText("PROP"+(augmentation+1),name);
      }
    }
}