]> source.dussan.org Git - tigervnc.git/commitdiff
Make all viewer parameters static. Viewer instances are isolated from each other...
authorBrian P. Hinz <bphinz@users.sf.net>
Sat, 27 Aug 2016 21:33:04 +0000 (17:33 -0400)
committerBrian P. Hinz <bphinz@users.sf.net>
Sat, 27 Aug 2016 21:33:04 +0000 (17:33 -0400)
21 files changed:
java/com/tigervnc/rfb/AliasParameter.java
java/com/tigervnc/rfb/CConnection.java
java/com/tigervnc/rfb/CSecurityTLS.java
java/com/tigervnc/rfb/CSecurityVeNCrypt.java
java/com/tigervnc/rfb/Configuration.java
java/com/tigervnc/rfb/Security.java
java/com/tigervnc/rfb/SecurityClient.java
java/com/tigervnc/vncviewer/BIPixelBuffer.java
java/com/tigervnc/vncviewer/CConn.java
java/com/tigervnc/vncviewer/ClipboardDialog.java
java/com/tigervnc/vncviewer/DesktopWindow.java
java/com/tigervnc/vncviewer/Dialog.java
java/com/tigervnc/vncviewer/F8Menu.java
java/com/tigervnc/vncviewer/OptionsDialog.java
java/com/tigervnc/vncviewer/OptionsDialogCallback.java [deleted file]
java/com/tigervnc/vncviewer/PasswdDialog.java
java/com/tigervnc/vncviewer/PlatformPixelBuffer.java
java/com/tigervnc/vncviewer/ServerDialog.java
java/com/tigervnc/vncviewer/Tunnel.java
java/com/tigervnc/vncviewer/Viewport.java
java/com/tigervnc/vncviewer/VncViewer.java

index a1ae838c51d35d31cac2a9b8b90a7ba55b8770ea..3f20ae46439d60162aebc65ee44e0fa8b47b9510 100644 (file)
@@ -44,5 +44,13 @@ public class AliasParameter extends VoidParameter {
     param.setImmutable();
   }
 
+  public void setHasBeenSet() {
+    param.setHasBeenSet();
+  }
+
+  public boolean hasBeenSet() {
+    return param.hasBeenSet();
+  }
+
   protected VoidParameter param;
 }
index c354868b47ce09ec8cc14ed18aa91dcc1de748d4..483d1f8434d57fccb25b18ccfda869447ef1f738 100644 (file)
@@ -111,7 +111,7 @@ abstract public class CConnection extends CMsgHandler {
     int secType = Security.secTypeInvalid;
 
     List<Integer> secTypes = new ArrayList<Integer>();
-    secTypes = Security.GetEnabledSecTypes();
+    secTypes = security.GetEnabledSecTypes();
 
     if (cp.isVersion(3,3)) {
 
@@ -292,6 +292,16 @@ abstract public class CConnection extends CMsgHandler {
   // Ownership of the IdentityVerifier is retained by the CConnection instance.
   //public IdentityVerifier getIdentityVerifier() { return 0; }
 
+  public void framebufferUpdateStart()
+  {
+    super.framebufferUpdateStart();
+  }
+
+  public void framebufferUpdateEnd()
+  {
+    super.framebufferUpdateEnd();
+  }
+
   // authSuccess() is called when authentication has succeeded.
   public void authSuccess() {}
 
index a8f6df35a83fe483b1fa6d7627576216b1f8a8ba..4b20e0bf0d96088a5248e1621704ac9d19b1b4ac 100644 (file)
@@ -58,11 +58,11 @@ import com.tigervnc.vncviewer.*;
 
 public class CSecurityTLS extends CSecurity {
 
-  public static StringParameter x509ca
-  = new StringParameter("x509ca",
+  public static StringParameter X509CA
+  = new StringParameter("X509CA",
                         "X509 CA certificate", "", Configuration.ConfigurationObject.ConfViewer);
-  public static StringParameter x509crl
-  = new StringParameter("x509crl",
+  public static StringParameter X509CRL
+  = new StringParameter("X509CRL",
                         "X509 CRL file", "", Configuration.ConfigurationObject.ConfViewer);
 
   private void initGlobal()
@@ -80,8 +80,8 @@ public class CSecurityTLS extends CSecurity {
     manager = null;
 
     setDefaults();
-    cafile = x509ca.getData();
-    crlfile = x509crl.getData();
+    cafile = X509CA.getData();
+    crlfile = X509CRL.getData();
   }
 
   public static String getDefaultCA() {
@@ -99,9 +99,9 @@ public class CSecurityTLS extends CSecurity {
   public static void setDefaults()
   {
     if (new File(getDefaultCA()).exists())
-      x509ca.setDefaultStr(getDefaultCA());
+      X509CA.setDefaultStr(getDefaultCA());
     if (new File(getDefaultCRL()).exists())
-      x509crl.setDefaultStr(getDefaultCRL());
+      X509CRL.setDefaultStr(getDefaultCRL());
   }
 
 // FIXME:
index f353874c93efde7c434eec74600f511431280233..179900a4cec560d90f50fbf855a8c297da59a82b 100644 (file)
@@ -134,7 +134,7 @@ public class CSecurityVeNCrypt extends CSecurity {
           Iterator<Integer> j;
           List<Integer> secTypes = new ArrayList<Integer>();
 
-          secTypes = Security.GetEnabledExtSecTypes();
+          secTypes = security.GetEnabledExtSecTypes();
 
           /* Honor server's security type order */
           for (i = 0; i < nAvailableTypes; i++) {
index 5d140d95a81ce774e80ce9eac680e4d9e137dc5f..11fc89a4c8be218913d501fe0f613713c341e769 100644 (file)
@@ -26,14 +26,19 @@ package com.tigervnc.rfb;
 
 import java.io.FileInputStream;
 import java.io.PrintWriter;
+import java.lang.reflect.Field;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
 import java.util.*;
 
+import com.tigervnc.vncviewer.VncViewer;
+
 public class Configuration {
 
   static LogWriter vlog = new LogWriter("Configuration");
 
+  private static final String IDENTIFIER_STRING = "TigerVNC Configuration file Version 1.0";
+
   public enum ConfigurationObject { ConfGlobal, ConfServer, ConfViewer };
 
   // -=- The Global/server/viewer Configuration objects
@@ -259,77 +264,6 @@ public class Configuration {
     list(79, 10);
   }
 
-  public void readAppletParams(java.applet.Applet applet) {
-    VoidParameter current = head;
-    while (current != null) {
-      String str = applet.getParameter(current.getName());
-      if (str != null)
-        current.setParam(str);
-      current = current._next;
-    }
-  }
-
-  public static void load(String filename) {
-    if (filename == null)
-      return;
-
-    /* Read parameters from file */
-    Properties props = new Properties();
-    try {
-      props.load(new FileInputStream(filename));
-    } catch(java.security.AccessControlException e)    {
-      vlog.error("Cannot access system properties:"+e.getMessage());
-      return;
-    } catch (java.lang.Exception e)    {
-      vlog.error("Error opening config file:"+e.getMessage());
-      return;
-    }
-
-    for (Iterator<String> i = props.stringPropertyNames().iterator(); i.hasNext();) {
-      String name = (String)i.next();
-      if (name.startsWith("[")) {
-        // skip the section delimiters
-        continue;
-      } else if (name.equals("host")) {
-        setParam("Server", props.getProperty(name));
-      } else if (name.equals("disableclipboard")) {
-        setParam("RecvClipboard", props.getProperty(name));
-        setParam("SendClipboard", props.getProperty(name));
-      } else if (name.equals("localcursor")) {
-        setParam("UseLocalCursor", props.getProperty(name));
-      } else {
-        if (!setParam(name, props.getProperty(name)))
-          vlog.debug("Cannot set parameter: "+name);
-      }
-    }
-  }
-
-  public static void save(String filename) {
-    PrintWriter pw = null;
-    try {
-      pw = new PrintWriter(filename, "UTF-8");
-    } catch (java.lang.Exception e)    {
-      vlog.error("Error opening config file:"+e.getMessage());
-      return;
-    }
-
-    pw.println("# TigerVNC viewer configuration");
-    DateFormat dateFormat = new SimpleDateFormat("E MMM d k:m:s z yyyy");
-    Date date = new Date();
-    pw.println("# "+dateFormat.format(date));
-    VoidParameter current = Configuration.global().head;
-    while (current != null) {
-      String name = current.getName();
-      String value = current.getValueStr();
-      if (!name.equals("Server") && !name.equals("Port") &&
-          value != null && value != current.getDefaultStr())
-        pw.println(name+"="+current.getValueStr());
-      current = current._next;
-    }
-    pw.flush();
-    pw.close();
-  }
-
   // Name for this Configuration
   private String name;
 
index a68ae3e94e9cca15c49fdd5d2e65f33a77e1319a..e256e6ebaac3a07e46f37a1d20a4038e5737f6b5 100644 (file)
@@ -60,6 +60,8 @@ public class Security {
   public static final int secResultFailed = 1;
   public static final int secResultTooMany = 2; // deprecated
 
+  public Security() { }
+
   public Security(StringParameter secTypes)
   {
     String secTypesStr;
@@ -70,9 +72,9 @@ public class Security {
     secTypesStr = null;
   }
 
-  public static List<Integer> enabledSecTypes = new ArrayList<Integer>();
+  private List<Integer> enabledSecTypes = new ArrayList<Integer>();
 
-  public static final List<Integer> GetEnabledSecTypes()
+  public final List<Integer> GetEnabledSecTypes()
   {
     List<Integer> result = new ArrayList<Integer>();
 
@@ -98,7 +100,7 @@ public class Security {
     return (result);
   }
 
-  public static final List<Integer> GetEnabledExtSecTypes()
+  public final List<Integer> GetEnabledExtSecTypes()
   {
     List<Integer> result = new ArrayList<Integer>();
 
@@ -111,7 +113,7 @@ public class Security {
     return (result);
   }
 
-  public static final void EnableSecType(int secType)
+  public final void EnableSecType(int secType)
   {
 
     for (Iterator<Integer> i = enabledSecTypes.iterator(); i.hasNext(); )
@@ -134,7 +136,29 @@ public class Security {
     return false;
   }
 
-  public static void DisableSecType(int secType) { enabledSecTypes.remove((Object)secType); }
+  public String ToString()
+  {
+    Iterator<Integer> i;
+    String out = new String("");
+    boolean firstpass = true;
+    String name;
+
+    for (i = enabledSecTypes.iterator(); i.hasNext(); ) {
+      name = secTypeName((Integer)i.next());
+      if (name.startsWith("[")) /* Unknown security type */
+        continue;
+
+      if (!firstpass)
+        out = out.concat(",");
+      else
+        firstpass = false;
+      out = out.concat(name);
+    }
+
+    return out;
+  }
+
+  public void DisableSecType(int secType) { enabledSecTypes.remove((Object)secType); }
 
   public static int secTypeNum(String name) {
     if (name.equalsIgnoreCase("None"))      return secTypeNone;
@@ -203,7 +227,9 @@ public class Security {
     return (result);
   }
 
-  public final void SetSecTypes(List<Integer> secTypes) { enabledSecTypes = secTypes; }
+  public final void SetSecTypes(List<Integer> secTypes) {
+    enabledSecTypes = secTypes;
+  }
 
   static LogWriter vlog = new LogWriter("Security");
 }
index 59499b1eaf15f5e9b529d2645079ab197e0a8862..ff2433c27fe779bd55be5f373cfd608505f79217 100644 (file)
@@ -78,9 +78,9 @@ public class SecurityClient extends Security {
   //UserPasswdGetter upg = null;
   String msg = null;
 
-  static StringParameter secTypes
+  public static StringParameter secTypes
   = new StringParameter("SecurityTypes",
-                        "Specify which security scheme to use (None, VncAuth)",
-                        "Ident,TLSIdent,X509Ident,X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,VncAuth,None", Configuration.ConfigurationObject.ConfViewer);
+                        "Specify which security scheme to use (None, VncAuth, Plain, Ident, TLSNone, TLSVnc, TLSPlain, TLSIdent, X509None, X509Vnc, X509Plain, X509Ident)",
+                        "X509Ident,X509Plain,TLSIdent,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone,Ident,VncAuth,None", Configuration.ConfigurationObject.ConfViewer);
 
 }
index 9612b36fc92600b0aedd6d495e518606f8142fdc..1634ebd1ab2cce4c946cae039ab4b36867cf7b81 100644 (file)
@@ -27,8 +27,8 @@ import com.tigervnc.rfb.Exception;
 
 public class BIPixelBuffer extends PlatformPixelBuffer implements ImageObserver
 {
-  public BIPixelBuffer(int w, int h, CConn cc_, DesktopWindow desktop_) {
-    super(w, h, cc_, desktop_);
+  public BIPixelBuffer(PixelFormat pf, int w, int h, DesktopWindow desktop_) {
+    super(pf, w, h, desktop_);
     clip = new Rectangle();
   }
 
index b9680ef7aa500a0c725e1fe736286252a6848df6..d7134344f984e295df6d5e0fe598eb85575d8566 100644 (file)
@@ -35,7 +35,9 @@
 package com.tigervnc.vncviewer;
 
 import java.awt.*;
+import java.awt.datatransfer.StringSelection;
 import java.awt.event.*;
+import java.awt.Toolkit;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -49,6 +51,7 @@ import javax.swing.ImageIcon;
 import java.net.InetSocketAddress;
 import java.net.SocketException;
 import java.util.*;
+import java.util.prefs.*;
 
 import com.tigervnc.rdr.*;
 import com.tigervnc.rfb.*;
@@ -57,16 +60,18 @@ import com.tigervnc.rfb.Exception;
 import com.tigervnc.network.Socket;
 import com.tigervnc.network.TcpSocket;
 
+import static com.tigervnc.vncviewer.Parameters.*;
+
 public class CConn extends CConnection implements 
-  UserPasswdGetter, UserMsgBox, OptionsDialogCallback, 
+  UserPasswdGetter, UserMsgBox,
   FdInStreamBlockCallback, ActionListener {
 
-  public final PixelFormat getPreferredPF() { return fullColourPF; }
-  static final PixelFormat verylowColourPF =
+  public final PixelFormat getPreferredPF() { return fullColorPF; }
+  static final PixelFormat verylowColorPF =
     new PixelFormat(8, 3, false, true, 1, 1, 1, 2, 1, 0);
-  static final PixelFormat lowColourPF =
+  static final PixelFormat lowColorPF =
     new PixelFormat(8, 6, false, true, 3, 3, 3, 4, 2, 0);
-  static final PixelFormat mediumColourPF =
+  static final PixelFormat mediumColorPF =
     new PixelFormat(8, 8, false, false, 7, 7, 3, 0, 3, 6);
   static final int KEY_LOC_SHIFT_R = 0;
   static final int KEY_LOC_SHIFT_L = 16;
@@ -75,67 +80,48 @@ public class CConn extends CConnection implements
   ////////////////////////////////////////////////////////////////////
   // The following methods are all called from the RFB thread
 
-  public CConn(VncViewer viewer_, Socket sock_,
-               String vncServerName)
+  public CConn(String vncServerName, Socket socket)
   {
-    sock = sock_; viewer = viewer_;
     pendingPFChange = false;
     currentEncoding = Encodings.encodingTight; lastServerEncoding = -1;
-    fullColour = viewer.fullColour.getValue();
-    lowColourLevel = viewer.lowColourLevel.getValue();
-    autoSelect = viewer.autoSelect.getValue();
     formatChange = false; encodingChange = false;
-    fullScreen = viewer.fullScreen.getValue();
-    menuKeyCode = MenuKey.getMenuKeyCode();
-    options = new OptionsDialog(this);
-    options.initDialog();
-    clipboardDialog = new ClipboardDialog(this);
     firstUpdate = true; pendingUpdate = false; continuousUpdates = false;
     forceNonincremental = true; supportsSyncFence = false;
+
+    setShared(shared.getValue());
+    sock = socket;
     downKeySym = new HashMap<Integer, Integer>();
 
-    setShared(viewer.shared.getValue());
     upg = this;
     msg = this;
 
-    String encStr = viewer.preferredEncoding.getValue();
-    int encNum = Encodings.encodingNum(encStr);
-    if (encNum != -1) {
+    int encNum = Encodings.encodingNum(preferredEncoding.getValue());
+    if (encNum != -1)
       currentEncoding = encNum;
-    }
+
+    cp.supportsLocalCursor = useLocalCursor.getValue();
+
     cp.supportsDesktopResize = true;
     cp.supportsExtendedDesktopSize = true;
-    cp.supportsSetDesktopSize = false;
-    cp.supportsClientRedirect = true;
     cp.supportsDesktopRename = true;
-    cp.supportsLocalCursor = viewer.useLocalCursor.getValue();
-    cp.customCompressLevel = viewer.customCompressLevel.getValue();
-    cp.compressLevel = viewer.compressLevel.getValue();
-    cp.noJpeg = viewer.noJpeg.getValue();
-    cp.qualityLevel = viewer.qualityLevel.getValue();
-    initMenu();
-
-    if (sock != null) {
-      String name = sock.getPeerEndpoint();
-      vlog.info("Accepted connection from " + name);
-    } else {
-      if (vncServerName != null &&
-          !viewer.alwaysShowServerDialog.getValue()) {
-        setServerName(Hostname.getHost(vncServerName));
-        setServerPort(Hostname.getPort(vncServerName));
-      } else {
-        ServerDialog dlg = new ServerDialog(options, vncServerName, this);
-        boolean ret = dlg.showDialog();
-        if (!ret) {
-          close();
-          return;
-        }
-        setServerName(viewer.vncServerName.getValueStr());
-        setServerPort(viewer.vncServerPort.getValue());
-      }
 
+    cp.supportsSetDesktopSize = false;
+    cp.supportsClientRedirect = true;
+    if (customCompressLevel.getValue())
+      cp.compressLevel = compressLevel.getValue();
+    else
+      cp.compressLevel = -1;
+
+    if (noJpeg.getValue())
+      cp.qualityLevel = qualityLevel.getValue();
+    else
+      cp.qualityLevel = -1;
+
+    if (sock == null) {
+      setServerName(Hostname.getHost(vncServerName));
+      setServerPort(Hostname.getPort(vncServerName));
       try {
-        if (viewer.tunnel.getValue() || (viewer.via.getValue() != null)) {
+        if (tunnel.getValue() || !via.getValue().isEmpty()) {
           int localPort = TcpSocket.findFreeTcpPort();
           if (localPort == 0)
             throw new Exception("Could not obtain free TCP port");
@@ -148,11 +134,21 @@ public class CConn extends CConnection implements
         throw new Exception(e.getMessage());
       }
       vlog.info("connected to host "+getServerName()+" port "+getServerPort());
+    } else {
+      String name = sock.getPeerEndpoint();
+      if (listenMode.getValue())
+        vlog.info("Accepted connection from " + name);
+      else
+        vlog.info("connected to host "+Hostname.getHost(name)+" port "+Hostname.getPort(name));
     }
 
     sock.inStream().setBlockCallback(this);
+
     setStreams(sock.inStream(), sock.outStream());
+
     initialiseProtocol();
+
+    OptionsDialog.addCallback("handleOptions", this);
   }
 
   public void refreshFramebuffer()
@@ -196,7 +192,7 @@ public class CConn extends CConnection implements
   public final boolean getUserPasswd(StringBuffer user, StringBuffer passwd) {
     String title = ("VNC Authentication ["
                     +csecurity.description() + "]");
-    String passwordFileStr = viewer.passwordFile.getValue();
+    String passwordFileStr = passwordFile.getValue();
     PasswdDialog dlg;
 
     if (user == null && !passwordFileStr.equals("")) {
@@ -222,16 +218,16 @@ public class CConn extends CConnection implements
     if (user == null) {
       dlg = new PasswdDialog(title, (user == null), (passwd == null));
     } else {
-      if ((passwd == null) && viewer.sendLocalUsername.getValue()) {
+      if ((passwd == null) && sendLocalUsername.getValue()) {
          user.append((String)System.getProperties().get("user.name"));
          return true;
       }
-      dlg = new PasswdDialog(title, viewer.sendLocalUsername.getValue(),
+      dlg = new PasswdDialog(title, sendLocalUsername.getValue(),
          (passwd == null));
     }
-    if (!dlg.showDialog()) return false;
+    dlg.showDialog();
     if (user != null) {
-      if (viewer.sendLocalUsername.getValue()) {
+      if (sendLocalUsername.getValue()) {
          user.append((String)System.getProperties().get("user.name"));
       } else {
          user.append(dlg.userEntry.getText());
@@ -252,13 +248,13 @@ public class CConn extends CConnection implements
 
     // If using AutoSelect with old servers, start in FullColor
     // mode. See comment in autoSelectFormatAndEncoding.
-    if (cp.beforeVersion(3, 8) && autoSelect)
-      fullColour = true;
+    if (cp.beforeVersion(3, 8) && autoSelect.getValue())
+      fullColor.setParam(true);
 
     serverPF = cp.pf();
 
-    desktop = new DesktopWindow(cp.width, cp.height, serverPF, this);
-    fullColourPF = desktop.getPreferredPF();
+    desktop = new DesktopWindow(cp.width, cp.height, cp.name(), serverPF, this);
+    fullColorPF = desktop.getPreferredPF();
 
     // Force a switch to the format and encoding we'd like
     formatChange = true; encodingChange = true;
@@ -273,55 +269,7 @@ public class CConn extends CConnection implements
     cp.setPF(pendingPF);
     pendingPFChange = false;
 
-    if (viewer.embed.getValue()) {
-      setupEmbeddedFrame();
-    } else {
-      recreateViewport();
-    }
-  }
-
-  void setupEmbeddedFrame() {
-    UIManager.getDefaults().put("ScrollPane.ancestorInputMap",
-      new UIDefaults.LazyInputMap(new Object[]{}));
-    JScrollPane sp = new JScrollPane();
-    sp.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
-    sp.getViewport().setBackground(Color.BLACK);
-    InputMap im = sp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
-    int ctrlAltShiftMask = Event.SHIFT_MASK | Event.CTRL_MASK | Event.ALT_MASK;
-    if (im != null) {
-      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, ctrlAltShiftMask),
-             "unitScrollUp");
-      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, ctrlAltShiftMask),
-             "unitScrollDown");
-      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, ctrlAltShiftMask),
-             "unitScrollLeft");
-      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, ctrlAltShiftMask),
-             "unitScrollRight");
-      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, ctrlAltShiftMask),
-             "scrollUp");
-      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, ctrlAltShiftMask),
-             "scrollDown");
-      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, ctrlAltShiftMask),
-             "scrollLeft");
-      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, ctrlAltShiftMask),
-             "scrollRight");
-    }
-    sp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
-    sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
-    desktop.setViewport(sp.getViewport());
-    viewer.getContentPane().removeAll();
-    viewer.add(sp);
-    viewer.addFocusListener(new FocusAdapter() {
-      public void focusGained(FocusEvent e) {
-        if (desktop.isAncestorOf(viewer))
-          desktop.requestFocus();
-      }
-      public void focusLost(FocusEvent e) {
-        releaseDownKeys();
-      }
-    });
-    viewer.validate();
-    desktop.requestFocus();
+    recreateViewport();
   }
 
   // setDesktopSize() is called when the desktop size changes (including when
@@ -350,10 +298,13 @@ public class CConn extends CConnection implements
                              String x509subject) {
     try {
       sock.close();
-      setServerPort(port);
       sock = new TcpSocket(host, port);
       vlog.info("Redirected to "+host+":"+port);
-      VncViewer.newViewer(viewer, sock, true);
+      setServerName(host);
+      setServerPort(port);
+      sock.inStream().setBlockCallback(this);
+      setStreams(sock.inStream(), sock.outStream());
+      initialiseProtocol();
     } catch (java.lang.Exception e) {
       throw new Exception(e.getMessage());
     }
@@ -362,10 +313,8 @@ public class CConn extends CConnection implements
   // setName() is called when the desktop name changes
   public void setName(String name) {
     super.setName(name);
-
-    if (viewport != null) {
+    if (viewport != null)
       viewport.setTitle(name+" - TigerVNC");
-    }
   }
 
   // framebufferUpdateStart() is called at the beginning of an update.
@@ -374,6 +323,8 @@ public class CConn extends CConnection implements
   // one.
   public void framebufferUpdateStart()
   {
+    super.framebufferUpdateStart();
+
     // Note: This might not be true if sync fences are supported
     pendingUpdate = false;
 
@@ -386,6 +337,7 @@ public class CConn extends CConnection implements
   // appropriately, and then request another incremental update.
   public void framebufferUpdateEnd()
   {
+    super.framebufferUpdateEnd();
 
     desktop.updateWindow();
 
@@ -398,10 +350,10 @@ public class CConn extends CConnection implements
         writer().writeFence(fenceTypes.fenceFlagRequest | fenceTypes.fenceFlagSyncNext, 0, null);
 
       if (cp.supportsSetDesktopSize &&
-          viewer.desktopSize.getValue() != null &&
-          viewer.desktopSize.getValue().split("x").length == 2) {
-        width = Integer.parseInt(viewer.desktopSize.getValue().split("x")[0]);
-        height = Integer.parseInt(viewer.desktopSize.getValue().split("x")[1]);
+          !desktopSize.getValue().isEmpty() &&
+          desktopSize.getValue().split("x").length == 2) {
+        width = Integer.parseInt(desktopSize.getValue().split("x")[0]);
+        height = Integer.parseInt(desktopSize.getValue().split("x")[1]);
         ScreenSet layout;
 
         layout = cp.screenLayout;
@@ -442,24 +394,28 @@ public class CConn extends CConnection implements
     }
 
     // Compute new settings based on updated bandwidth values
-    if (autoSelect)
+    if (autoSelect.getValue())
       autoSelectFormatAndEncoding();
   }
 
   // The rest of the callbacks are fairly self-explanatory...
 
-  public void setColourMapEntries(int firstColour, int nColours, int[] rgbs) {
-    desktop.setColourMapEntries(firstColour, nColours, rgbs);
+  public void setColourMapEntries(int firstColor, int nColors, int[] rgbs) {
+    desktop.setColourMapEntries(firstColor, nColors, rgbs);
   }
 
   public void bell() {
-    if (viewer.acceptBell.getValue())
+    if (acceptBell.getValue())
       desktop.getToolkit().beep();
   }
 
   public void serverCutText(String str, int len) {
-    if (viewer.acceptClipboard.getValue())
-      clipboardDialog.serverCutText(str, len);
+    StringSelection buffer;
+
+    if (!acceptClipboard.getValue())
+      return;
+
+    ClipboardDialog.serverCutText(str);
   }
 
   // We start timing on beginRect and stop timing on endRect, to
@@ -543,50 +499,54 @@ public class CConn extends CConnection implements
       return;
 
     desktop.resize();
-    if (viewer.embed.getValue()) {
-      setupEmbeddedFrame();
-    } else {
+    if (!firstUpdate)
       recreateViewport();
-    }
   }
   
-  public void setEmbeddedFeatures(boolean s) {
-    menu.restore.setEnabled(s);
-    menu.minimize.setEnabled(s);
-    menu.maximize.setEnabled(s);
-    menu.fullScreen.setEnabled(s);
-    menu.newConn.setEnabled(s);
-    options.fullScreen.setEnabled(s);
-    options.fullScreenAllMonitors.setEnabled(s);
-    options.scalingFactor.setEnabled(s);
-  }
-
   // recreateViewport() recreates our top-level window.  This seems to be
   // better than attempting to resize the existing window, at least with
   // various X window managers.
 
   public void recreateViewport() {
-    if (viewer.embed.getValue())
-      return;
-    if (viewport != null) viewport.dispose();
-    viewport = new Viewport(cp.name(), this);
-    viewport.setUndecorated(fullScreen);
-    desktop.setViewport(viewport.getViewport());
-    reconfigureViewport();
-    if ((cp.width > 0) && (cp.height > 0))
-      viewport.setVisible(true);
-    desktop.requestFocusInWindow();
+    if (embed.getValue()) {
+      desktop.setViewport(VncViewer.getViewport());
+      Container viewer =
+        SwingUtilities.getAncestorOfClass(JApplet.class, desktop);
+      viewer.addFocusListener(new FocusAdapter() {
+        public void focusGained(FocusEvent e) {
+          Container c =
+            SwingUtilities.getAncestorOfClass(JApplet.class, desktop);
+          if (c != null && desktop.isAncestorOf(c))
+            desktop.requestFocus();
+        }
+        public void focusLost(FocusEvent e) {
+          releaseDownKeys();
+        }
+      });
+      viewer.validate();
+      desktop.requestFocus();
+    } else {
+      if (viewport != null)
+        viewport.dispose();
+      viewport = new Viewport(cp.name(), this);
+      viewport.setUndecorated(fullScreen.getValue());
+      desktop.setViewport(viewport.getViewport());
+      reconfigureViewport();
+      if ((cp.width > 0) && (cp.height > 0))
+        viewport.setVisible(true);
+      desktop.requestFocusInWindow();
+    }
   }
 
   private void reconfigureViewport() {
     Dimension dpySize = viewport.getScreenSize();
     int w = desktop.scaledWidth;
     int h = desktop.scaledHeight;
-    if (fullScreen) {
-      if (!viewer.fullScreenAllMonitors.getValue())
+    if (fullScreen.getValue()) {
+      if (!fullScreenAllMonitors.getValue())
         viewport.setExtendedState(JFrame.MAXIMIZED_BOTH);
       viewport.setBounds(viewport.getScreenBounds());
-      if (!viewer.fullScreenAllMonitors.getValue())
+      if (!fullScreenAllMonitors.getValue())
         Viewport.setFullScreenWindow(viewport);
     } else {
       int wmDecorationWidth = viewport.getInsets().left + viewport.getInsets().right;
@@ -629,7 +589,7 @@ public class CConn extends CConnection implements
   private void autoSelectFormatAndEncoding() {
     long kbitsPerSecond = sock.inStream().kbitsPerSecond();
     long timeWaited = sock.inStream().timeWaited();
-    boolean newFullColour = fullColour;
+    boolean newFullColor = fullColor.getValue();
     int newQualityLevel = cp.qualityLevel;
 
     // Always use Tight
@@ -653,7 +613,7 @@ public class CConn extends CConnection implements
         vlog.info("Throughput "+kbitsPerSecond+
                   " kbit/s - changing to quality "+newQualityLevel);
         cp.qualityLevel = newQualityLevel;
-        viewer.qualityLevel.setParam(Integer.toString(newQualityLevel));
+        qualityLevel.setParam(newQualityLevel);
         encodingChange = true;
       }
     }
@@ -670,12 +630,12 @@ public class CConn extends CConnection implements
     }
 
     // Select best color level
-    newFullColour = (kbitsPerSecond > 256);
-    if (newFullColour != fullColour) {
+    newFullColor = (kbitsPerSecond > 256);
+    if (newFullColor != fullColor.getValue()) {
       vlog.info("Throughput "+kbitsPerSecond+
                 " kbit/s - full color is now "+
-                   (newFullColour ? "enabled" : "disabled"));
-      fullColour = newFullColour;
+                (newFullColor ? "enabled" : "disabled"));
+      fullColor.setParam(newFullColor);
       formatChange = true;
       forceNonincremental = true;
     }
@@ -691,15 +651,15 @@ public class CConn extends CConnection implements
       /* Catch incorrect requestNewUpdate calls */
       assert(!pendingUpdate || supportsSyncFence);
 
-      if (fullColour) {
-        pf = fullColourPF;
+      if (fullColor.getValue()) {
+        pf = fullColorPF;
       } else {
-        if (lowColourLevel == 0) {
-          pf = verylowColourPF;
-        } else if (lowColourLevel == 1) {
-          pf = lowColourPF;
+        if (lowColorLevel.getValue() == 0) {
+          pf = verylowColorPF;
+        } else if (lowColorLevel.getValue() == 1) {
+          pf = lowColorPF;
         } else {
-          pf = mediumColourPF;
+          pf = mediumColorPF;
         }
       }
 
@@ -746,12 +706,11 @@ public class CConn extends CConnection implements
   // close() shuts down the socket, thus waking up the RFB thread.
   public void close() {
     if (closeListener != null) {
-      viewer.embed.setParam(true);
-      if (VncViewer.nViewers == 1) {
-        JFrame f = (JFrame)JOptionPane.getFrameForComponent(viewer);
-        if (f != null)
-          f.dispatchEvent(new WindowEvent(f, WindowEvent.WINDOW_CLOSING));
-      }
+      embed.setParam(true);
+      JFrame f =
+        (JFrame)SwingUtilities.getAncestorOfClass(JFrame.class, desktop);
+      if (f != null)
+        f.dispatchEvent(new WindowEvent(f, WindowEvent.WINDOW_CLOSING));
     }
     deleteWindow();
     shuttingDown = true;
@@ -763,47 +722,6 @@ public class CConn extends CConnection implements
     }
   }
 
-  // Menu callbacks.  These are guaranteed only to be called after serverInit()
-  // has been called, since the menu is only accessible from the DesktopWindow
-
-  private void initMenu() {
-    menu = new F8Menu(this);
-  }
-
-  void showMenu(int x, int y) {
-    String os = System.getProperty("os.name");
-    if (os.startsWith("Windows"))
-      com.sun.java.swing.plaf.windows.WindowsLookAndFeel.setMnemonicHidden(false);
-    menu.show(desktop, x, y);
-  }
-
-  void showAbout() {
-    String pkgDate = "";
-    String pkgTime = "";
-    try {
-      Manifest manifest = new Manifest(VncViewer.timestamp);
-      Attributes attributes = manifest.getMainAttributes();
-      pkgDate = attributes.getValue("Package-Date");
-      pkgTime = attributes.getValue("Package-Time");
-    } catch (java.lang.Exception e) { }
-
-    Window fullScreenWindow = Viewport.getFullScreenWindow();
-    if (fullScreenWindow != null)
-      Viewport.setFullScreenWindow(null);
-    String msg =
-      String.format(VncViewer.aboutText, VncViewer.version, VncViewer.build,
-                    VncViewer.buildDate, VncViewer.buildTime);
-    JOptionPane op =
-      new JOptionPane(msg, JOptionPane.INFORMATION_MESSAGE,
-                      JOptionPane.DEFAULT_OPTION, VncViewer.logoIcon);
-    JDialog dlg = op.createDialog(desktop, "About TigerVNC Viewer for Java");
-    dlg.setIconImage(VncViewer.frameIcon);
-    dlg.setAlwaysOnTop(true);
-    dlg.setVisible(true);
-    if (fullScreenWindow != null)
-      Viewport.setFullScreenWindow(fullScreenWindow);
-  }
-
   void showInfo() {
     Window fullScreenWindow = Viewport.getFullScreenWindow();
     if (fullScreenWindow != null)
@@ -830,8 +748,10 @@ public class CConn extends CConnection implements
                     cp.majorVersion, cp.minorVersion,
                     Security.secTypeName(csecurity.getType()),
                     csecurity.description());
-    JOptionPane op = new JOptionPane(msg, JOptionPane.PLAIN_MESSAGE,
-                                     JOptionPane.DEFAULT_OPTION);
+    Object[] options = {"Close  \u21B5"};
+    JOptionPane op =
+      new JOptionPane(msg, JOptionPane.PLAIN_MESSAGE,
+                      JOptionPane.DEFAULT_OPTION, null, options);
     JDialog dlg = op.createDialog(desktop, "VNC connection info");
     dlg.setIconImage(VncViewer.frameIcon);
     dlg.setAlwaysOnTop(true);
@@ -845,507 +765,74 @@ public class CConn extends CConnection implements
     pendingUpdate = true;
   }
 
-
-  // OptionsDialogCallback.  setOptions() sets the options dialog's checkboxes
-  // etc to reflect our flags.  getOptions() sets our flags according to the
-  // options dialog's checkboxes.  They are both called from the GUI thread.
-  // Some of the flags are also accessed by the RFB thread.  I believe that
-  // reading and writing boolean and int values in java is atomic, so there is
-  // no need for synchronization.
-
-  public void setOptions() {
-    int digit;
-    options.autoSelect.setSelected(autoSelect);
-    options.fullColour.setSelected(fullColour);
-    options.veryLowColour.setSelected(!fullColour && lowColourLevel == 0);
-    options.lowColour.setSelected(!fullColour && lowColourLevel == 1);
-    options.mediumColour.setSelected(!fullColour && lowColourLevel == 2);
-    options.tight.setSelected(currentEncoding == Encodings.encodingTight);
-    options.zrle.setSelected(currentEncoding == Encodings.encodingZRLE);
-    options.hextile.setSelected(currentEncoding == Encodings.encodingHextile);
-    options.raw.setSelected(currentEncoding == Encodings.encodingRaw);
-
-    options.customCompressLevel.setSelected(viewer.customCompressLevel.getValue());
-    digit = 0 + viewer.compressLevel.getValue();
-    if (digit >= 0 && digit <= 9) {
-      options.compressLevel.setSelectedItem(digit);
-    } else {
-      options.compressLevel.setSelectedItem(Integer.parseInt(viewer.compressLevel.getDefaultStr()));
-    }
-    options.noJpeg.setSelected(!viewer.noJpeg.getValue());
-    digit = 0 + viewer.qualityLevel.getValue();
-    if (digit >= 0 && digit <= 9) {
-      options.qualityLevel.setSelectedItem(digit);
-    } else {
-      options.qualityLevel.setSelectedItem(Integer.parseInt(viewer.qualityLevel.getDefaultStr()));
-    }
-
-    options.viewOnly.setSelected(viewer.viewOnly.getValue());
-    options.acceptClipboard.setSelected(viewer.acceptClipboard.getValue());
-    options.sendClipboard.setSelected(viewer.sendClipboard.getValue());
-    options.menuKey.setSelectedItem(KeyEvent.getKeyText(MenuKey.getMenuKeyCode()));
-    options.sendLocalUsername.setSelected(viewer.sendLocalUsername.getValue());
-
-    if (state() == RFBSTATE_NORMAL) {
-      options.shared.setEnabled(false);
-      options.secVeNCrypt.setEnabled(false);
-      options.encNone.setEnabled(false);
-      options.encTLS.setEnabled(false);
-      options.encX509.setEnabled(false);
-      options.x509ca.setEnabled(false);
-      options.caButton.setEnabled(false);
-      options.x509crl.setEnabled(false);
-      options.crlButton.setEnabled(false);
-      options.secIdent.setEnabled(false);
-      options.secNone.setEnabled(false);
-      options.secVnc.setEnabled(false);
-      options.secPlain.setEnabled(false);
-      options.sendLocalUsername.setEnabled(false);
-      options.cfLoadButton.setEnabled(false);
-      options.cfSaveAsButton.setEnabled(true);
-      options.sshTunnel.setEnabled(false);
-      options.sshUseGateway.setEnabled(false);
-      options.sshUser.setEnabled(false);
-      options.sshHost.setEnabled(false);
-      options.sshPort.setEnabled(false);
-      options.sshUseExt.setEnabled(false);
-      options.sshClient.setEnabled(false);
-      options.sshClientBrowser.setEnabled(false);
-      options.sshArgsDefault.setEnabled(false);
-      options.sshArgsCustom.setEnabled(false);
-      options.sshArguments.setEnabled(false);
-      options.sshConfig.setEnabled(false);
-      options.sshConfigBrowser.setEnabled(false);
-      options.sshKeyFile.setEnabled(false);
-      options.sshKeyFileBrowser.setEnabled(false);
-    } else {
-      options.shared.setSelected(viewer.shared.getValue());
-      options.sendLocalUsername.setSelected(viewer.sendLocalUsername.getValue());
-      options.cfSaveAsButton.setEnabled(false);
-      if (viewer.tunnel.getValue() || viewer.via.getValue() != null)
-        options.sshTunnel.setSelected(true);
-      if (viewer.via.getValue() != null)
-        options.sshUseGateway.setSelected(true);
-      options.sshUser.setText(Tunnel.getSshUser(this));
-      options.sshHost.setText(Tunnel.getSshHost(this));
-      options.sshPort.setText(Integer.toString(Tunnel.getSshPort(this)));
-      options.sshUseExt.setSelected(viewer.extSSH.getValue());
-      File client = new File(viewer.extSSHClient.getValue());
-      if (client.exists() && client.canRead())
-        options.sshClient.setText(client.getAbsolutePath());
-      if (viewer.extSSHArgs.getValue() == null) {
-        options.sshArgsDefault.setSelected(true);
-        options.sshArguments.setText("");
-      } else {
-        options.sshArgsCustom.setSelected(true);
-        options.sshArguments.setText(viewer.extSSHArgs.getValue());
-      }
-      File config = new File(viewer.sshConfig.getValue());
-      if (config.exists() && config.canRead())
-        options.sshConfig.setText(config.getAbsolutePath());
-      options.sshKeyFile.setText(Tunnel.getSshKeyFile(this));
-
-      /* Process non-VeNCrypt sectypes */
-      java.util.List<Integer> secTypes = new ArrayList<Integer>();
-      secTypes = Security.GetEnabledSecTypes();
-      for (Iterator<Integer> i = secTypes.iterator(); i.hasNext();) {
-        switch ((Integer)i.next()) {
-        case Security.secTypeVeNCrypt:
-          options.secVeNCrypt.setSelected(UserPreferences.getBool("viewer", "secVeNCrypt", true));
-          break;
-        case Security.secTypeNone:
-          options.encNone.setSelected(true);
-          options.secNone.setSelected(UserPreferences.getBool("viewer", "secTypeNone", true));
-          break;
-        case Security.secTypeVncAuth:
-          options.encNone.setSelected(true);
-          options.secVnc.setSelected(UserPreferences.getBool("viewer", "secTypeVncAuth", true));
-          break;
-        }
-      }
-
-      /* Process VeNCrypt subtypes */
-      if (options.secVeNCrypt.isSelected()) {
-        java.util.List<Integer> secTypesExt = new ArrayList<Integer>();
-        secTypesExt = Security.GetEnabledExtSecTypes();
-        for (Iterator<Integer> iext = secTypesExt.iterator(); iext.hasNext();) {
-          switch ((Integer)iext.next()) {
-          case Security.secTypePlain:
-            options.encNone.setSelected(UserPreferences.getBool("viewer", "encNone", true));
-            options.secPlain.setSelected(UserPreferences.getBool("viewer", "secPlain", true));
-            break;
-          case Security.secTypeIdent:
-            options.encNone.setSelected(UserPreferences.getBool("viewer", "encNone", true));
-            options.secIdent.setSelected(UserPreferences.getBool("viewer", "secIdent", true));
-            break;
-          case Security.secTypeTLSNone:
-            options.encTLS.setSelected(UserPreferences.getBool("viewer", "encTLS", true));
-            options.secNone.setSelected(UserPreferences.getBool("viewer", "secNone", true));
-            break;
-          case Security.secTypeTLSVnc:
-            options.encTLS.setSelected(UserPreferences.getBool("viewer", "encTLS", true));
-            options.secVnc.setSelected(UserPreferences.getBool("viewer", "secVnc", true));
-            break;
-          case Security.secTypeTLSPlain:
-            options.encTLS.setSelected(UserPreferences.getBool("viewer", "encTLS", true));
-            options.secPlain.setSelected(UserPreferences.getBool("viewer", "secPlain", true));
-            break;
-          case Security.secTypeTLSIdent:
-            options.encTLS.setSelected(UserPreferences.getBool("viewer", "encTLS", true));
-            options.secIdent.setSelected(UserPreferences.getBool("viewer", "secIdent", true));
-            break;
-          case Security.secTypeX509None:
-            options.encX509.setSelected(UserPreferences.getBool("viewer", "encX509", true));
-            options.secNone.setSelected(UserPreferences.getBool("viewer", "secNone", true));
-            break;
-          case Security.secTypeX509Vnc:
-            options.encX509.setSelected(UserPreferences.getBool("viewer", "encX509", true));
-            options.secVnc.setSelected(UserPreferences.getBool("viewer", "secVnc", true));
-            break;
-          case Security.secTypeX509Plain:
-            options.encX509.setSelected(UserPreferences.getBool("viewer", "encX509", true));
-            options.secPlain.setSelected(UserPreferences.getBool("viewer", "secPlain", true));
-            break;
-          case Security.secTypeX509Ident:
-            options.encX509.setSelected(UserPreferences.getBool("viewer", "encX509", true));
-            options.secIdent.setSelected(UserPreferences.getBool("viewer", "secIdent", true));
-            break;
-          }
-        }
-      }
-      File caFile = new File(viewer.x509ca.getValue());
-      if (caFile.exists() && caFile.canRead())
-        options.x509ca.setText(caFile.getAbsolutePath());
-      File crlFile = new File(viewer.x509crl.getValue());
-      if (crlFile.exists() && crlFile.canRead())
-        options.x509crl.setText(crlFile.getAbsolutePath());
-      options.encNone.setEnabled(options.secVeNCrypt.isSelected());
-      options.encTLS.setEnabled(options.secVeNCrypt.isSelected());
-      options.encX509.setEnabled(options.secVeNCrypt.isSelected());
-      options.x509ca.setEnabled(options.secVeNCrypt.isSelected() &&
-                                options.encX509.isSelected());
-      options.caButton.setEnabled(options.secVeNCrypt.isSelected() &&
-                                  options.encX509.isSelected());
-      options.x509crl.setEnabled(options.secVeNCrypt.isSelected() &&
-                                 options.encX509.isSelected());
-      options.crlButton.setEnabled(options.secVeNCrypt.isSelected() &&
-                                   options.encX509.isSelected());
-      options.secIdent.setEnabled(options.secVeNCrypt.isSelected());
-      options.secPlain.setEnabled(options.secVeNCrypt.isSelected());
-      options.sendLocalUsername.setEnabled(options.secPlain.isSelected()||
-        options.secIdent.isSelected());
-      options.sshTunnel.setEnabled(true);
-        options.sshUseGateway.setEnabled(options.sshTunnel.isSelected());
-        options.sshUser.setEnabled(options.sshTunnel.isSelected() &&
-                                   options.sshUseGateway.isEnabled() &&
-                                   options.sshUseGateway.isSelected());
-        options.sshHost.setEnabled(options.sshTunnel.isSelected() &&
-                                   options.sshUseGateway.isEnabled() &&
-                                   options.sshUseGateway.isSelected());
-        options.sshPort.setEnabled(options.sshTunnel.isSelected() &&
-                                   options.sshUseGateway.isEnabled() &&
-                                   options.sshUseGateway.isSelected());
-        options.sshUseExt.setEnabled(options.sshTunnel.isSelected());
-        options.sshClient.setEnabled(options.sshTunnel.isSelected() &&
-                                     options.sshUseExt.isEnabled() &&
-                                     options.sshUseExt.isSelected());
-        options.sshClientBrowser.setEnabled(options.sshTunnel.isSelected() &&
-                                            options.sshUseExt.isEnabled() &&
-                                            options.sshUseExt.isSelected());
-        options.sshArgsDefault.setEnabled(options.sshTunnel.isSelected() &&
-                                          options.sshUseExt.isEnabled() &&
-                                          options.sshUseExt.isSelected());
-        options.sshArgsCustom.setEnabled(options.sshTunnel.isSelected() &&
-                                         options.sshUseExt.isEnabled() &&
-                                         options.sshUseExt.isSelected());
-        options.sshArguments.setEnabled(options.sshTunnel.isSelected() &&
-                                        options.sshUseExt.isEnabled() &&
-                                        options.sshUseExt.isSelected() &&
-                                        options.sshArgsCustom.isSelected());
-        options.sshConfig.setEnabled(options.sshTunnel.isSelected() &&
-                                     options.sshUseExt.isEnabled() &&
-                                     !options.sshUseExt.isSelected());
-        options.sshConfigBrowser.setEnabled(options.sshTunnel.isSelected() &&
-                                            options.sshUseExt.isEnabled() &&
-                                            !options.sshUseExt.isSelected());
-        options.sshKeyFile.setEnabled(options.sshTunnel.isSelected() &&
-                                      options.sshUseExt.isEnabled() &&
-                                      !options.sshUseExt.isSelected());
-        options.sshKeyFileBrowser.setEnabled(options.sshTunnel.isSelected() &&
-                                             options.sshUseExt.isEnabled() &&
-                                             !options.sshUseExt.isSelected());
-    }
-
-    options.fullScreen.setSelected(fullScreen);
-    options.fullScreenAllMonitors.setSelected(viewer.fullScreenAllMonitors.getValue());
-    options.useLocalCursor.setSelected(viewer.useLocalCursor.getValue());
-    options.acceptBell.setSelected(viewer.acceptBell.getValue());
-    String scaleString = viewer.scalingFactor.getValue();
-    if (scaleString.equalsIgnoreCase("Auto")) {
-      options.scalingFactor.setSelectedItem("Auto");
-    } else if(scaleString.equalsIgnoreCase("FixedRatio")) {
-      options.scalingFactor.setSelectedItem("Fixed Aspect Ratio");
-    } else {
-      digit = Integer.parseInt(scaleString);
-      if (digit >= 1 && digit <= 1000) {
-        options.scalingFactor.setSelectedItem(digit+"%");
-      } else {
-        digit = Integer.parseInt(viewer.scalingFactor.getDefaultStr());
-        options.scalingFactor.setSelectedItem(digit+"%");
-      }
-      int scaleFactor =
-        Integer.parseInt(scaleString.substring(0, scaleString.length()));
-    }
-    if (viewer.desktopSize.getValue() != null &&
-        viewer.desktopSize.getValue().split("x").length == 2) {
-      options.desktopSize.setSelected(true);
-      String desktopWidth = viewer.desktopSize.getValue().split("x")[0];
-      options.desktopWidth.setText(desktopWidth);
-      String desktopHeight = viewer.desktopSize.getValue().split("x")[1];
-      options.desktopHeight.setText(desktopHeight);
-    }
+  public synchronized int currentEncoding() {
+    return currentEncoding;
   }
 
-  public void getOptions() {
-    autoSelect = options.autoSelect.isSelected();
-    if (fullColour != options.fullColour.isSelected()) {
-      formatChange = true;
-      forceNonincremental = true;
-    }
-    fullColour = options.fullColour.isSelected();
-    if (!fullColour) {
-      int newLowColourLevel = (options.veryLowColour.isSelected() ? 0 :
-                               options.lowColour.isSelected() ? 1 : 2);
-      if (newLowColourLevel != lowColourLevel) {
-        lowColourLevel = newLowColourLevel;
-        formatChange = true;
-        forceNonincremental = true;
-      }
-    }
-    int newEncoding = (options.zrle.isSelected() ?  Encodings.encodingZRLE :
-                       options.hextile.isSelected() ?  Encodings.encodingHextile :
-                       options.tight.isSelected() ?  Encodings.encodingTight :
-                       Encodings.encodingRaw);
-    if (newEncoding != currentEncoding) {
-      currentEncoding = newEncoding;
-      encodingChange = true;
-    }
+  public void handleOptions()
+  {
 
-    viewer.customCompressLevel.setParam(options.customCompressLevel.isSelected());
-    if (cp.customCompressLevel != viewer.customCompressLevel.getValue()) {
-      cp.customCompressLevel = viewer.customCompressLevel.getValue();
-      encodingChange = true;
-    }
-    if (Integer.parseInt(options.compressLevel.getSelectedItem().toString()) >= 0 &&
-        Integer.parseInt(options.compressLevel.getSelectedItem().toString()) <= 9) {
-      viewer.compressLevel.setParam(options.compressLevel.getSelectedItem().toString());
-    } else {
-      viewer.compressLevel.setParam(viewer.compressLevel.getDefaultStr());
-    }
-    if (cp.compressLevel != viewer.compressLevel.getValue()) {
-      cp.compressLevel = viewer.compressLevel.getValue();
-      encodingChange = true;
-    }
-    viewer.noJpeg.setParam(!options.noJpeg.isSelected());
-    if (cp.noJpeg != viewer.noJpeg.getValue()) {
-      cp.noJpeg = viewer.noJpeg.getValue();
-      encodingChange = true;
-    }
-    viewer.qualityLevel.setParam(options.qualityLevel.getSelectedItem().toString());
-    if (cp.qualityLevel != viewer.qualityLevel.getValue()) {
-      cp.qualityLevel = viewer.qualityLevel.getValue();
-      encodingChange = true;
-    }
-    if (!options.x509ca.getText().equals(""))
-        CSecurityTLS.x509ca.setParam(options.x509ca.getText());
-    if (!options.x509crl.getText().equals(""))
-        CSecurityTLS.x509crl.setParam(options.x509crl.getText());
-    viewer.sendLocalUsername.setParam(options.sendLocalUsername.isSelected());
-
-    viewer.viewOnly.setParam(options.viewOnly.isSelected());
-    viewer.acceptClipboard.setParam(options.acceptClipboard.isSelected());
-    viewer.sendClipboard.setParam(options.sendClipboard.isSelected());
-    viewer.acceptBell.setParam(options.acceptBell.isSelected());
-    String scaleString =
-      options.scalingFactor.getSelectedItem().toString();
-    String oldScaleFactor = viewer.scalingFactor.getValue();
-    if (scaleString.equalsIgnoreCase("Fixed Aspect Ratio")) {
-      scaleString = new String("FixedRatio");
-    } else if (scaleString.equalsIgnoreCase("Auto")) {
-      scaleString = new String("Auto");
-    } else {
-      scaleString=scaleString.substring(0, scaleString.length()-1);
-    }
-    if (!oldScaleFactor.equals(scaleString)) {
-      viewer.scalingFactor.setParam(scaleString);
-      if ((options.fullScreen.isSelected() == fullScreen) &&
-          (desktop != null))
-        recreateViewport();
+    if (viewport != null && viewport.isVisible()) {
+      viewport.toFront();
+      viewport.requestFocus();
     }
 
-    clipboardDialog.setSendingEnabled(viewer.sendClipboard.getValue());
-    viewer.menuKey.setParam(MenuKey.getMenuKeySymbols()[options.menuKey.getSelectedIndex()].name);
-    F8Menu.f8.setText("Send "+KeyEvent.getKeyText(MenuKey.getMenuKeyCode()));
+    // Checking all the details of the current set of encodings is just
+    // a pain. Assume something has changed, as resending the encoding
+    // list is cheap. Avoid overriding what the auto logic has selected
+    // though.
+    if (!autoSelect.getValue()) {
+      int encNum = Encodings.encodingNum(preferredEncoding.getValue());
 
-    setShared(options.shared.isSelected());
-    viewer.useLocalCursor.setParam(options.useLocalCursor.isSelected());
-    if (cp.supportsLocalCursor != viewer.useLocalCursor.getValue()) {
-      cp.supportsLocalCursor = viewer.useLocalCursor.getValue();
-      encodingChange = true;
-      if (desktop != null)
-        desktop.resetLocalCursor();
+      if (encNum != -1)
+        this.currentEncoding = encNum;
     }
-    viewer.extSSH.setParam(options.sshUseExt.isSelected());
 
-    checkEncodings();
+    this.cp.supportsLocalCursor = useLocalCursor.getValue();
 
-    if (state() != RFBSTATE_NORMAL) {
-      /* Process security types which don't use encryption */
-      if (options.encNone.isSelected()) {
-        if (options.secNone.isSelected())
-          Security.EnableSecType(Security.secTypeNone);
-        if (options.secVnc.isSelected())
-          Security.EnableSecType(Security.secTypeVncAuth);
-        if (options.secPlain.isSelected())
-          Security.EnableSecType(Security.secTypePlain);
-        if (options.secIdent.isSelected())
-          Security.EnableSecType(Security.secTypeIdent);
-      } else {
-        Security.DisableSecType(Security.secTypeNone);
-        Security.DisableSecType(Security.secTypeVncAuth);
-        Security.DisableSecType(Security.secTypePlain);
-        Security.DisableSecType(Security.secTypeIdent);
-      }
+    if (customCompressLevel.getValue())
+      this.cp.compressLevel = compressLevel.getValue();
+    else
+      this.cp.compressLevel = -1;
 
-      /* Process security types which use TLS encryption */
-      if (options.encTLS.isSelected()) {
-        if (options.secNone.isSelected())
-          Security.EnableSecType(Security.secTypeTLSNone);
-        if (options.secVnc.isSelected())
-          Security.EnableSecType(Security.secTypeTLSVnc);
-        if (options.secPlain.isSelected())
-          Security.EnableSecType(Security.secTypeTLSPlain);
-        if (options.secIdent.isSelected())
-          Security.EnableSecType(Security.secTypeTLSIdent);
-      } else {
-        Security.DisableSecType(Security.secTypeTLSNone);
-        Security.DisableSecType(Security.secTypeTLSVnc);
-        Security.DisableSecType(Security.secTypeTLSPlain);
-        Security.DisableSecType(Security.secTypeTLSIdent);
-      }
+    if (!noJpeg.getValue() && !autoSelect.getValue())
+      this.cp.qualityLevel = qualityLevel.getValue();
+    else
+      this.cp.qualityLevel = -1;
 
-      /* Process security types which use X509 encryption */
-      if (options.encX509.isSelected()) {
-        if (options.secNone.isSelected())
-          Security.EnableSecType(Security.secTypeX509None);
-        if (options.secVnc.isSelected())
-          Security.EnableSecType(Security.secTypeX509Vnc);
-        if (options.secPlain.isSelected())
-          Security.EnableSecType(Security.secTypeX509Plain);
-        if (options.secIdent.isSelected())
-          Security.EnableSecType(Security.secTypeX509Ident);
-      } else {
-        Security.DisableSecType(Security.secTypeX509None);
-        Security.DisableSecType(Security.secTypeX509Vnc);
-        Security.DisableSecType(Security.secTypeX509Plain);
-        Security.DisableSecType(Security.secTypeX509Ident);
-      }
+    this.encodingChange = true;
 
-      /* Process *None security types */
-      if (options.secNone.isSelected()) {
-        if (options.encNone.isSelected())
-          Security.EnableSecType(Security.secTypeNone);
-        if (options.encTLS.isSelected())
-          Security.EnableSecType(Security.secTypeTLSNone);
-        if (options.encX509.isSelected())
-          Security.EnableSecType(Security.secTypeX509None);
-      } else {
-        Security.DisableSecType(Security.secTypeNone);
-        Security.DisableSecType(Security.secTypeTLSNone);
-        Security.DisableSecType(Security.secTypeX509None);
-      }
+    // Format changes refreshes the entire screen though and are therefore
+    // very costly. It's probably worth the effort to see if it is necessary
+    // here.
+    PixelFormat pf;
 
-      /* Process *Vnc security types */
-      if (options.secVnc.isSelected()) {
-        if (options.encNone.isSelected())
-          Security.EnableSecType(Security.secTypeVncAuth);
-        if (options.encTLS.isSelected())
-          Security.EnableSecType(Security.secTypeTLSVnc);
-        if (options.encX509.isSelected())
-          Security.EnableSecType(Security.secTypeX509Vnc);
-      } else {
-        Security.DisableSecType(Security.secTypeVncAuth);
-        Security.DisableSecType(Security.secTypeTLSVnc);
-        Security.DisableSecType(Security.secTypeX509Vnc);
-      }
+    if (fullColor.getValue()) {
+      pf = fullColorPF;
+    } else {
+      if (lowColorLevel.getValue() == 0)
+        pf = verylowColorPF;
+      else if (lowColorLevel.getValue() == 1)
+        pf = lowColorPF;
+      else
+        pf = mediumColorPF;
+    }
 
-      /* Process *Plain security types */
-      if (options.secPlain.isSelected()) {
-        if (options.encNone.isSelected())
-          Security.EnableSecType(Security.secTypePlain);
-        if (options.encTLS.isSelected())
-          Security.EnableSecType(Security.secTypeTLSPlain);
-        if (options.encX509.isSelected())
-          Security.EnableSecType(Security.secTypeX509Plain);
-      } else {
-        Security.DisableSecType(Security.secTypePlain);
-        Security.DisableSecType(Security.secTypeTLSPlain);
-        Security.DisableSecType(Security.secTypeX509Plain);
-      }
+    if (!pf.equal(this.cp.pf())) {
+      this.formatChange = true;
 
-      /* Process *Ident security types */
-      if (options.secIdent.isSelected()) {
-        if (options.encNone.isSelected())
-          Security.EnableSecType(Security.secTypeIdent);
-        if (options.encTLS.isSelected())
-          Security.EnableSecType(Security.secTypeTLSIdent);
-        if (options.encX509.isSelected())
-          Security.EnableSecType(Security.secTypeX509Ident);
-      } else {
-        Security.DisableSecType(Security.secTypeIdent);
-        Security.DisableSecType(Security.secTypeTLSIdent);
-        Security.DisableSecType(Security.secTypeX509Ident);
-      }
-      if (options.sshTunnel.isSelected()) {
-        if (options.sshUseGateway.isSelected()) {
-          String user = options.sshUser.getText();
-          String host = options.sshHost.getText();
-          String port = options.sshPort.getText();
-          viewer.via.setParam(user+"@"+host+":"+port);
-        } else {
-          viewer.tunnel.setParam(true);
-        }
-      }
-      viewer.extSSH.setParam(options.sshUseExt.isSelected());
-      viewer.extSSHClient.setParam(options.sshClient.getText());
-      if (options.sshArgsCustom.isSelected())
-        viewer.extSSHArgs.setParam(options.sshArguments.getText());
-      viewer.sshConfig.setParam(options.sshConfig.getText());
-      viewer.sshKeyFile.setParam(options.sshKeyFile.getText());
-    }
-    String desktopSize = (options.desktopSize.isSelected()) ?
-        options.desktopWidth.getText() + "x" + options.desktopHeight.getText() : "";
-    viewer.desktopSize.setParam(desktopSize);
-    if (options.fullScreen.isSelected() ^ fullScreen) {
-      viewer.fullScreenAllMonitors.setParam(options.fullScreenAllMonitors.isSelected());
-      toggleFullScreen();
-    } else {
-      if (viewer.fullScreenAllMonitors.getValue() !=
-          options.fullScreenAllMonitors.isSelected()) {
-        viewer.fullScreenAllMonitors.setParam(options.fullScreenAllMonitors.isSelected());
-        if (desktop != null)
-          recreateViewport();
-      } else {
-        viewer.fullScreenAllMonitors.setParam(options.fullScreenAllMonitors.isSelected());
-      }
+      // Without fences, we cannot safely trigger an update request directly
+      // but must wait for the next update to arrive.
+      if (this.supportsSyncFence)
+        this.requestNewUpdate();
     }
+
   }
 
   public void toggleFullScreen() {
-    if (viewer.embed.getValue())
+    if (embed.getValue())
       return;
-    fullScreen = !fullScreen;
-    menu.fullScreen.setSelected(fullScreen);
+    fullScreen.setParam(!fullScreen.getValue());
     if (viewport != null) {
       if (!viewport.lionFSSupported()) {
         recreateViewport();
@@ -1369,7 +856,7 @@ public class CConn extends CConnection implements
   }
 
   public void writeKeyEvent(KeyEvent ev) {
-    if (viewer.viewOnly.getValue() || shuttingDown)
+    if (viewOnly.getValue() || shuttingDown)
       return;
 
     boolean down = (ev.getID() == KeyEvent.KEY_PRESSED);
@@ -1539,12 +1026,6 @@ public class CConn extends CConnection implements
 
   // the following never change so need no synchronization:
 
-
-  // viewer object is only ever accessed by the GUI thread so needs no
-  // synchronization (except for one test in DesktopWindow - see comment
-  // there).
-  VncViewer viewer;
-
   // access to desktop by different threads is specified in DesktopWindow
 
   // the following need no synchronization:
@@ -1559,27 +1040,21 @@ public class CConn extends CConnection implements
   // reading and writing int and boolean is atomic in java, so no
   // synchronization of the following flags is needed:
 
-  int lowColourLevel;
-
 
   // All menu, options, about and info stuff is done in the GUI thread (apart
   // from when constructed).
-  F8Menu menu;
-  OptionsDialog options;
-
-  // clipboard sync issues?
-  ClipboardDialog clipboardDialog;
 
   // the following are only ever accessed by the GUI thread:
   int buttonMask;
 
+  private String serverHost;
+  private int serverPort;
   private Socket sock;
 
   protected DesktopWindow desktop;
 
-  // FIXME: should be private
-  public PixelFormat serverPF;
-  private PixelFormat fullColourPF;
+  private PixelFormat serverPF;
+  private PixelFormat fullColorPF;
 
   private boolean pendingPFChange;
   private PixelFormat pendingPF;
@@ -1597,11 +1072,7 @@ public class CConn extends CConnection implements
 
   private boolean supportsSyncFence;
 
-  public int menuKeyCode;
   Viewport viewport;
-  private boolean fullColour;
-  private boolean autoSelect;
-  boolean fullScreen;
   private HashMap<Integer, Integer> downKeySym;
   public ActionListener closeListener = null;
 
index 441846cff792156008d835feb387c7444680d848..1404c227dfc7bf99138157ff30ee668552b2342b 100644 (file)
@@ -26,83 +26,126 @@ import java.io.*;
 import java.nio.*;
 import javax.swing.*;
 import javax.swing.border.*;
+import javax.swing.event.*;
 import javax.swing.text.*;
 
 import com.tigervnc.rfb.LogWriter;
 
+import static com.tigervnc.vncviewer.Parameters.*;
+
 class ClipboardDialog extends Dialog {
 
-  private class VncTransferHandler extends TransferHandler {
-    // Custom TransferHandler designed to limit the size of outbound
-    // clipboard transfers to VncViewer.maxCutText.getValue() bytes.
-    private LogWriter vlog = new LogWriter("VncTransferHandler");
-
-    public void exportToClipboard(JComponent c, Clipboard clip, int a)
-        throws IllegalStateException {
-      if (!(c instanceof JTextComponent)) return;
-      StringSelection selection =
-        new StringSelection(((JTextComponent)c).getText());
-      clip.setContents(selection, null);
-    }
+  protected static class MyJTextArea extends JTextArea {
 
-    public boolean importData(JComponent c, Transferable t) {
-      if (canImport(c, t.getTransferDataFlavors())) {
+         private class VncTransferHandler extends TransferHandler {
+           // Custom TransferHandler designed to limit the size of outbound
+           // clipboard transfers to VncViewer.maxCutText.getValue() bytes.
+           private LogWriter vlog = new LogWriter("VncTransferHandler");
+       
+           public void exportToClipboard(JComponent c, Clipboard clip, int a)
+               throws IllegalStateException {
+             if (!(c instanceof JTextComponent)) return;
+        String text = ((JTextComponent)c).getText();
         try {
-          DataFlavor VncFlavor = null;
-          for (DataFlavor f : t.getTransferDataFlavors())
-            if (f.isFlavorTextType() && f.isRepresentationClassInputStream())
-              VncFlavor = f;
-          if (VncFlavor == null) return false;
-          Reader reader = (Reader)VncFlavor.getReaderForText(t);
-          CharBuffer cbuf =
-            CharBuffer.allocate(VncViewer.maxCutText.getValue());
-          cbuf.limit(reader.read(cbuf.array(), 0, cbuf.length()));
-          reader.close();
-          if (c instanceof JTextComponent)
-            ((JTextComponent)c).setText(cbuf.toString());
-          return true;
-        } catch (OutOfMemoryError oome) {
-          vlog.error("ERROR: Too much data on local clipboard!");
-        } catch (UnsupportedFlavorException ufe) {
-          // Skip import
-          vlog.info(ufe.toString());
-        } catch (IOException ioe) {
-          // Skip import
-          vlog.info(ioe.toString());
+          if (text.equals((String)clip.getData(DataFlavor.stringFlavor))) return;
+        } catch (IOException e) {
+          // worst case we set the clipboard contents unnecessarily
+               vlog.info(e.toString());
+        } catch (UnsupportedFlavorException e) {
+          // worst case we set the clipboard contents unnecessarily
+               vlog.info(e.toString());
         }
-      }
-      return false;
-    }
+             StringSelection selection = new StringSelection(text);
+             clip.setContents(selection, null);
+           }
+       
+           public boolean importData(JComponent c, Transferable t) {
+             if (canImport(c, t.getTransferDataFlavors())) {
+               try {
+                 DataFlavor VncFlavor = null;
+                 for (DataFlavor f : t.getTransferDataFlavors()) {
+                   if (f.isMimeTypeEqual("text/plain") &&
+                  f.isRepresentationClassInputStream()) {
+                     VncFlavor = f;
+                break;
+              }
+            }
+                 if (VncFlavor == null) return false;
+                 CharBuffer cbuf =
+                   CharBuffer.allocate(maxCutText.getValue());
+                 Reader reader = (Reader)VncFlavor.getReaderForText(t);
+            int n = reader.read(cbuf.array(), 0, cbuf.length());
+                 reader.close();
+            // reader returns -1 (EOF) for empty clipboard
+            cbuf.limit(n < 0 ? 0 : n);
+                 if (c instanceof JTextComponent)
+              if (!cbuf.toString().equals(((JTextComponent)c).getText()))
+                     ((JTextComponent)c).setText(cbuf.toString());
+                 return true;
+               } catch (OutOfMemoryError e) {
+                 vlog.error("ERROR: Too much data on local clipboard!");
+               } catch (UnsupportedFlavorException e) {
+                 // Skip import
+                 vlog.info(e.toString());
+               } catch (IOException e) {
+                 // Skip import
+                 vlog.info(e.toString());
+               }
+             }
+             return false;
+           }
+       
+           public boolean canImport(JComponent c, DataFlavor[] flavors) {
+             for (DataFlavor f : flavors)
+               if (f.isMimeTypeEqual("text/plain") &&
+              f.isRepresentationClassReader()) {
+                 return true;
+          }
+             return false;
+           }
+         }
+
+         private class MyTextListener implements DocumentListener {
+           public MyTextListener() { }
+       
+           public void changedUpdate(DocumentEvent e) { } 
+
+           public void insertUpdate(DocumentEvent e) {
+        if (!listen) return;
+             String text = textArea.getText();
+             if (sendClipboard.getValue())
+               VncViewer.cc.writeClientCutText(text, text.length());
+           }
+       
+           public void removeUpdate(DocumentEvent e) { }
+         }
 
-    public boolean canImport(JComponent c, DataFlavor[] flavors) {
-      for (DataFlavor f : flavors)
-        if (f.isFlavorTextType() && f.isRepresentationClassReader())
-          return true;
-      return false;
+    public MyJTextArea() {
+      super();
+      setTransferHandler(new VncTransferHandler());
+      getDocument().addDocumentListener(new MyTextListener());
+      // If the textArea can receive the focus, then text within the textArea
+      // can be selected.  On platforms that don't support separate selection
+      // and clipboard buffers, this triggers a replacement of the textAra's
+      // contents with the selected text.
+      setFocusable(false);
+      setLineWrap(false);
+      setWrapStyleWord(true);
     }
   }
 
-  public ClipboardDialog(CConn cc_) {
+  public ClipboardDialog() {
     super(false);
     setTitle("VNC Clipboard Viewer");
     setPreferredSize(new Dimension(640, 480));
-    addWindowFocusListener(new WindowAdapter() {
+    addWindowFocusListener(new WindowFocusListener() {
       // Necessary to ensure that updates from the system clipboard
-      // still occur when the ClipboardDialog has the focus.
-      public void WindowGainedFocus(WindowEvent e) {
+      // are propagated to the textArea when the dialog is visible.
+      public void windowGainedFocus(WindowEvent e) {
         clientCutText();
       }
+      public void windowLostFocus(WindowEvent e) { }
     });
-    cc = cc_;
-    textArea = new JTextArea();
-    textArea.setTransferHandler(new VncTransferHandler());
-    // If the textArea can receive the focus, then text within the textArea
-    // can be selected.  On platforms that don't support separate selection
-    // and clipboard buffers, this triggers a replacement of the textAra's
-    // contents with the selected text.
-    textArea.setFocusable(false);
-    textArea.setLineWrap(false);
-    textArea.setWrapStyleWord(true);
     JScrollPane sp = new JScrollPane(textArea);
     getContentPane().add(sp, BorderLayout.CENTER);
     // button panel placed below the scrollpane
@@ -118,19 +161,40 @@ class ClipboardDialog extends Dialog {
     pack();
   }
 
-  public void serverCutText(String str, int len) {
+  public static void showDialog(Container c) {
+    if (dialog == null)
+      dialog = new ClipboardDialog();
+    dialog.show(c);
+  }
+
+  public void show(Container c) {
+    super.showDialog(c);
+  }
+
+  public void endDialog() {
+    super.endDialog();
+    dialog.dispose();
+  }
+
+  public static void serverCutText(String str) {
+    if (textArea.getText().equals(str))
+      return;
+    // Update the text area with incoming serverCutText.  We need to diable
+    // the DocumentListener temporarily to prevent an clientCutText msg from
+    // being sent back to the server when the textArea is updated.
+    listen = false;
     textArea.setText(str);
     textArea.copy();
+    listen = true;
   }
 
-  public void clientCutText() {
-    int hc = textArea.getText().hashCode();
+  public static void clientCutText() {
+    // Update the textArea with the current contents of the system clipboard.
+    // The TransferHandler ensures that the textArea's contents are only 
+    // changed when they differ from the clipboard's.  If the textArea is
+    // updated, the DocumentListener will trigger an RFB clientCutText msg.
     textArea.paste();
     textArea.setCaretPosition(0);
-    String text = textArea.getText();
-    if (cc.viewer.sendClipboard.getValue())
-      if (hc != text.hashCode())
-        cc.writeClientCutText(text, text.length());
   }
 
   public void setSendingEnabled(boolean b) {
@@ -140,18 +204,20 @@ class ClipboardDialog extends Dialog {
   public void actionPerformed(ActionEvent e) {
     Object s = e.getSource();
     if (s instanceof JButton && (JButton)s == clearButton) {
-      serverCutText(new String(""), 0);
+      serverCutText(new String(""));
     } else if (s instanceof JButton && (JButton)s == sendButton) {
       String text = textArea.getText();
-      cc.writeClientCutText(text, text.length());
+      VncViewer.cc.writeClientCutText(text, text.length());
       endDialog();
     } else if (s instanceof JButton && (JButton)s == cancelButton) {
       endDialog();
     }
   }
 
-  CConn cc;
-  JTextArea textArea;
-  JButton clearButton, sendButton, cancelButton;
+  private JButton clearButton, sendButton, cancelButton;
+  private static boolean listen = true;
+  static ClipboardDialog dialog;
+  static MyJTextArea textArea = new MyJTextArea();
+  static Toolkit tk = Toolkit.getDefaultToolkit();
   static LogWriter vlog = new LogWriter("ClipboardDialog");
 }
index ff48fc1c8b9a938dbac178e791345717c760901e..de2d2cd51487654b80fd09859a0ef87520a4157b 100644 (file)
@@ -43,18 +43,22 @@ import com.tigervnc.rfb.*;
 import com.tigervnc.rfb.Cursor;
 import com.tigervnc.rfb.Point;
 
+import static com.tigervnc.vncviewer.Parameters.*;
+
 class DesktopWindow extends JPanel implements Runnable, MouseListener,
   MouseMotionListener, MouseWheelListener, KeyListener {
 
   ////////////////////////////////////////////////////////////////////
   // The following methods are all called from the RFB thread
 
-  public DesktopWindow(int width, int height, PixelFormat serverPF,
+  public DesktopWindow(int width, int height, String name, PixelFormat serverPF,
                        CConn cc_) {
     cc = cc_;
     setSize(width, height);
     setScaledSize();
     setOpaque(false);
+    if (cc.viewport != null)
+      cc.viewport.setName(name);
     GraphicsEnvironment ge =
       GraphicsEnvironment.getLocalGraphicsEnvironment();
     GraphicsDevice gd = ge.getDefaultScreenDevice();
@@ -67,7 +71,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
     } else {
       vlog.debug("GraphicsDevice does not support HW acceleration.");
     }
-    im = new BIPixelBuffer(width, height, cc, this);
+    im = new BIPixelBuffer(serverPF, width, height, this);
 
     cursor = new Cursor();
     cursorBacking = new ManagedPixelBuffer();
@@ -86,7 +90,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
     addKeyListener(this);
     addFocusListener(new FocusAdapter() {
       public void focusGained(FocusEvent e) {
-        cc.clipboardDialog.clientCutText();
+        ClipboardDialog.clientCutText();
       }
       public void focusLost(FocusEvent e) {
         cc.releaseDownKeys();
@@ -94,6 +98,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
     });
     setFocusTraversalKeysEnabled(false);
     setFocusable(true);
+    OptionsDialog.addCallback("handleOptions", this);
   }
 
   public int width() {
@@ -125,10 +130,8 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
     // might be being altered by the GUI thread.  However it's only a single
     // boolean and it doesn't matter if we get the wrong value anyway.
 
-    synchronized(cc.viewer.useLocalCursor) {
-      if (!cc.viewer.useLocalCursor.getValue())
-        return;
-    }
+    if (!useLocalCursor.getValue())
+      return;
 
     hideLocalCursor();
 
@@ -319,7 +322,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
   }
 
   public void setScaledSize() {
-    String scaleString = cc.viewer.scalingFactor.getValue();
+    String scaleString = scalingFactor.getValue();
     if (!scaleString.equalsIgnoreCase("Auto") &&
         !scaleString.equalsIgnoreCase("FixedRatio")) {
       int scalingFactor = Integer.parseInt(scaleString);
@@ -369,7 +372,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
 
   // Mouse-Motion callback function
   private void mouseMotionCB(MouseEvent e) {
-    if (!cc.viewer.viewOnly.getValue() &&
+    if (!viewOnly.getValue() &&
         e.getX() >= 0 && e.getX() <= scaledWidth &&
         e.getY() >= 0 && e.getY() <= scaledHeight)
       cc.writePointerEvent(e);
@@ -394,7 +397,7 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
 
   // Mouse callback function
   private void mouseCB(MouseEvent e) {
-    if (!cc.viewer.viewOnly.getValue()) {
+    if (!viewOnly.getValue()) {
       if ((e.getID() == MouseEvent.MOUSE_RELEASED) ||
           (e.getX() >= 0 && e.getX() <= scaledWidth &&
            e.getY() >= 0 && e.getY() <= scaledHeight))
@@ -407,14 +410,14 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
   public void mousePressed(MouseEvent e) { mouseCB(e); }
   public void mouseClicked(MouseEvent e) {}
   public void mouseEntered(MouseEvent e) {
-    if (cc.viewer.embed.getValue())
+    if (embed.getValue())
       requestFocus();
   }
   public void mouseExited(MouseEvent e) {}
 
   // MouseWheel callback function
   private void mouseWheelCB(MouseWheelEvent e) {
-    if (!cc.viewer.viewOnly.getValue())
+    if (!viewOnly.getValue())
       cc.writeWheelEvent(e);
   }
 
@@ -443,14 +446,15 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
         lastY : (int)Math.floor(lastY * scaleHeightRatio);
       java.awt.Point ev = new java.awt.Point(lastX, lastY);
       ev.translate(sx - lastX, sy - lastY);
-      cc.showMenu((int)ev.getX(), (int)ev.getY());
+      F8Menu menu = new F8Menu(cc);
+      menu.show(this, (int)ev.getX(), (int)ev.getY());
       return;
     }
     int ctrlAltShiftMask = Event.SHIFT_MASK | Event.CTRL_MASK | Event.ALT_MASK;
     if ((e.getModifiers() & ctrlAltShiftMask) == ctrlAltShiftMask) {
       switch (e.getKeyCode()) {
         case KeyEvent.VK_A:
-          cc.showAbout();
+          VncViewer.showAbout(this);
           return;
         case KeyEvent.VK_F:
           cc.toggleFullScreen();
@@ -462,10 +466,10 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
           cc.showInfo();
           return;
         case KeyEvent.VK_O:
-          cc.options.showDialog(cc.viewport);
+            OptionsDialog.showDialog(cc.viewport);
           return;
         case KeyEvent.VK_W:
-          VncViewer.newViewer(cc.viewer);
+          VncViewer.newViewer();
           return;
         case KeyEvent.VK_LEFT:
         case KeyEvent.VK_RIGHT:
@@ -564,6 +568,14 @@ class DesktopWindow extends JPanel implements Runnable, MouseListener,
     setColourMapEntriesTimerThread = null;
   }
 
+  public void handleOptions()
+  {
+    if (fullScreen.getValue() && Viewport.getFullScreenWindow() == null)
+      cc.toggleFullScreen();
+    else if (!fullScreen.getValue() && Viewport.getFullScreenWindow() != null)
+      cc.toggleFullScreen();
+  }
+
   // access to cc by different threads is specified in CConn
   CConn cc;
 
index 8bf197995fd501a6e01091294b37b7fc15fdc39e..4419159ebace6f7ded9f47b1b1988598d17f0356 100644 (file)
@@ -31,6 +31,7 @@ package com.tigervnc.vncviewer;
 import java.awt.*;
 import java.awt.Dialog.*;
 import java.awt.event.*;
+import java.io.File;
 import javax.swing.*;
 import javax.swing.border.*;
 import javax.swing.text.*;
@@ -52,7 +53,7 @@ class Dialog extends JDialog implements ActionListener,
     }
   }
 
-  public boolean showDialog(Component c) {
+  public void showDialog(Component c) {
     initDialog();
     if (c != null) {
       setLocationRelativeTo(c);
@@ -70,11 +71,10 @@ class Dialog extends JDialog implements ActionListener,
     if (getModalityType() == ModalityType.APPLICATION_MODAL)
       setAlwaysOnTop(true);
     setVisible(true);
-    return ret;
   }
 
-  public boolean showDialog() {
-    return showDialog(null);
+  public void showDialog() {
+    showDialog(null);
   }
 
   public void endDialog() {
@@ -137,6 +137,21 @@ class Dialog extends JDialog implements ActionListener,
     return width + gap;
   }
 
+  public static File showChooser(String title, File defFile, Container c) {
+    JFileChooser fc = new JFileChooser(defFile);
+    fc.setDialogTitle(title);
+    fc.setApproveButtonText("OK  \u21B5");
+    fc.setFileHidingEnabled(false);
+    if (fc.showOpenDialog(c) == JFileChooser.APPROVE_OPTION)
+      return fc.getSelectedFile();
+    else
+      return null;
+  }
+
+  protected File showChooser(String title, File defFile) {
+    return showChooser(title, defFile, this);
+  }
+
   protected class GroupedJRadioButton extends JRadioButton {
     public GroupedJRadioButton(String l, ButtonGroup g, JComponent c) {
       super(l);
@@ -181,9 +196,9 @@ class Dialog extends JDialog implements ActionListener,
       JTextField jtf = (JTextField)editor.getEditorComponent();
       jtf.setDocument(doc);
     }
+
   }
 
   private Window fullScreenWindow;
-  protected boolean ret = true;
 
 }
index 472f11f8140774c6d3dd9278a6bcb7f50c4c3206..6aadb2d3e9cd517cc49d2b3e3875bcb2954f9e1e 100644 (file)
@@ -22,48 +22,58 @@ package com.tigervnc.vncviewer;
 import java.awt.*;
 import java.awt.Cursor;
 import java.awt.event.*;
+import java.io.File;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JDialog;
 import javax.swing.JFrame;
 import javax.swing.JFileChooser;
-import javax.swing.JPopupMenu;
 import javax.swing.JMenuItem;
-import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPopupMenu;
 
 import com.tigervnc.rfb.*;
 
+import static com.tigervnc.vncviewer.Parameters.*;
+
 public class F8Menu extends JPopupMenu implements ActionListener {
-  public F8Menu(CConn cc_) {
+  public F8Menu(CConn cc) {
     super("VNC Menu");
     setLightWeightPopupEnabled(false);
-    cc = cc_;
+    String os = System.getProperty("os.name");
+    if (os.startsWith("Windows"))
+      com.sun.java.swing.plaf.windows.WindowsLookAndFeel.setMnemonicHidden(false);
+    this.cc = cc;
     restore    = addMenuItem("Restore",KeyEvent.VK_R);
-    restore.setEnabled(!cc.viewer.embed.getValue());
+    restore.setEnabled(!embed.getValue());
     move       = addMenuItem("Move");
     move.setEnabled(false);
     size       = addMenuItem("Size");
     size.setEnabled(false);
     minimize   = addMenuItem("Minimize", KeyEvent.VK_N);
-    minimize.setEnabled(!cc.viewer.embed.getValue());
+    minimize.setEnabled(!embed.getValue());
     maximize   = addMenuItem("Maximize", KeyEvent.VK_X);
-    maximize.setEnabled(!cc.viewer.embed.getValue());
+    maximize.setEnabled(!embed.getValue());
     addSeparator();
     exit       = addMenuItem("Close Viewer", KeyEvent.VK_C);
     addSeparator();
-    fullScreen = new JCheckBoxMenuItem("Full Screen");
-    fullScreen.setMnemonic(KeyEvent.VK_F);
-    fullScreen.setSelected(cc.fullScreen);
-    fullScreen.addActionListener(this);
-    fullScreen.setEnabled(!cc.viewer.embed.getValue());
-    add(fullScreen);
+    fullScreenCheckbox = new JCheckBoxMenuItem("Full Screen");
+    fullScreenCheckbox.setMnemonic(KeyEvent.VK_F);
+    fullScreenCheckbox.setSelected(fullScreen.getValue());
+    fullScreenCheckbox.addActionListener(this);
+    fullScreenCheckbox.setEnabled(!embed.getValue());
+    add(fullScreenCheckbox);
     addSeparator();
     clipboard  = addMenuItem("Clipboard...");
     addSeparator();
-    f8 = addMenuItem("Send "+KeyEvent.getKeyText(MenuKey.getMenuKeyCode()), MenuKey.getMenuKeyCode());
+    int keyCode = MenuKey.getMenuKeyCode();
+    String keyText = KeyEvent.getKeyText(keyCode);
+    f8 = addMenuItem("Send "+keyText, keyCode);
     ctrlAltDel = addMenuItem("Send Ctrl-Alt-Del");
     addSeparator();
     refresh    = addMenuItem("Refresh Screen", KeyEvent.VK_H);
     addSeparator();
     newConn    = addMenuItem("New connection...", KeyEvent.VK_W);
-    newConn.setEnabled(!cc.viewer.embed.getValue());
+    newConn.setEnabled(!embed.getValue());
     options    = addMenuItem("Options...", KeyEvent.VK_O);
     save       = addMenuItem("Save connection info as...", KeyEvent.VK_S);
     info       = addMenuItem("Connection info...", KeyEvent.VK_I);
@@ -94,19 +104,20 @@ public class F8Menu extends JPopupMenu implements ActionListener {
   public void actionPerformed(ActionEvent ev) {
     if (actionMatch(ev, exit)) {
       cc.close();
-    } else if (actionMatch(ev, fullScreen)) {
+    } else if (actionMatch(ev, fullScreenCheckbox)) {
       cc.toggleFullScreen();
     } else if (actionMatch(ev, restore)) {
-      if (cc.fullScreen) cc.toggleFullScreen();
+      if (fullScreen.getValue()) cc.toggleFullScreen();
       cc.viewport.setExtendedState(JFrame.NORMAL);
     } else if (actionMatch(ev, minimize)) {
-      if (cc.fullScreen) cc.toggleFullScreen();
+      if (fullScreen.getValue()) cc.toggleFullScreen();
       cc.viewport.setExtendedState(JFrame.ICONIFIED);
     } else if (actionMatch(ev, maximize)) {
-      if (cc.fullScreen) cc.toggleFullScreen();
+      if (fullScreen.getValue()) cc.toggleFullScreen();
       cc.viewport.setExtendedState(JFrame.MAXIMIZED_BOTH);
     } else if (actionMatch(ev, clipboard)) {
-      cc.clipboardDialog.showDialog(cc.viewport);
+      //ClipboardDialog dlg = new ClipboardDialog(cc);
+      ClipboardDialog.showDialog(cc.viewport);
     } else if (actionMatch(ev, f8)) {
       cc.writeKeyEvent(MenuKey.getMenuKeySym(), true);
       cc.writeKeyEvent(MenuKey.getMenuKeySym(), false);
@@ -120,29 +131,37 @@ public class F8Menu extends JPopupMenu implements ActionListener {
     } else if (actionMatch(ev, refresh)) {
       cc.refresh();
     } else if (actionMatch(ev, newConn)) {
-      VncViewer.newViewer(cc.viewer);
+      VncViewer.newViewer();
     } else if (actionMatch(ev, options)) {
-      cc.options.showDialog(cc.viewport);
+      OptionsDialog.showDialog(cc.viewport);
     } else if (actionMatch(ev, save)) {
-      JFileChooser fc = new JFileChooser();
-      fc.setDialogTitle("Save current configuration as:");
-      fc.setApproveButtonText("OK");
-      fc.setFileHidingEnabled(false);
-      Window fullScreenWindow = Viewport.getFullScreenWindow();
-      if (fullScreenWindow != null)
-        Viewport.setFullScreenWindow(null);
-      int ret = fc.showOpenDialog(cc.viewport);
-      if (fullScreenWindow != null)
-        Viewport.setFullScreenWindow(fullScreenWindow);
-      if (ret == JFileChooser.APPROVE_OPTION) {
-        String filename = fc.getSelectedFile().toString();
-        if (filename != null)
-          Configuration.save(filename);
-      }
+           String title = "Save the TigerVNC configuration to file";
+           File dflt = new File(FileUtils.getVncHomeDir().concat("default.tigervnc"));
+           if (!dflt.exists() || !dflt.isFile())
+             dflt = new File(FileUtils.getVncHomeDir());
+           File f = Dialog.showChooser(title, dflt, this);
+           while (f != null && f.exists() && f.isFile()) {
+             String msg = f.getAbsolutePath();
+             msg = msg.concat(" already exists. Do you want to overwrite?");
+             Object[] options = {"Overwrite", "No  \u21B5"};
+             JOptionPane op =
+               new JOptionPane(msg, JOptionPane.QUESTION_MESSAGE,
+                               JOptionPane.OK_CANCEL_OPTION, null, options, options[1]);
+             JDialog dlg = op.createDialog(this, "TigerVNC Viewer");
+             dlg.setIconImage(VncViewer.frameIcon);
+             dlg.setAlwaysOnTop(true);
+             dlg.setVisible(true);
+             if (op.getValue() == options[0])
+               break;
+             else
+               f = Dialog.showChooser(title, f, this);
+           }
+           if (f != null && (!f.exists() || f.canWrite()))
+             saveViewerParameters(f.getAbsolutePath(), vncServerName.getValue());
     } else if (actionMatch(ev, info)) {
       cc.showInfo();
     } else if (actionMatch(ev, about)) {
-      cc.showAbout();
+      VncViewer.showAbout(cc.desktop);
     } else if (actionMatch(ev, dismiss)) {
       firePopupMenuCanceled();
     }
@@ -153,6 +172,6 @@ public class F8Menu extends JPopupMenu implements ActionListener {
   JMenuItem exit, clipboard, ctrlAltDel, refresh;
   JMenuItem newConn, options, save, info, about, dismiss;
   static JMenuItem f8;
-  JCheckBoxMenuItem fullScreen;
+  JCheckBoxMenuItem fullScreenCheckbox;
   static LogWriter vlog = new LogWriter("F8Menu");
 }
index 369b965d40ca1c578ff1112cc7adb3214247ac88..8d49a2e358ddc344a02cb3623dd0fc6d2b4a4192 100644 (file)
@@ -22,6 +22,7 @@ package com.tigervnc.vncviewer;
 import java.awt.*;
 import java.awt.event.*;
 import java.io.File;
+import java.lang.reflect.*;
 import java.text.Format;
 import java.text.NumberFormat;
 import javax.swing.*;
@@ -29,7 +30,9 @@ import javax.swing.border.*;
 import javax.swing.UIManager.*;
 import javax.swing.text.*;
 import java.util.*;
+import java.util.List;
 import java.util.Map.Entry;
+import java.util.prefs.*;
 
 import com.tigervnc.rfb.*;
 
@@ -44,6 +47,8 @@ import static java.awt.GridBagConstraints.RELATIVE;
 import static java.awt.GridBagConstraints.REMAINDER;
 import static java.awt.GridBagConstraints.VERTICAL;
 
+import static com.tigervnc.vncviewer.Parameters.*;
+
 class OptionsDialog extends Dialog {
 
   private class IntegerDocument extends PlainDocument {
@@ -88,47 +93,541 @@ class OptionsDialog extends Dialog {
     }
   }
 
-  // Constants
-  static LogWriter vlog = new LogWriter("OptionsDialog");
+  private static Map<String, Object> callbacks = new HashMap<String, Object>();
+  /* Compression */
+  JCheckBox autoselectCheckbox;
+
+  ButtonGroup encodingGroup;
+  JRadioButton tightButton;
+  JRadioButton zrleButton;
+  JRadioButton hextileButton;
+  JRadioButton rawButton;
+
+  ButtonGroup colorlevelGroup;
+  JRadioButton fullcolorButton;
+  JRadioButton mediumcolorButton;
+  JRadioButton lowcolorButton;
+  JRadioButton verylowcolorButton;
+
+  JCheckBox compressionCheckbox;
+  JCheckBox jpegCheckbox;
+  JComboBox compressionInput;
+  JComboBox jpegInput;
+
+  /* Security */
+  JCheckBox encNoneCheckbox;
+  JCheckBox encTLSCheckbox;
+  JCheckBox encX509Checkbox;
+  JTextField caInput;
+  JTextField crlInput;
+  JButton caChooser;
+  JButton crlChooser;
+
+  JCheckBox authNoneCheckbox;
+  JCheckBox authVncCheckbox;
+  JCheckBox authPlainCheckbox;
+  JCheckBox authIdentCheckbox;
+  JCheckBox sendLocalUsernameCheckbox;
 
-  CConn cc;
-  @SuppressWarnings({"rawtypes"})
-  JComboBox menuKey, compressLevel, qualityLevel, scalingFactor;
-  ButtonGroup encodingGroup, colourGroup, sshArgsGroup;
-  JRadioButton zrle, hextile, tight, raw, fullColour, mediumColour,
-               lowColour, veryLowColour, sshArgsDefault, sshArgsCustom;
-  JCheckBox autoSelect, customCompressLevel, noJpeg, viewOnly,
-            acceptClipboard, sendClipboard, acceptBell, desktopSize,
-            fullScreen, fullScreenAllMonitors, shared, useLocalCursor,
-            secVeNCrypt, encNone, encTLS, encX509, secNone, secVnc,
-            secPlain, secIdent, sendLocalUsername, sshTunnel, sshUseExt,
-            sshUseGateway;
-  JButton okButton, cancelButton, caButton, crlButton, cfLoadButton,
-          cfSaveAsButton, defSaveButton, defReloadButton, defClearButton,
-          sshConfigBrowser, sshKeyFileBrowser, sshClientBrowser;
-  JTextField desktopWidth, desktopHeight, x509ca, x509crl, sshUser, sshHost,
-             sshPort, sshClient, sshArguments, sshConfig, sshKeyFile;
-  JTabbedPane tabPane;
+  /* Input */
+  JCheckBox viewOnlyCheckbox;
+  JCheckBox acceptClipboardCheckbox;
+  JCheckBox sendClipboardCheckbox;
+  JComboBox menuKeyChoice;
+
+  /* Screen */
+  JCheckBox desktopSizeCheckbox;
+  JTextField desktopWidthInput;
+  JTextField desktopHeightInput;
+  JCheckBox fullScreenCheckbox;
+  JCheckBox fullScreenAllMonitorsCheckbox;
+  JComboBox scalingFactorInput;
+
+  /* Misc. */
+  JCheckBox sharedCheckbox;
+  JCheckBox localCursorCheckbox;
+  JCheckBox acceptBellCheckbox;
+
+  /* SSH */
+  JCheckBox tunnelCheckbox;
+  JCheckBox viaCheckbox;
+  JTextField viaUserInput;
+  JTextField viaHostInput;
+  JTextField viaPortInput;
+  JCheckBox extSSHCheckbox;
+  JTextField sshClientInput;
+  JButton sshClientChooser;
+  JRadioButton sshArgsDefaultButton;
+  JRadioButton sshArgsCustomButton;
+  JTextField sshArgsInput;
+  JTextField sshConfigInput;
+  JTextField sshKeyFileInput;
+  JButton sshConfigChooser;
+  JButton sshKeyFileChooser;
 
   @SuppressWarnings({"rawtypes","unchecked"})
-  public OptionsDialog(CConn cc_) {
+  public OptionsDialog() {
     super(true);
-    cc = cc_;
     setTitle("VNC Viewer Options");
     setResizable(false);
 
     getContentPane().setLayout(
       new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));
 
-    tabPane = new JTabbedPane();
+    JTabbedPane tabPane = new JTabbedPane();
     tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
 
     encodingGroup = new ButtonGroup();
-    colourGroup = new ButtonGroup();
-    sshArgsGroup = new ButtonGroup();
-    int indent = 0;
+    colorlevelGroup = new ButtonGroup();
+
+    // tabPane
+    tabPane.addTab("Compression", createCompressionPanel());
+    tabPane.addTab("Security", createSecurityPanel());
+    tabPane.addTab("Input", createInputPanel());
+    tabPane.addTab("Screen", createScreenPanel());
+    tabPane.addTab("Misc", createMiscPanel());
+    tabPane.addTab("SSH", createSshPanel());
+    tabPane.setBorder(BorderFactory.createEmptyBorder());
+    // Resize the tabPane if necessary to prevent scrolling
+    Insets tpi =
+      (Insets)UIManager.get("TabbedPane:TabbedPaneTabArea.contentMargins");
+    int minWidth = tpi.left + tpi.right;
+    for (int i = 0; i < tabPane.getTabCount(); i++)
+      minWidth += tabPane.getBoundsAt(i).width;
+    int minHeight = tabPane.getPreferredSize().height;
+    if (tabPane.getPreferredSize().width < minWidth)
+      tabPane.setPreferredSize(new Dimension(minWidth, minHeight));
+
+    // button pane
+    JButton okButton = new JButton("OK  \u21B5");
+    okButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        storeOptions();
+        endDialog();
+      }
+    });
+    JButton cancelButton = new JButton("Cancel");
+    cancelButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        endDialog();
+      }
+    });
+
+    JPanel buttonPane = new JPanel(new GridLayout(1, 5, 10, 10));
+    buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 5));
+    buttonPane.add(Box.createRigidArea(new Dimension()));
+    buttonPane.add(Box.createRigidArea(new Dimension()));
+    buttonPane.add(Box.createRigidArea(new Dimension()));
+    buttonPane.add(cancelButton);
+    buttonPane.add(okButton);
+
+    this.add(tabPane);
+    this.add(buttonPane);
+    addListeners(this);
+    pack();
+  }
+
+  public static void showDialog(Container c) {
+    OptionsDialog dialog = new OptionsDialog();
+    dialog.show(c);
+  }
+
+  public void show(Container c) {
+    loadOptions();
+    super.showDialog(c);
+  }
+
+  public static void addCallback(String cb, Object obj)
+  {
+    callbacks.put(cb, obj);
+  }
+
+  public static void removeCallback(String cb)
+  {
+    callbacks.remove(cb);
+  }
+
+  public void endDialog() {
+    super.endDialog();
+    // Making a new dialog is so cheap that it's not worth keeping
+    this.dispose();
+  }
+
+  public void setEmbeddedFeatures(boolean s) {
+    fullScreenCheckbox.setEnabled(s);
+    fullScreenAllMonitorsCheckbox.setEnabled(s);
+    scalingFactorInput.setEnabled(s);
+  }
+
+  private void loadOptions()
+  {
+    /* Compression */
+    autoselectCheckbox.setSelected(autoSelect.getValue());
+  
+    int encNum = Encodings.encodingNum(preferredEncoding.getValueStr());
+  
+    switch (encNum) {
+    case Encodings.encodingTight:
+      tightButton.setSelected(true);
+      break;
+    case Encodings.encodingZRLE:
+      zrleButton.setSelected(true);
+      break;
+    case Encodings.encodingHextile:
+      hextileButton.setSelected(true);
+      break;
+    case Encodings.encodingRaw:
+      rawButton.setSelected(true);
+      break;
+    }
+  
+    if (fullColor.getValue())
+      fullcolorButton.setSelected(true);
+    else {
+      switch (lowColorLevel.getValue()) {
+      case 0:
+        verylowcolorButton.setSelected(true);
+        break;
+      case 1:
+        lowcolorButton.setSelected(true);
+        break;
+      case 2:
+        mediumcolorButton.setSelected(true);
+        break;
+      }
+    }
+
+    int digit = 0;
+
+    compressionCheckbox.setSelected(customCompressLevel.getValue());
+    jpegCheckbox.setSelected(!noJpeg.getValue());
+    digit = 0 + compressLevel.getValue();
+    compressionInput.setSelectedItem(digit);
+    digit = 0 + qualityLevel.getValue();
+    jpegInput.setSelectedItem(digit);
+
+    handleAutoselect();
+    handleCompression();
+    handleJpeg();
+
+    /* Security */
+    Security security = new Security(SecurityClient.secTypes);
+
+    List<Integer> secTypes;
+    Iterator<Integer> iter;
+
+    List<Integer> secTypesExt;
+    Iterator<Integer> iterExt;
+
+    encNoneCheckbox.setSelected(false);
+    encTLSCheckbox.setSelected(false);
+    encX509Checkbox.setSelected(false);
+  
+    authNoneCheckbox.setSelected(false);
+    authVncCheckbox.setSelected(false);
+    authPlainCheckbox.setSelected(false);
+    authIdentCheckbox.setSelected(false);
+    sendLocalUsernameCheckbox.setSelected(sendLocalUsername.getValue());
+  
+    secTypes = security.GetEnabledSecTypes();
+    for (iter = secTypes.iterator(); iter.hasNext(); ) {
+      switch ((Integer)iter.next()) {
+      case Security.secTypeNone:
+        encNoneCheckbox.setSelected(true);
+        authNoneCheckbox.setSelected(true);
+        break;
+      case Security.secTypeVncAuth:
+        encNoneCheckbox.setSelected(true);
+        authVncCheckbox.setSelected(true);
+        break;
+      }
+    }
+  
+    secTypesExt = security.GetEnabledExtSecTypes();
+    for (iterExt = secTypesExt.iterator(); iterExt.hasNext(); ) {
+      switch ((Integer)iterExt.next()) {
+      case Security.secTypePlain:
+        encNoneCheckbox.setSelected(true);
+        authPlainCheckbox.setSelected(true);
+        break;
+      case Security.secTypeIdent:
+        encNoneCheckbox.setSelected(true);
+        authIdentCheckbox.setSelected(true);
+        break;
+      case Security.secTypeTLSNone:
+        encTLSCheckbox.setSelected(true);
+        authNoneCheckbox.setSelected(true);
+        break;
+      case Security.secTypeTLSVnc:
+        encTLSCheckbox.setSelected(true);
+        authVncCheckbox.setSelected(true);
+        break;
+      case Security.secTypeTLSPlain:
+        encTLSCheckbox.setSelected(true);
+        authPlainCheckbox.setSelected(true);
+        break;
+      case Security.secTypeTLSIdent:
+        encTLSCheckbox.setSelected(true);
+        authIdentCheckbox.setSelected(true);
+        break;
+      case Security.secTypeX509None:
+        encX509Checkbox.setSelected(true);
+        authNoneCheckbox.setSelected(true);
+        break;
+      case Security.secTypeX509Vnc:
+        encX509Checkbox.setSelected(true);
+        authVncCheckbox.setSelected(true);
+        break;
+      case Security.secTypeX509Plain:
+        encX509Checkbox.setSelected(true);
+        authPlainCheckbox.setSelected(true);
+        break;
+      case Security.secTypeX509Ident:
+        encX509Checkbox.setSelected(true);
+        authIdentCheckbox.setSelected(true);
+        break;
+      }
+    }
+
+    File caFile = new File(CSecurityTLS.X509CA.getValueStr());
+    if (caFile.exists() && caFile.canRead())
+      caInput.setText(caFile.getAbsolutePath());
+    File crlFile = new File(CSecurityTLS.X509CRL.getValueStr());
+    if (crlFile.exists() && crlFile.canRead())
+      crlInput.setText(crlFile.getAbsolutePath());
+
+    handleX509();
+    handleSendLocalUsername();
+
+    /* Input */
+    viewOnlyCheckbox.setSelected(viewOnly.getValue());
+    acceptClipboardCheckbox.setSelected(acceptClipboard.getValue());
+    sendClipboardCheckbox.setSelected(sendClipboard.getValue());
+  
+    menuKeyChoice.setSelectedIndex(0);
+  
+    String menuKeyStr = menuKey.getValueStr();
+    for (int i = 0; i < menuKeyChoice.getItemCount(); i++)
+      if (menuKeyStr.equals(menuKeyChoice.getItemAt(i)))
+        menuKeyChoice.setSelectedIndex(i);
+  
+    /* Screen */
+    String width, height;
+
+    if (desktopSize.getValueStr().isEmpty() ||
+        desktopSize.getValueStr().split("x").length != 2) {
+      desktopSizeCheckbox.setSelected(false);
+      desktopWidthInput.setText("1024");
+      desktopHeightInput.setText("768");
+    } else {
+      desktopSizeCheckbox.setSelected(true);
+      width = desktopSize.getValueStr().split("x")[0];
+      desktopWidthInput.setText(width);
+      height = desktopSize.getValueStr().split("x")[1];
+      desktopHeightInput.setText(height);
+    }
+    fullScreenCheckbox.setSelected(fullScreen.getValue());
+    fullScreenAllMonitorsCheckbox.setSelected(fullScreenAllMonitors.getValue());
+
+    scalingFactorInput.setSelectedItem("100%");
+    String scaleStr = scalingFactor.getValueStr();
+    if (scaleStr.matches("^[0-9]+$"))
+      scaleStr = scaleStr.concat("%");
+    for (int i = 0; i < scalingFactorInput.getItemCount(); i++)
+      if (scaleStr.equals(scalingFactorInput.getItemAt(i)))
+        scalingFactorInput.setSelectedIndex(i);
+
+    handleDesktopSize();
+  
+    /* Misc. */
+    sharedCheckbox.setSelected(shared.getValue());
+    localCursorCheckbox.setSelected(useLocalCursor.getValue());
+    acceptBellCheckbox.setSelected(acceptBell.getValue());
+
+    /* SSH */
+    File f;
+    tunnelCheckbox.setSelected(tunnel.getValue() || !via.getValueStr().isEmpty());
+    viaCheckbox.setSelected(!via.getValueStr().isEmpty());
+    if (viaCheckbox.isSelected()) {
+      viaUserInput.setText(Tunnel.getSshUser());
+      viaHostInput.setText(Tunnel.getSshHost());
+      viaPortInput.setText(Integer.toString(Tunnel.getSshPort()));
+    }
+    extSSHCheckbox.setSelected(extSSH.getValue());
+    f = new File(extSSHClient.getValueStr());
+    if (f.exists() && f.isFile() && f.canExecute())
+      sshClientInput.setText(f.getAbsolutePath());
+    if (extSSHArgs.getValueStr().isEmpty()) {
+      sshArgsDefaultButton.setSelected(true);
+    } else {
+      sshArgsCustomButton.setSelected(true);
+      sshArgsInput.setText(extSSHArgs.getValueStr());
+    }
+    f = new File(sshKeyFile.getValueStr());
+    if (f.exists() && f.isFile() && f.canRead())
+      sshKeyFileInput.setText(f.getAbsolutePath());
+    f = new File(sshConfig.getValueStr());
+    if (f.exists() && f.isFile() && f.canRead())
+      sshConfigInput.setText(f.getAbsolutePath());
+
+    handleTunnel();
+    handleVia();
+    handleExtSSH();
+    handleEmbed();
+    handleRfbState();
+  }
+
+  private void storeOptions() {
+    /* Compression */
+    autoSelect.setParam(autoselectCheckbox.isSelected());
+
+    if (tightButton.isSelected())
+      preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingTight));
+    else if (zrleButton.isSelected())
+      preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingZRLE));
+    else if (hextileButton.isSelected())
+      preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingHextile));
+    else if (rawButton.isSelected())
+      preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingRaw));
+
+    fullColor.setParam(fullcolorButton.isSelected());
+    if (verylowcolorButton.isSelected())
+      lowColorLevel.setParam(0);
+    else if (lowcolorButton.isSelected())
+      lowColorLevel.setParam(1);
+    else if (mediumcolorButton.isSelected())
+      lowColorLevel.setParam(2);
+
+    customCompressLevel.setParam(compressionCheckbox.isSelected());
+    noJpeg.setParam(!jpegCheckbox.isSelected());
+    compressLevel.setParam((Integer)compressionInput.getSelectedItem());
+    qualityLevel.setParam((Integer)jpegInput.getSelectedItem());
+
+    /* Security */
+    Security security = new Security();
+
+    /* Process security types which don't use encryption */
+    if (encNoneCheckbox.isSelected()) {
+      if (authNoneCheckbox.isSelected())
+        security.EnableSecType(Security.secTypeNone);
+      if (authVncCheckbox.isSelected())
+        security.EnableSecType(Security.secTypeVncAuth);
+      if (authPlainCheckbox.isSelected())
+        security.EnableSecType(Security.secTypePlain);
+      if (authIdentCheckbox.isSelected())
+        security.EnableSecType(Security.secTypeIdent);
+    }
+
+    /* Process security types which use TLS encryption */
+    if (encTLSCheckbox.isSelected()) {
+      if (authNoneCheckbox.isSelected())
+        security.EnableSecType(Security.secTypeTLSNone);
+      if (authVncCheckbox.isSelected())
+        security.EnableSecType(Security.secTypeTLSVnc);
+      if (authPlainCheckbox.isSelected())
+        security.EnableSecType(Security.secTypeTLSPlain);
+      if (authIdentCheckbox.isSelected())
+        security.EnableSecType(Security.secTypeTLSIdent);
+    }
+
+    /* Process security types which use X509 encryption */
+    if (encX509Checkbox.isSelected()) {
+      if (authNoneCheckbox.isSelected())
+        security.EnableSecType(Security.secTypeX509None);
+      if (authVncCheckbox.isSelected())
+        security.EnableSecType(Security.secTypeX509Vnc);
+      if (authPlainCheckbox.isSelected())
+        security.EnableSecType(Security.secTypeX509Plain);
+      if (authIdentCheckbox.isSelected())
+        security.EnableSecType(Security.secTypeX509Ident);
+    }
+
+    if (authIdentCheckbox.isSelected() ||
+        authPlainCheckbox.isSelected()) {
+      sendLocalUsername.setParam(sendLocalUsernameCheckbox.isSelected());
+    }
+
+    SecurityClient.secTypes.setParam(security.ToString());
+
+    File caFile = new File(caInput.getText());
+    if (caFile.exists() && caFile.canRead())
+      CSecurityTLS.X509CA.setParam(caFile.getAbsolutePath());
+    File crlFile = new File(crlInput.getText());
+    if (crlFile.exists() && crlFile.canRead())
+      CSecurityTLS.X509CRL.setParam(crlFile.getAbsolutePath());
+    /* Input */
+    viewOnly.setParam(viewOnlyCheckbox.isSelected());
+    acceptClipboard.setParam(acceptClipboardCheckbox.isSelected());
+    sendClipboard.setParam(sendClipboardCheckbox.isSelected());
+
+    String menuKeyStr =
+      MenuKey.getMenuKeySymbols()[menuKeyChoice.getSelectedIndex()].name;
+    menuKey.setParam(menuKeyStr);
 
-    // Compression tab
+    /* Screen */
+    if (desktopSizeCheckbox.isSelected() &&
+        !desktopWidthInput.getText().isEmpty() &&
+        !desktopHeightInput.getText().isEmpty()) {
+      String width = desktopWidthInput.getText();
+      String height = desktopHeightInput.getText();
+      desktopSize.setParam(width.concat("x").concat(height));
+    } else {
+      desktopSize.setParam("");
+    }
+    fullScreen.setParam(fullScreenCheckbox.isSelected());
+    fullScreenAllMonitors.setParam(fullScreenAllMonitorsCheckbox.isSelected());
+
+    String scaleStr =
+      ((String)scalingFactorInput.getSelectedItem()).replace("%", "");
+    if (scaleStr.equals("Fixed Aspect Ratio"))
+      scaleStr = "FixedRatio";
+    scalingFactor.setParam(scaleStr);
+
+    /* Misc. */
+    shared.setParam(sharedCheckbox.isSelected());
+    useLocalCursor.setParam(localCursorCheckbox.isSelected());
+    acceptBell.setParam(acceptBellCheckbox.isSelected());
+
+    /* SSH */
+    tunnel.setParam(tunnelCheckbox.isSelected());
+    if (viaCheckbox.isSelected() &&
+        !viaUserInput.getText().isEmpty() &&
+        !viaHostInput.getText().isEmpty() &&
+        !viaPortInput.getText().isEmpty()) {
+      String sshUser = viaUserInput.getText();
+      String sshHost = viaHostInput.getText();
+      String sshPort = viaPortInput.getText();
+      String viaStr = sshUser.concat("@").concat(sshHost).concat(":").concat(sshPort);
+      via.setParam(viaStr);
+    }
+    extSSH.setParam(extSSHCheckbox.isSelected());
+    if (!sshClientInput.getText().isEmpty())
+      extSSHClient.setParam(sshClientInput.getText());
+    if (sshArgsDefaultButton.isSelected())
+      if (!sshArgsInput.getText().isEmpty())
+        extSSHArgs.setParam(sshArgsInput.getText());
+    if (!sshConfigInput.getText().isEmpty())
+      sshConfig.setParam(sshConfigInput.getText());
+    if (!sshKeyFileInput.getText().isEmpty())
+      sshKeyFile.setParam(sshKeyFileInput.getText());
+
+    try {
+      for (Map.Entry<String, Object> iter : callbacks.entrySet()) {
+        Object obj = iter.getValue();
+        Method cb = obj.getClass().getMethod(iter.getKey(), new Class[]{});
+        cb.invoke(obj);
+      }
+    } catch (NoSuchMethodException e) {
+      vlog.error("NoSuchMethodException: "+e.getMessage());
+    } catch (IllegalAccessException e) {
+      vlog.error("IllegalAccessException: "+e.getMessage());
+    } catch (InvocationTargetException e) {
+      vlog.error("InvocationTargetException: "+e.getMessage());
+    }
+  }
+
+  private JPanel createCompressionPanel() {
     JPanel FormatPanel = new JPanel();
     FormatPanel.setLayout(new BoxLayout(FormatPanel,
                                         BoxLayout.PAGE_AXIS));
@@ -138,61 +637,72 @@ class OptionsDialog extends Dialog {
     autoSelectPane.setLayout(new BoxLayout(autoSelectPane,
                                            BoxLayout.LINE_AXIS));
     autoSelectPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
-    autoSelect = new JCheckBox("Auto Select");
-    autoSelectPane.add(autoSelect);
+    autoselectCheckbox = new JCheckBox("Auto Select");
+    autoselectCheckbox.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        handleAutoselect();
+      }
+    });
+    autoSelectPane.add(autoselectCheckbox);
     autoSelectPane.add(Box.createHorizontalGlue());
 
     JPanel encodingPanel = new JPanel(new GridLayout(4, 1));
-    encodingPanel.
-      setBorder(BorderFactory.createTitledBorder("Preferred encoding"));
-    tight = new GroupedJRadioButton("Tight",
-                                    encodingGroup, encodingPanel);
-    zrle = new GroupedJRadioButton("ZRLE",
-                                    encodingGroup, encodingPanel);
-    hextile = new GroupedJRadioButton("Hextile",
-                                      encodingGroup, encodingPanel);
-    raw = new GroupedJRadioButton("Raw", encodingGroup, encodingPanel);
-
-    JPanel colourPanel = new JPanel(new GridLayout(4, 1));
-    colourPanel.setBorder(BorderFactory.createTitledBorder("Color level"));
-    fullColour = new GroupedJRadioButton("Full (all available colors)",
-                                          colourGroup, colourPanel);
-    mediumColour = new GroupedJRadioButton("Medium (256 colors)",
-                                            colourGroup, colourPanel);
-    lowColour = new GroupedJRadioButton("Low (64 colours)",
-                                        colourGroup, colourPanel);
-    veryLowColour = new GroupedJRadioButton("Very low(8 colors)",
-                                            colourGroup, colourPanel);
+    encodingPanel.setBorder(BorderFactory.createTitledBorder("Preferred encoding"));
+    tightButton = new GroupedJRadioButton("Tight", encodingGroup, encodingPanel);
+    zrleButton = new GroupedJRadioButton("ZRLE", encodingGroup, encodingPanel);
+    hextileButton = new GroupedJRadioButton("Hextile", encodingGroup, encodingPanel);
+    rawButton = new GroupedJRadioButton("Raw", encodingGroup, encodingPanel);
+
+    JPanel colorPanel = new JPanel(new GridLayout(4, 1));
+    colorPanel.setBorder(BorderFactory.createTitledBorder("Color level"));
+    fullcolorButton = new GroupedJRadioButton("Full (all available colors)",
+                                              colorlevelGroup, colorPanel);
+    mediumcolorButton = new GroupedJRadioButton("Medium (256 colors)",
+                                                colorlevelGroup, colorPanel);
+    lowcolorButton = new GroupedJRadioButton("Low (64 colors)",
+                                             colorlevelGroup, colorPanel);
+    verylowcolorButton = new GroupedJRadioButton("Very low (8 colors)",
+                                                 colorlevelGroup, colorPanel);
 
     JPanel encodingPane = new JPanel(new GridLayout(1, 2, 5, 0));
     encodingPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
     encodingPane.add(encodingPanel);
-    encodingPane.add(colourPanel);
+    encodingPane.add(colorPanel);
 
     JPanel tightPanel = new JPanel(new GridBagLayout());
-    customCompressLevel = new JCheckBox("Custom Compression Level");
+    compressionCheckbox = new JCheckBox("Custom Compression Level");
+    compressionCheckbox.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        handleCompression();
+      }
+    });
     Object[] compressionLevels = { 1, 2, 3, 4, 5, 6 };
-    compressLevel  = new MyJComboBox(compressionLevels);
-    ((MyJComboBox)compressLevel).setDocument(new IntegerDocument(1));
-    compressLevel.setPrototypeDisplayValue("0.");
-    compressLevel.setEditable(true);
+    compressionInput = new MyJComboBox(compressionLevels);
+    ((MyJComboBox)compressionInput).setDocument(new IntegerDocument(1));
+    compressionInput.setPrototypeDisplayValue("0.");
+    compressionInput.setEditable(true);
     JLabel compressionLabel =
       new JLabel("Level (1=fast, 6=best [4-6 are rarely useful])");
-    noJpeg = new JCheckBox("Allow JPEG Compression");
+    jpegCheckbox = new JCheckBox("Allow JPEG Compression");
+    jpegCheckbox.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        handleJpeg();
+      }
+    });
     Object[] qualityLevels = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
-    qualityLevel  = new MyJComboBox(qualityLevels);
-    qualityLevel.setPrototypeDisplayValue("0.");
+    jpegInput = new MyJComboBox(qualityLevels);
+    jpegInput.setPrototypeDisplayValue("0.");
     JLabel qualityLabel = new JLabel("Quality (0=poor, 9=best)");
 
-    tightPanel.add(customCompressLevel,
+    tightPanel.add(compressionCheckbox,
                    new GridBagConstraints(0, 0,
                                           REMAINDER, 1,
                                           LIGHT, LIGHT,
                                           LINE_START, NONE,
                                           new Insets(0, 0, 0, 0),
                                           NONE, NONE));
-    indent = getButtonLabelInset(customCompressLevel);
-    tightPanel.add(compressLevel,
+    int indent = getButtonLabelInset(compressionCheckbox);
+    tightPanel.add(compressionInput,
                    new GridBagConstraints(0, 1,
                                           1, 1,
                                           LIGHT, LIGHT,
@@ -206,15 +716,15 @@ class OptionsDialog extends Dialog {
                                           LINE_START, HORIZONTAL,
                                           new Insets(0, 5, 0, 0),
                                           NONE, NONE));
-    tightPanel.add(noJpeg,
+    tightPanel.add(jpegCheckbox,
                    new GridBagConstraints(0, 2,
                                           REMAINDER, 1,
                                           LIGHT, LIGHT, 
                                           LINE_START, NONE,
                                           new Insets(5, 0, 0, 0),
                                           NONE, NONE));
-    indent = getButtonLabelInset(noJpeg);
-    tightPanel.add(qualityLevel,
+    indent = getButtonLabelInset(jpegCheckbox);
+    tightPanel.add(jpegInput,
                    new GridBagConstraints(0, 3,
                                           1, 1,
                                           LIGHT, LIGHT,
@@ -238,8 +748,10 @@ class OptionsDialog extends Dialog {
     FormatPanel.add(autoSelectPane);
     FormatPanel.add(encodingPane);
     FormatPanel.add(tightPanel);
+    return FormatPanel;
+  }
 
-    // security tab
+  private JPanel createSecurityPanel() {
     JPanel SecPanel = new JPanel();
     SecPanel.setLayout(new BoxLayout(SecPanel,
                                      BoxLayout.PAGE_AXIS));
@@ -249,46 +761,63 @@ class OptionsDialog extends Dialog {
     vencryptPane.setLayout(new BoxLayout(vencryptPane,
                                          BoxLayout.LINE_AXIS));
     vencryptPane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
-    secVeNCrypt = new JCheckBox("Extended encryption and "+
-                                "authentication methods (VeNCrypt)");
-    vencryptPane.add(secVeNCrypt);
-    vencryptPane.add(Box.createHorizontalGlue());
 
     JPanel encrPanel = new JPanel(new GridBagLayout());
     encrPanel.setBorder(BorderFactory.createTitledBorder("Encryption"));
-    encNone = new JCheckBox("None");
-    encTLS = new JCheckBox("Anonymous TLS");
-    encX509 = new JCheckBox("TLS with X.509 certificates");
+    encNoneCheckbox = new JCheckBox("None");
+    encTLSCheckbox = new JCheckBox("Anonymous TLS");
+    encX509Checkbox = new JCheckBox("TLS with X.509 certificates");
+    encX509Checkbox.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        handleX509();
+      }
+    });
     JLabel caLabel = new JLabel("X.509 CA Certificate");
-    x509ca = new JTextField();
-    x509ca.setName(Configuration.getParam("x509ca").getName());
-    caButton = new JButton("Browse");
+    caInput = new JTextField();
+    caChooser = new JButton("Browse");
+    caChooser.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        JComponent c = ((JButton)e.getSource()).getRootPane();
+        File dflt = new File(CSecurityTLS.X509CA.getValueStr());
+        File f = showChooser("Path to X509 CA certificate", dflt, c);
+        if (f != null && f.exists() && f.canRead())
+          caInput.setText(f.getAbsolutePath());
+      }
+    });
     JLabel crlLabel = new JLabel("X.509 CRL file");
-    x509crl = new JTextField();
-    x509crl.setName(Configuration.getParam("x509crl").getName());
-    crlButton = new JButton("Browse");
-    encrPanel.add(encNone,
+    crlInput = new JTextField();
+    crlChooser = new JButton("Browse");
+    crlChooser.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        JComponent c = ((JButton)e.getSource()).getRootPane();
+        File dflt = new File(CSecurityTLS.X509CRL.getValueStr());
+        File f = showChooser("Path to X509 CRL file", dflt, c);
+        if (f != null && f.exists() && f.canRead())
+          crlInput.setText(f.getAbsolutePath());
+      }
+    });
+    encrPanel.add(encNoneCheckbox,
                   new GridBagConstraints(0, 0,
                                          REMAINDER, 1,
                                          HEAVY, LIGHT,
                                          LINE_START, NONE,
                                          new Insets(0, 0, 4, 0),
                                          NONE, NONE));
-    encrPanel.add(encTLS,
+    encrPanel.add(encTLSCheckbox,
                   new GridBagConstraints(0, 1,
                                          REMAINDER, 1,
                                          HEAVY, LIGHT,
                                          LINE_START, NONE,
                                          new Insets(0, 0, 4, 0),
                                          NONE, NONE));
-    encrPanel.add(encX509,
+    encrPanel.add(encX509Checkbox,
                   new GridBagConstraints(0, 2,
                                          3, 1,
                                          HEAVY, LIGHT,
                                          LINE_START, NONE,
                                          new Insets(0, 0, 0, 0),
                                          NONE, NONE));
-    indent = getButtonLabelInset(encX509);
+    int indent = getButtonLabelInset(encX509Checkbox);
     encrPanel.add(caLabel,
                   new GridBagConstraints(0, 3,
                                          1, 1,
@@ -296,14 +825,14 @@ class OptionsDialog extends Dialog {
                                          LINE_END, NONE,
                                          new Insets(0, indent, 5, 0),
                                          0, 0));
-    encrPanel.add(x509ca,
+    encrPanel.add(caInput,
                   new GridBagConstraints(1, 3,
                                          1, 1,
                                          HEAVY, LIGHT,
                                          LINE_START, HORIZONTAL,
                                          new Insets(0, 5, 5, 0),
                                          0, 0));
-    encrPanel.add(caButton,
+    encrPanel.add(caChooser,
                   new GridBagConstraints(2, 3,
                                          1, 1,
                                          LIGHT, LIGHT,
@@ -317,14 +846,14 @@ class OptionsDialog extends Dialog {
                                          LINE_END, NONE,
                                          new Insets(0, indent, 0, 0),
                                          0, 0));
-    encrPanel.add(x509crl,
+    encrPanel.add(crlInput,
                   new GridBagConstraints(1, 4,
                                          1, 1,
                                          HEAVY, LIGHT,
                                          LINE_START, HORIZONTAL,
                                          new Insets(0, 5, 0, 0),
                                          0, 0));
-    encrPanel.add(crlButton,
+    encrPanel.add(crlChooser,
                   new GridBagConstraints(2, 4,
                                          1, 1,
                                          LIGHT, LIGHT,
@@ -335,40 +864,50 @@ class OptionsDialog extends Dialog {
     JPanel authPanel = new JPanel(new GridBagLayout());
     authPanel.setBorder(BorderFactory.createTitledBorder("Authentication"));
 
-    secNone = new JCheckBox("None");
-    secVnc = new JCheckBox("Standard VNC");
-    secPlain = new JCheckBox("Plaintext");
-    secIdent = new JCheckBox("Ident");
-    sendLocalUsername = new JCheckBox("Send Local Username");
-    authPanel.add(secNone,
+    authNoneCheckbox = new JCheckBox("None");
+    authVncCheckbox = new JCheckBox("Standard VNC");
+    authPlainCheckbox = new JCheckBox("Plaintext");
+    authPlainCheckbox.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        handleSendLocalUsername();
+      }
+    });
+    authIdentCheckbox = new JCheckBox("Ident");
+    authIdentCheckbox.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        handleSendLocalUsername();
+      }
+    });
+    sendLocalUsernameCheckbox = new JCheckBox("Send Local Username");
+    authPanel.add(authNoneCheckbox,
                   new GridBagConstraints(0, 0,
                                          REMAINDER, 1,
                                          LIGHT, LIGHT,
                                          LINE_START, NONE,
                                          new Insets(0, 0, 4, 0),
                                          NONE, NONE));
-    authPanel.add(secVnc,
+    authPanel.add(authVncCheckbox,
                   new GridBagConstraints(0, 1,
                                          REMAINDER, 1,
                                          LIGHT, LIGHT,
                                          LINE_START, NONE,
                                          new Insets(0, 0, 4, 0),
                                          NONE, NONE));
-    authPanel.add(secPlain,
+    authPanel.add(authPlainCheckbox,
                   new GridBagConstraints(0, 2,
                                          1, 1,
                                          LIGHT, LIGHT,
                                          LINE_START, NONE,
                                          new Insets(0, 0, 2, 0),
                                          NONE, NONE));
-    authPanel.add(secIdent,
+    authPanel.add(authIdentCheckbox,
                   new GridBagConstraints(0, 3,
                                          1, 1,
                                          LIGHT, LIGHT,
                                          LINE_START, NONE,
                                          new Insets(2, 0, 0, 0),
                                          NONE, NONE));
-    authPanel.add(sendLocalUsername,
+    authPanel.add(sendLocalUsernameCheckbox,
                   new GridBagConstraints(1, 2,
                                          1, 2,
                                          HEAVY, LIGHT,
@@ -404,35 +943,40 @@ class OptionsDialog extends Dialog {
                                         LINE_START, BOTH,
                                         new Insets(0, 0, 0, 0),
                                         NONE, NONE));
+    return SecPanel;
+  }
 
-    // Input tab
+  private JPanel createInputPanel() {
     JPanel inputPanel = new JPanel(new GridBagLayout());
     inputPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
 
-    viewOnly = new JCheckBox("View Only (ignore mouse & keyboard)");
-    acceptClipboard = new JCheckBox("Accept clipboard from server");
-    sendClipboard = new JCheckBox("Send clipboard to server");
-    JLabel menuKeyLabel = new JLabel("Menu Key");
+    viewOnlyCheckbox = new JCheckBox("View only (ignore mouse and keyboard)");
+    acceptClipboardCheckbox = new JCheckBox("Accept clipboard from server");
+    sendClipboardCheckbox = new JCheckBox("Send clipboard to server");
+    JLabel menuKeyLabel = new JLabel("Menu key");
     String[] menuKeys = new String[MenuKey.getMenuKeySymbolCount()];
+    //String[] menuKeys = new String[MenuKey.getMenuKeySymbolCount()+1];
+    //menuKeys[0] = "None";
     for (int i = 0; i < MenuKey.getMenuKeySymbolCount(); i++)
       menuKeys[i] = MenuKey.getKeyText(MenuKey.getMenuKeySymbols()[i]);
-    menuKey  = new JComboBox(menuKeys);
+      //menuKeys[i+1] = MenuKey.getKeyText(MenuKey.getMenuKeySymbols()[i]);
+    menuKeyChoice = new JComboBox(menuKeys);
 
-    inputPanel.add(viewOnly,
+    inputPanel.add(viewOnlyCheckbox,
                    new GridBagConstraints(0, 0,
                                           REMAINDER, 1,
                                           HEAVY, LIGHT,
                                           LINE_START, NONE,
                                           new Insets(0, 0, 4, 0),
                                           NONE, NONE));
-    inputPanel.add(acceptClipboard,
+    inputPanel.add(acceptClipboardCheckbox,
                    new GridBagConstraints(0, 1,
                                           REMAINDER, 1,
                                           HEAVY, LIGHT,
                                           LINE_START, NONE,
                                           new Insets(0, 0, 4, 0),
                                           NONE, NONE));
-    inputPanel.add(sendClipboard,
+    inputPanel.add(sendClipboardCheckbox,
                    new GridBagConstraints(0, 2,
                                           REMAINDER, 1,
                                           HEAVY, LIGHT,
@@ -446,7 +990,7 @@ class OptionsDialog extends Dialog {
                                           LINE_START, NONE,
                                           new Insets(0, 0, 0, 0),
                                           NONE, NONE));
-    inputPanel.add(menuKey,
+    inputPanel.add(menuKeyChoice,
                    new GridBagConstraints(1, 3,
                                           1, 1,
                                           HEAVY, LIGHT,
@@ -460,42 +1004,42 @@ class OptionsDialog extends Dialog {
                                           LINE_START, BOTH,
                                           new Insets(0, 0, 0, 0),
                                           NONE, NONE));
+    return inputPanel;
+  }
 
-    // Screen tab
+  private JPanel createScreenPanel() {
     JPanel ScreenPanel = new JPanel(new GridBagLayout());
     ScreenPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
-    desktopSize = new JCheckBox("Resize remote session on connect");
-    desktopSize.setEnabled(!cc.viewer.embed.getValue() &&
-                           (cc.viewer.desktopSize.getValue() != null));
-    desktopWidth = new IntegerTextField(5);
-    desktopWidth.setEnabled(desktopSize.isSelected());
-    desktopHeight = new IntegerTextField(5);
-    desktopHeight.setEnabled(desktopSize.isSelected());
+    desktopSizeCheckbox = new JCheckBox("Resize remote session on connect");
+    desktopSizeCheckbox.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        handleDesktopSize();
+      }
+    });
+    desktopWidthInput = new IntegerTextField(5);
+    desktopHeightInput = new IntegerTextField(5);
     JPanel desktopSizePanel =
       new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
-    desktopSizePanel.add(desktopWidth);
+    desktopSizePanel.add(desktopWidthInput);
     desktopSizePanel.add(new JLabel(" x "));
-    desktopSizePanel.add(desktopHeight);
-    fullScreen = new JCheckBox("Full-screen mode");
-    fullScreen.setEnabled(!cc.viewer.embed.getValue());
-    fullScreenAllMonitors =
+    desktopSizePanel.add(desktopHeightInput);
+    fullScreenCheckbox = new JCheckBox("Full-screen mode");
+    fullScreenAllMonitorsCheckbox =
       new JCheckBox("Enable full-screen mode over all monitors");
-    fullScreenAllMonitors.setEnabled(!cc.viewer.embed.getValue());
     JLabel scalingFactorLabel = new JLabel("Scaling Factor");
     Object[] scalingFactors = {
       "Auto", "Fixed Aspect Ratio", "50%", "75%", "95%", "100%", "105%",
       "125%", "150%", "175%", "200%", "250%", "300%", "350%", "400%" };
-    scalingFactor = new MyJComboBox(scalingFactors);
-    scalingFactor.setEditable(true);
-    scalingFactor.setEnabled(!cc.viewer.embed.getValue());
-    ScreenPanel.add(desktopSize,
+    scalingFactorInput = new MyJComboBox(scalingFactors);
+    scalingFactorInput.setEditable(true);
+    ScreenPanel.add(desktopSizeCheckbox,
                     new GridBagConstraints(0, 0,
                                            REMAINDER, 1,
                                            LIGHT, LIGHT,
                                            LINE_START, NONE,
                                            new Insets(0, 0, 0, 0),
                                            NONE, NONE));
-    indent = getButtonLabelInset(desktopSize);
+    int indent = getButtonLabelInset(desktopSizeCheckbox);
     ScreenPanel.add(desktopSizePanel,
                     new GridBagConstraints(0, 1,
                                            REMAINDER, 1,
@@ -503,15 +1047,15 @@ class OptionsDialog extends Dialog {
                                            LINE_START, NONE,
                                            new Insets(0, indent, 0, 0),
                                            NONE, NONE));
-    ScreenPanel.add(fullScreen,
+    ScreenPanel.add(fullScreenCheckbox,
                     new GridBagConstraints(0, 2,
                                            REMAINDER, 1,
                                            LIGHT, LIGHT,
                                            LINE_START, NONE,
                                            new Insets(0, 0, 4, 0),
                                            NONE, NONE));
-    indent = getButtonLabelInset(fullScreen);
-    ScreenPanel.add(fullScreenAllMonitors,
+    indent = getButtonLabelInset(fullScreenCheckbox);
+    ScreenPanel.add(fullScreenAllMonitorsCheckbox,
                     new GridBagConstraints(0, 3,
                                            REMAINDER, 1,
                                            LIGHT, LIGHT,
@@ -525,7 +1069,7 @@ class OptionsDialog extends Dialog {
                                            LINE_START, NONE,
                                            new Insets(0, 0, 4, 0),
                                            NONE, NONE));
-    ScreenPanel.add(scalingFactor,
+    ScreenPanel.add(scalingFactorInput,
                     new GridBagConstraints(1, 4,
                                            1, 1,
                                            HEAVY, LIGHT,
@@ -539,29 +1083,31 @@ class OptionsDialog extends Dialog {
                                            LINE_START, BOTH,
                                            new Insets(0, 0, 0, 0),
                                            NONE, NONE));
+    return ScreenPanel;
+  }
 
-    // Misc tab
+  private JPanel createMiscPanel() {
     JPanel MiscPanel = new JPanel(new GridBagLayout());
     MiscPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
-    shared =
-      new JCheckBox("Shared connection (do not disconnect other viewers)");
-    useLocalCursor = new JCheckBox("Render cursor locally");
-    acceptBell = new JCheckBox("Beep when requested by the server");
-    MiscPanel.add(shared,
+    sharedCheckbox =
+      new JCheckBox("Shared (don't disconnect other viewers)");
+    localCursorCheckbox = new JCheckBox("Render cursor locally");
+    acceptBellCheckbox = new JCheckBox("Beep when requested by the server");
+    MiscPanel.add(sharedCheckbox,
                   new GridBagConstraints(0, 0,
                                          1, 1,
                                          LIGHT, LIGHT,
                                          LINE_START, NONE,
                                          new Insets(0, 0, 4, 0),
                                          NONE, NONE));
-    MiscPanel.add(useLocalCursor,
+    MiscPanel.add(localCursorCheckbox,
                   new GridBagConstraints(0, 1,
                                          1, 1,
                                          LIGHT, LIGHT,
                                          LINE_START, NONE,
                                          new Insets(0, 0, 4, 0),
                                          NONE, NONE));
-    MiscPanel.add(acceptBell,
+    MiscPanel.add(acceptBellCheckbox,
                   new GridBagConstraints(0, 2,
                                          1, 1,
                                          LIGHT, LIGHT,
@@ -575,52 +1121,101 @@ class OptionsDialog extends Dialog {
                                          LINE_START, BOTH,
                                          new Insets(0, 0, 0, 0),
                                          NONE, NONE));
+    return MiscPanel;
+  }
 
-    // SSH tab
+  private JPanel createSshPanel() {
     JPanel sshPanel = new JPanel(new GridBagLayout());
     sshPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
-    sshTunnel = new JCheckBox("Tunnel VNC over SSH");
+    ButtonGroup sshArgsGroup = new ButtonGroup();
+    tunnelCheckbox = new JCheckBox("Tunnel VNC over SSH");
+    tunnelCheckbox.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        handleTunnel();
+      }
+    });
 
     JPanel tunnelPanel = new JPanel(new GridBagLayout());
 
-    sshUseGateway = new JCheckBox("Use SSH gateway");
+    viaCheckbox = new JCheckBox("Use SSH gateway");
+    viaCheckbox.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        handleVia();
+      }
+    });
     JLabel sshUserLabel = new JLabel("Username");
-    sshUser = new JTextField();
+    viaUserInput = new JTextField();
     JLabel sshUserAtLabel = new JLabel("@");
     JLabel sshHostLabel = new JLabel("Hostname (or IP address)");
-    sshHost = new JTextField("");
+    viaHostInput = new JTextField("");
     JLabel sshPortLabel = new JLabel("Port");
-    sshPort = new IntegerTextField(5);
+    viaPortInput = new IntegerTextField(5);
 
-    sshUseExt = new JCheckBox("Use external SSH client");
-    sshClient = new JTextField();
-    sshClient.setName(Configuration.getParam("extSSHClient").getName());
-    sshClientBrowser = new JButton("Browse");
+    extSSHCheckbox = new JCheckBox("Use external SSH client");
+    extSSHCheckbox.addItemListener(new ItemListener() {
+      public void itemStateChanged(ItemEvent e) {
+        handleExtSSH();
+      }
+    });
+    sshClientInput = new JTextField();
+    sshClientChooser = new JButton("Browse");
+    sshClientChooser.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        JComponent c = ((JButton)e.getSource()).getRootPane();
+        File dflt = new File(extSSHClient.getValueStr());
+        File f = showChooser("Path to external SSH client", dflt, c);
+        if (f != null && f.exists() && f.isFile() && f.canExecute())
+          sshClientInput.setText(f.getAbsolutePath());
+      }
+    });
     JLabel sshConfigLabel = new JLabel("SSH config file");
-    sshConfig = new JTextField();
-    sshConfig.setName(Configuration.getParam("sshConfig").getName());
-    sshConfigBrowser = new JButton("Browse");
+    sshConfigInput = new JTextField();
+    sshConfigChooser = new JButton("Browse");
+    sshConfigChooser.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        JComponent c = ((JButton)e.getSource()).getRootPane();
+        File dflt = new File(sshConfig.getValueStr());
+        File f = showChooser("Path to OpenSSH client config file", dflt, c);
+        if (f != null && f.exists() && f.isFile() && f.canRead())
+          sshConfigInput.setText(f.getAbsolutePath());
+      }
+    });
     JLabel sshKeyFileLabel = new JLabel("SSH identity file");
-    sshKeyFile = new JTextField();
-    sshKeyFile.setName(Configuration.getParam("sshKeyFile").getName());
-    sshKeyFileBrowser = new JButton("Browse");
+    sshKeyFileInput = new JTextField();
+    sshKeyFileChooser = new JButton("Browse");
+    sshKeyFileChooser.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        JComponent c = ((JButton)e.getSource()).getRootPane();
+        File f = showChooser("Path to SSH key file", null, c);
+        if (f != null && f.exists() && f.isFile() && f.canRead())
+          sshKeyFileInput.setText(f.getAbsolutePath());
+      }
+    });
     JPanel sshArgsPanel = new JPanel(new GridBagLayout());
     JLabel sshArgsLabel = new JLabel("Arguments:");
-    sshArgsDefault =
-      new GroupedJRadioButton("Default", sshArgsGroup, sshArgsPanel);
-    sshArgsCustom =
-      new GroupedJRadioButton("Custom", sshArgsGroup, sshArgsPanel);
-    sshArguments = new JTextField();
+    sshArgsDefaultButton = new GroupedJRadioButton("Default", sshArgsGroup, sshArgsPanel);
+    sshArgsDefaultButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        sshArgsInput.setEnabled(sshArgsCustomButton.isSelected());
+      }
+    });
+    sshArgsCustomButton = new GroupedJRadioButton("Custom", sshArgsGroup, sshArgsPanel);
+    sshArgsCustomButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        sshArgsInput.setEnabled(sshArgsCustomButton.isSelected());
+      }
+    });
+    sshArgsInput = new JTextField();
 
     JPanel gatewayPanel = new JPanel(new GridBagLayout());
-    gatewayPanel.add(sshUseGateway,
+    gatewayPanel.add(viaCheckbox,
                     new GridBagConstraints(0, 0,
                                            REMAINDER, 1,
                                            LIGHT, LIGHT,
                                            LINE_START, NONE,
                                            new Insets(0, 0, 4, 0),
                                            NONE, NONE));
-    indent = getButtonLabelInset(sshUseGateway);
+    int indent = getButtonLabelInset(viaCheckbox);
     gatewayPanel.add(sshUserLabel,
                  new GridBagConstraints(0, 1,
                                         1, 1,
@@ -642,7 +1237,7 @@ class OptionsDialog extends Dialog {
                                         LINE_START, HORIZONTAL,
                                         new Insets(0, 5, 4, 0),
                                         NONE, NONE));
-    gatewayPanel.add(sshUser,
+    gatewayPanel.add(viaUserInput,
                  new GridBagConstraints(0, 2,
                                         1, 1,
                                         LIGHT, LIGHT,
@@ -656,14 +1251,14 @@ class OptionsDialog extends Dialog {
                                         LINE_START, HORIZONTAL,
                                         new Insets(0, 2, 0, 2),
                                         NONE, NONE));
-    gatewayPanel.add(sshHost,
+    gatewayPanel.add(viaHostInput,
                  new GridBagConstraints(2, 2,
                                         1, 1,
                                         HEAVY, LIGHT,
                                         LINE_START, HORIZONTAL,
                                         new Insets(0, 0, 0, 0),
                                         NONE, NONE));
-    gatewayPanel.add(sshPort,
+    gatewayPanel.add(viaPortInput,
                  new GridBagConstraints(3, 2,
                                         1, 1,
                                         LIGHT, LIGHT,
@@ -672,21 +1267,21 @@ class OptionsDialog extends Dialog {
                                         NONE, NONE));
 
     JPanel clientPanel = new JPanel(new GridBagLayout());
-    clientPanel.add(sshUseExt,
+    clientPanel.add(extSSHCheckbox,
                     new GridBagConstraints(0, 0,
                                            1, 1,
                                            LIGHT, LIGHT,
                                            LINE_START, NONE,
                                            new Insets(0, 0, 0, 0),
                                            NONE, NONE));
-    clientPanel.add(sshClient,
+    clientPanel.add(sshClientInput,
                     new GridBagConstraints(1, 0,
                                            1, 1,
                                            HEAVY, LIGHT,
                                            LINE_START, HORIZONTAL,
                                            new Insets(0, 5, 0, 0),
                                            NONE, NONE));
-    clientPanel.add(sshClientBrowser,
+    clientPanel.add(sshClientChooser,
                     new GridBagConstraints(2, 0,
                                            1, 1,
                                            LIGHT, LIGHT,
@@ -700,28 +1295,28 @@ class OptionsDialog extends Dialog {
                                            LINE_START, NONE,
                                            new Insets(0, 0, 0, 0),
                                            NONE, NONE));
-    sshArgsPanel.add(sshArgsDefault,
+    sshArgsPanel.add(sshArgsDefaultButton,
                     new GridBagConstraints(1, 1,
                                            1, 1,
                                            LIGHT, LIGHT,
                                            LINE_START, NONE,
                                            new Insets(0, 5, 0, 0),
                                            NONE, NONE));
-    sshArgsPanel.add(sshArgsCustom,
+    sshArgsPanel.add(sshArgsCustomButton,
                     new GridBagConstraints(2, 1,
                                            1, 1,
                                            LIGHT, LIGHT,
                                            LINE_START, NONE,
                                            new Insets(0, 5, 0, 0),
                                            NONE, NONE));
-    sshArgsPanel.add(sshArguments,
+    sshArgsPanel.add(sshArgsInput,
                     new GridBagConstraints(3, 1,
                                            1, 1,
                                            HEAVY, LIGHT,
                                            LINE_START, HORIZONTAL,
                                            new Insets(0, 5, 0, 0),
                                            NONE, NONE));
-    indent = getButtonLabelInset(sshUseExt);
+    indent = getButtonLabelInset(extSSHCheckbox);
     clientPanel.add(sshArgsPanel,
                     new GridBagConstraints(0, 1,
                                            REMAINDER, 1,
@@ -731,7 +1326,9 @@ class OptionsDialog extends Dialog {
                                            NONE, NONE));
 
     JPanel opensshPanel = new JPanel(new GridBagLayout());
-    opensshPanel.setBorder(BorderFactory.createTitledBorder("Embedded SSH client configuration"));
+    TitledBorder border =
+      BorderFactory.createTitledBorder("Embedded SSH client configuration");
+    opensshPanel.setBorder(border);
     opensshPanel.add(sshConfigLabel,
                      new GridBagConstraints(0, 0,
                                             1, 1,
@@ -739,14 +1336,14 @@ class OptionsDialog extends Dialog {
                                             LINE_START, NONE,
                                             new Insets(0, 0, 5, 0),
                                             NONE, NONE));
-    opensshPanel.add(sshConfig,
+    opensshPanel.add(sshConfigInput,
                      new GridBagConstraints(1, 0,
                                             1, 1,
                                             HEAVY, LIGHT,
                                             LINE_START, HORIZONTAL,
                                             new Insets(0, 5, 5, 0),
                                             NONE, NONE));
-    opensshPanel.add(sshConfigBrowser,
+    opensshPanel.add(sshConfigChooser,
                      new GridBagConstraints(2, 0,
                                             1, 1,
                                             LIGHT, LIGHT,
@@ -760,14 +1357,14 @@ class OptionsDialog extends Dialog {
                                             LINE_START, NONE,
                                             new Insets(0, 0, 0, 0),
                                             NONE, NONE));
-    opensshPanel.add(sshKeyFile,
+    opensshPanel.add(sshKeyFileInput,
                      new GridBagConstraints(1, 1,
                                             1, 1,
                                             HEAVY, LIGHT,
                                             LINE_START, HORIZONTAL,
                                             new Insets(0, 5, 0, 0),
                                             NONE, NONE));
-    opensshPanel.add(sshKeyFileBrowser,
+    opensshPanel.add(sshKeyFileChooser,
                      new GridBagConstraints(2, 1,
                                             1, 1,
                                             LIGHT, LIGHT,
@@ -796,14 +1393,14 @@ class OptionsDialog extends Dialog {
                                            new Insets(0, 0, 0, 0),
                                            NONE, NONE));
 
-    sshPanel.add(sshTunnel,
+    sshPanel.add(tunnelCheckbox,
                  new GridBagConstraints(0, 0,
                                         REMAINDER, 1,
                                         LIGHT, LIGHT,
                                         LINE_START, NONE,
                                         new Insets(0, 0, 4, 0),
                                         NONE, NONE));
-    indent = getButtonLabelInset(sshTunnel);
+    indent = getButtonLabelInset(tunnelCheckbox);
     sshPanel.add(tunnelPanel,
                  new GridBagConstraints(0, 2,
                                         REMAINDER, 1,
@@ -818,601 +1415,142 @@ class OptionsDialog extends Dialog {
                                         LINE_START, BOTH,
                                         new Insets(0, 0, 0, 0),
                                         NONE, NONE));
+    return sshPanel;
+  }
 
-    // load/save tab
-    JPanel loadSavePanel = new JPanel(new GridBagLayout());
-    loadSavePanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
-    JPanel configPanel = new JPanel(new GridBagLayout());
-    configPanel.
-      setBorder(BorderFactory.createTitledBorder("Configuration File"));
-    cfLoadButton = new JButton("Load");
-    cfSaveAsButton = new JButton("Save As...");
-    configPanel.add(cfLoadButton,
-                    new GridBagConstraints(0, 0,
-                                           1, 1,
-                                           HEAVY, LIGHT,
-                                           CENTER, HORIZONTAL,
-                                           new Insets(0, 0, 5, 0),
-                                           NONE, NONE));
-    configPanel.add(cfSaveAsButton,
-                    new GridBagConstraints(0, 1,
-                                           1, 1,
-                                           HEAVY, HEAVY,
-                                           CENTER, HORIZONTAL,
-                                           new Insets(0, 0, 0, 0),
-                                           NONE, NONE));
+  private void handleAutoselect()
+  {
+    ButtonGroup[] groups = { encodingGroup, colorlevelGroup };
+    for (ButtonGroup grp : groups) {
+      Enumeration<AbstractButton> elems = grp.getElements();
+      while (elems.hasMoreElements())
+        elems.nextElement().setEnabled(!autoselectCheckbox.isSelected());
+    }
 
-    JPanel defaultsPanel = new JPanel(new GridBagLayout());
-    defaultsPanel.setBorder(BorderFactory.createTitledBorder("Defaults"));
-    defClearButton = new JButton("Clear");
-    defReloadButton = new JButton("Reload");
-    defSaveButton = new JButton("Save");
-    defaultsPanel.add(defClearButton,
-                      new GridBagConstraints(0, 0,
-                                             1, 1,
-                                             HEAVY, LIGHT,
-                                             CENTER, HORIZONTAL,
-                                             new Insets(0, 0, 5, 0),
-                                             NONE, NONE));
-    defaultsPanel.add(defReloadButton,
-                      new GridBagConstraints(0, 1,
-                                             1, 1,
-                                             HEAVY, LIGHT,
-                                             CENTER, HORIZONTAL,
-                                             new Insets(0, 0, 5, 0),
-                                             NONE, NONE));
-    defaultsPanel.add(defSaveButton,
-                      new GridBagConstraints(0, 2,
-                                             1, 1,
-                                             HEAVY, HEAVY,
-                                             CENTER, HORIZONTAL,
-                                             new Insets(0, 0, 0, 0),
-                                             NONE, NONE));
-
-    loadSavePanel.add(configPanel,
-                      new GridBagConstraints(0, 0,
-                                             1, 1,
-                                             HEAVY, LIGHT,
-                                             PAGE_START, HORIZONTAL,
-                                             new Insets(0, 0, 0, 0),
-                                             NONE, NONE));
-    loadSavePanel.add(Box.createRigidArea(new Dimension(5, 0)),
-                      new GridBagConstraints(1, 1,
-                                             1, 1,
-                                             LIGHT, LIGHT,
-                                             LINE_START, NONE,
-                                             new Insets(0, 0, 0, 0),
-                                             NONE, NONE));
-    loadSavePanel.add(defaultsPanel,
-                      new GridBagConstraints(2, 0,
-                                             1, 1,
-                                             HEAVY, LIGHT,
-                                             PAGE_START, HORIZONTAL,
-                                             new Insets(0, 0, 0, 0),
-                                             NONE, NONE));
-    loadSavePanel.add(Box.createRigidArea(new Dimension(5, 0)),
-                      new GridBagConstraints(0, 1,
-                                             REMAINDER, REMAINDER,
-                                             HEAVY, HEAVY,
-                                             LINE_START, BOTH,
-                                             new Insets(0, 0, 0, 0),
-                                             NONE, NONE));
+    // JPEG setting is also affected by autoselection
+    jpegCheckbox.setEnabled(!autoselectCheckbox.isSelected());
+    handleJpeg();
+  }
 
-    // tabPane
-    tabPane.addTab("Compression", FormatPanel);
-    tabPane.addTab("Security", SecPanel);
-    tabPane.addTab("Input", inputPanel);
-    tabPane.addTab("Screen", ScreenPanel);
-    tabPane.addTab("Misc", MiscPanel);
-    tabPane.addTab("SSH", sshPanel);
-    tabPane.addTab("Load / Save", loadSavePanel);
-    tabPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
-    // Resize the tabPane if necessary to prevent scrolling
-    Insets tpi =
-      (Insets)UIManager.get("TabbedPane:TabbedPaneTabArea.contentMargins");
-    int minWidth = tpi.left + tpi.right;
-    for (int i = 0; i < tabPane.getTabCount(); i++)
-      minWidth += tabPane.getBoundsAt(i).width;
-    int minHeight = tabPane.getPreferredSize().height;
-    if (tabPane.getPreferredSize().width < minWidth)
-      tabPane.setPreferredSize(new Dimension(minWidth, minHeight));
+  private void handleCompression()
+  {
+    compressionInput.setEnabled(compressionCheckbox.isSelected());
+  }
 
-    // button pane
-    okButton = new JButton("OK");
-    cancelButton = new JButton("Cancel");
+  private void handleJpeg()
+  {
+    if (jpegCheckbox.isSelected() &&
+        !autoselectCheckbox.isSelected())
+      jpegInput.setEnabled(true);
+    else
+      jpegInput.setEnabled(false);
+  }
 
-    JPanel buttonPane = new JPanel(new GridLayout(1, 5, 10, 10));
-    buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 5));
-    buttonPane.add(Box.createRigidArea(new Dimension()));
-    buttonPane.add(Box.createRigidArea(new Dimension()));
-    buttonPane.add(Box.createRigidArea(new Dimension()));
-    buttonPane.add(okButton);
-    buttonPane.add(cancelButton);
+  private void handleX509()
+  {
+    caInput.setEnabled(encX509Checkbox.isSelected());
+    caChooser.setEnabled(encX509Checkbox.isSelected());
+    crlInput.setEnabled(encX509Checkbox.isSelected());
+    crlChooser.setEnabled(encX509Checkbox.isSelected());
+  }
 
-    this.add(tabPane);
-    this.add(buttonPane);
-    addListeners(this);
-    pack();
+  private void handleSendLocalUsername()
+  {
+    boolean value = authIdentCheckbox.isSelected() ||
+                    authPlainCheckbox.isSelected();
+        sendLocalUsernameCheckbox.setEnabled(value);
   }
 
-  public void initDialog() {
-    if (cc != null) cc.setOptions();
-    zrle.setEnabled(!autoSelect.isSelected());
-    hextile.setEnabled(!autoSelect.isSelected());
-    tight.setEnabled(!autoSelect.isSelected());
-    raw.setEnabled(!autoSelect.isSelected());
-    fullColour.setEnabled(!autoSelect.isSelected());
-    mediumColour.setEnabled(!autoSelect.isSelected());
-    lowColour.setEnabled(!autoSelect.isSelected());
-    veryLowColour.setEnabled(!autoSelect.isSelected());
-    compressLevel.setEnabled(customCompressLevel.isSelected());
-    qualityLevel.setEnabled(noJpeg.isSelected());
-    sendLocalUsername.setEnabled(secVeNCrypt.isEnabled() &&
-      (secPlain.isSelected() || secIdent.isSelected()));
-    sshArguments.setEnabled(sshTunnel.isSelected() &&
-      (sshUseExt.isSelected() && sshArgsCustom.isSelected()));
+  private void handleDesktopSize()
+  {
+    desktopWidthInput.setEnabled(desktopSizeCheckbox.isSelected());
+    desktopHeightInput.setEnabled(desktopSizeCheckbox.isSelected());
   }
 
-  private void updatePreferences() {
-    if (autoSelect.isSelected()) {
-      UserPreferences.set("global", "AutoSelect", true);
-    } else {
-      UserPreferences.set("global", "AutoSelect", false);
-      if (zrle.isSelected()) {
-        UserPreferences.set("global", "PreferredEncoding", "ZRLE");
-      } else if (hextile.isSelected()) {
-        UserPreferences.set("global", "PreferredEncoding", "hextile");
-      } else if (tight.isSelected()) {
-        UserPreferences.set("global", "PreferredEncoding", "Tight");
-      } else if (raw.isSelected()) {
-        UserPreferences.set("global", "PreferredEncoding", "raw");
-      }
-    }
-    if (fullColour.isSelected()) {
-      UserPreferences.set("global", "FullColour", true);
-    } else {
-      UserPreferences.set("global", "FullColour", false);
-      if (mediumColour.isSelected()) {
-        UserPreferences.set("global", "LowColorLevel", 2);
-      } else if (lowColour.isSelected()) {
-        UserPreferences.set("global", "LowColorLevel", 1);
-      } else if (veryLowColour.isSelected()) {
-        UserPreferences.set("global", "LowColorLevel", 0);
-      }
-    }
-    UserPreferences.set("global", "NoJPEG", !noJpeg.isSelected());
-    UserPreferences.set("global",
-            "QualityLevel", (Integer)qualityLevel.getSelectedItem());
-    UserPreferences.set("global",
-            "CustomCompressLevel", customCompressLevel.isSelected());
-    UserPreferences.set("global",
-            "CompressLevel", (Integer)compressLevel.getSelectedItem());
-    UserPreferences.set("global", "ViewOnly", viewOnly.isSelected());
-    UserPreferences.set("global",
-            "AcceptClipboard", acceptClipboard.isSelected());
-    UserPreferences.set("global", "SendClipboard", sendClipboard.isSelected());
-    String menuKeyStr =
-      MenuKey.getMenuKeySymbols()[menuKey.getSelectedIndex()].name;
-    UserPreferences.set("global", "MenuKey", menuKeyStr);
-    String desktopSizeString =
-      desktopSize.isSelected() ?
-        desktopWidth.getText() + "x" + desktopHeight.getText() : "";
-    UserPreferences.set("global", "DesktopSize", desktopSizeString);
-    UserPreferences.set("global", "FullScreen", fullScreen.isSelected());
-    UserPreferences.set("global",
-            "FullScreenAllMonitors", fullScreenAllMonitors.isSelected());
-    UserPreferences.set("global", "Shared", shared.isSelected());
-    UserPreferences.set("global",
-            "UseLocalCursor", useLocalCursor.isSelected());
-    UserPreferences.set("global", "AcceptBell", acceptBell.isSelected());
-    String scaleString = scalingFactor.getSelectedItem().toString();
-    if (scaleString.equalsIgnoreCase("Auto")) {
-      UserPreferences.set("global", "ScalingFactor", "Auto");
-    } else if(scaleString.equalsIgnoreCase("Fixed Aspect Ratio")) {
-      UserPreferences.set("global", "ScalingFactor", "FixedRatio");
+  private void handleTunnel()
+  {
+    viaCheckbox.setEnabled(tunnelCheckbox.isSelected());
+    extSSHCheckbox.setEnabled(tunnelCheckbox.isSelected());
+    if (tunnelCheckbox.isSelected()) {
+      JComponent[] components = { viaUserInput, viaHostInput, viaPortInput };
+      for (JComponent c : components)
+        c.setEnabled(viaCheckbox.isSelected());
+      sshClientInput.setEnabled(extSSHCheckbox.isSelected());
+      sshClientChooser.setEnabled(extSSHCheckbox.isSelected());
+      sshArgsDefaultButton.setEnabled(extSSHCheckbox.isSelected());
+      sshArgsCustomButton.setEnabled(extSSHCheckbox.isSelected());
+      sshArgsInput.setEnabled(extSSHCheckbox.isSelected());
+      sshConfigInput.setEnabled(!extSSHCheckbox.isSelected());
+      sshConfigChooser.setEnabled(!extSSHCheckbox.isSelected());
+      sshKeyFileInput.setEnabled(!extSSHCheckbox.isSelected());
+      sshKeyFileChooser.setEnabled(!extSSHCheckbox.isSelected());
     } else {
-      scaleString=scaleString.substring(0, scaleString.length()-1);
-      UserPreferences.set("global", "ScalingFactor", scaleString);
-    }
-    UserPreferences.set("viewer", "secVeNCrypt", secVeNCrypt.isSelected());
-    UserPreferences.set("viewer", "encNone", encNone.isSelected());
-    UserPreferences.set("viewer", "encTLS", encTLS.isSelected());
-    UserPreferences.set("viewer", "encX509", encX509.isSelected());
-    UserPreferences.set("viewer", "secNone", secNone.isSelected());
-    UserPreferences.set("viewer", "secVnc", secVnc.isSelected());
-    UserPreferences.set("viewer", "secPlain", secPlain.isSelected());
-    UserPreferences.set("viewer", "secIdent", secIdent.isSelected());
-    UserPreferences.set("global",
-            "SendLocalUsername", sendLocalUsername.isSelected());
-    if (!CSecurityTLS.x509ca.getValueStr().equals(""))
-      UserPreferences.set("viewer", "x509ca",
-              CSecurityTLS.x509ca.getValueStr());
-    if (!CSecurityTLS.x509crl.getValueStr().equals(""))
-      UserPreferences.set("viewer", "x509crl",
-              CSecurityTLS.x509crl.getValueStr());
-    UserPreferences.set("global", "Tunnel", sshTunnel.isSelected());
-    if (sshUseGateway.isSelected()) {
-      String via = sshUser.getText()+"@"+sshHost.getText()+":"+sshPort.getText();
-      UserPreferences.set("global", "Via", via);
+      JComponent[] components = {
+        viaUserInput, viaHostInput, viaPortInput, sshClientInput,
+        sshClientChooser, sshArgsDefaultButton, sshArgsCustomButton,
+        sshArgsInput, sshConfigInput, sshConfigChooser, sshKeyFileInput,
+        sshKeyFileChooser, };
+      for (JComponent c : components)
+        c.setEnabled(false);
     }
-    if (sshUseExt.isSelected()) {
-      UserPreferences.set("global", "extSSH", sshUseExt.isSelected());
-      UserPreferences.set("global", "extSSHClient", sshClient.getText());
-      if (!sshArguments.getText().isEmpty())
-        UserPreferences.set("global", "extSSHArgs", sshArguments.getText());
-    }
-    UserPreferences.set("global", "SSHConfig", sshConfig.getText());
-    UserPreferences.set("global", "SSHKeyFile", sshKeyFile.getText());
   }
 
-  private void restorePreferences() {
-    autoSelect.setSelected(UserPreferences.getBool("global", "AutoSelect"));
-    if (!autoSelect.isSelected()) {
-      if (UserPreferences.getBool("global", "FullColour")) {
-        fullColour.setSelected(true);
-      } else {
-        switch (UserPreferences.getInt("global", "LowColorLevel")) {
-        case 2:
-          mediumColour.setSelected(true);
-          break;
-        case 1:
-          lowColour.setSelected(true);
-          break;
-        case 0:
-          veryLowColour.setSelected(true);
-          break;
-        }
-      }
-      String encoding = UserPreferences.get("global", "PreferredEncoding");
-      if (encoding != null) {
-        switch (Encodings.encodingNum(encoding)) {
-        case Encodings.encodingZRLE:
-          zrle.setSelected(true);
-          break;
-        case Encodings.encodingHextile:
-          hextile.setSelected(true);
-          break;
-        case Encodings.encodingRaw:
-          raw.setSelected(true);
-          break;
-        default:
-          tight.setSelected(true);
-        }
-      }
+  private void handleVia()
+  {
+    if (tunnelCheckbox.isSelected()) {
+      viaUserInput.setEnabled(viaCheckbox.isSelected());
+      viaHostInput.setEnabled(viaCheckbox.isSelected());
+      viaPortInput.setEnabled(viaCheckbox.isSelected());
     }
-    noJpeg.setSelected(!UserPreferences.getBool("global", "NoJPEG"));
-    qualityLevel.setSelectedItem(UserPreferences.getInt("global",
-            "QualityLevel"));
-    customCompressLevel.setSelected(UserPreferences.getBool("global",
-            "CustomCompressLevel"));
-    compressLevel.setSelectedItem(UserPreferences.getInt("global",
-            "CompressLevel"));
-    viewOnly.setSelected(UserPreferences.getBool("global", "ViewOnly"));
-    acceptClipboard.setSelected(UserPreferences.getBool("global",
-            "AcceptClipboard"));
-    sendClipboard.setSelected(UserPreferences.getBool("global",
-            "SendClipboard"));
-    menuKey.setSelectedItem(UserPreferences.get("global", "MenuKey"));
-    desktopSize.setSelected(!UserPreferences.get("global", "DesktopSize").isEmpty());
-    if (desktopSize.isSelected()) {
-      String desktopSizeString = UserPreferences.get("global", "DesktopSize");
-      desktopWidth.setText(desktopSizeString.split("x")[0]);
-      desktopHeight.setText(desktopSizeString.split("x")[1]);
-    }
-    fullScreen.setSelected(UserPreferences.getBool("global", "FullScreen"));
-    fullScreenAllMonitors.setSelected(UserPreferences.getBool("global",
-            "FullScreenAllMonitors"));
-    if (shared.isEnabled())
-      shared.setSelected(UserPreferences.getBool("global", "Shared"));
-    useLocalCursor.setSelected(UserPreferences.getBool("global",
-            "UseLocalCursor"));
-    acceptBell.setSelected(UserPreferences.getBool("global", "AcceptBell"));
-    String scaleString = UserPreferences.get("global", "ScalingFactor");
-    if (scaleString != null) {
-      if (scaleString.equalsIgnoreCase("Auto")) {
-        scalingFactor.setSelectedItem("Auto");
-      } else if (scaleString.equalsIgnoreCase("FixedRatio")) {
-        scalingFactor.setSelectedItem("Fixed Aspect Ratio");
-      } else {
-        scalingFactor.setSelectedItem(scaleString+"%");
-      }
-    }
-    if (secVeNCrypt.isEnabled()) {
-      secVeNCrypt.setSelected(UserPreferences.getBool("viewer",
-              "secVeNCrypt", true));
-      if (secVeNCrypt.isSelected()) {
-        encNone.setSelected(UserPreferences.getBool("viewer", "encNone", true));
-        encTLS.setSelected(UserPreferences.getBool("viewer", "encTLS", true));
-        encX509.setSelected(UserPreferences.getBool("viewer", "encX509", true));
-        secPlain.setSelected(UserPreferences.getBool("viewer", "secPlain", true));
-        secIdent.setSelected(UserPreferences.getBool("viewer", "secIdent", true));
-        sendLocalUsername.setSelected(UserPreferences.getBool("global",
-                "SendLocalUsername"));
-      }
-    }
-    if (secNone.isEnabled())
-      secNone.setSelected(UserPreferences.getBool("viewer", "secNone", true));
-    if (secVnc.isEnabled())
-      secVnc.setSelected(UserPreferences.getBool("viewer", "secVnc", true));
-    sshTunnel.setSelected(UserPreferences.getBool("global", "Tunnel"));
-    sshUseGateway.setSelected(UserPreferences.get("global", "Via") != null);
-    if (sshUseGateway.isSelected())
-      cc.viewer.via.setParam(UserPreferences.get("global", "Via"));
-    sshUser.setText(Tunnel.getSshUser(cc));
-    sshHost.setText(Tunnel.getSshHost(cc));
-    sshPort.setText(Integer.toString(Tunnel.getSshPort(cc)));
-    sshUseExt.setSelected(UserPreferences.getBool("global", "extSSH"));
-    File f = new File(UserPreferences.get("global", "extSSHClient"));
-    if (f.exists() && f.canExecute())
-      sshClient.setText(f.getAbsolutePath());
-    sshArguments.setText(UserPreferences.get("global", "extSSHArgs"));
-    if (sshArguments.getText().isEmpty())
-      sshArgsDefault.setSelected(true);
-    else
-      sshArgsCustom.setSelected(true);
-    f = new File(UserPreferences.get("global", "SSHConfig"));
-    if (f.exists() && f.canRead())
-      sshConfig.setText(f.getAbsolutePath());
-    if (UserPreferences.get("global", "SSHKeyFile") != null) {
-      f = new File(UserPreferences.get("global", "SSHKeyFile"));
-      if (f.exists() && f.canRead())
-        sshKeyFile.setText(f.getAbsolutePath());
-    } else {
-      sshKeyFile.setText(Tunnel.getSshKeyFile(cc));
-    }
-    sshUseGateway.setEnabled(sshTunnel.isSelected());
-    sshUser.setEnabled(sshTunnel.isSelected() &&
-                       sshUseGateway.isEnabled() &&
-                       sshUseGateway.isSelected());
-    sshHost.setEnabled(sshTunnel.isSelected() &&
-                       sshUseGateway.isEnabled() &&
-                       sshUseGateway.isSelected());
-    sshPort.setEnabled(sshTunnel.isSelected() &&
-                       sshUseGateway.isEnabled() &&
-                       sshUseGateway.isSelected());
-    sshUseExt.setEnabled(sshTunnel.isSelected());
-    sshClient.setEnabled(sshTunnel.isSelected() &&
-                         sshUseExt.isEnabled());
-    sshClientBrowser.setEnabled(sshTunnel.isSelected() &&
-                                sshUseExt.isEnabled() &&
-                                sshUseExt.isSelected());
-    sshArgsDefault.setEnabled(sshTunnel.isSelected() &&
-                              sshUseExt.isEnabled() &&
-                              sshUseExt.isSelected());
-    sshArgsCustom.setEnabled(sshTunnel.isSelected() &&
-                             sshUseExt.isEnabled() &&
-                             sshUseExt.isSelected());
-    sshArguments.setEnabled(sshTunnel.isSelected() &&
-                            sshUseExt.isEnabled() &&
-                            sshUseExt.isSelected() &&
-                            sshArgsCustom.isSelected());
-    sshConfig.setEnabled(sshTunnel.isSelected() &&
-                         sshUseExt.isEnabled() &&
-                         !sshUseExt.isSelected());
-    sshConfigBrowser.setEnabled(sshTunnel.isSelected() &&
-                                sshUseExt.isEnabled() &&
-                                !sshUseExt.isSelected());
-    sshKeyFile.setEnabled(sshTunnel.isSelected() &&
-                          sshUseExt.isEnabled() &&
-                          !sshUseExt.isSelected());
-    sshKeyFileBrowser.setEnabled(sshTunnel.isSelected() &&
-                                 sshUseExt.isEnabled() &&
-                                 !sshUseExt.isSelected());
   }
 
-  public void endDialog() {
-    super.endDialog();
-    if (cc.viewport != null && cc.viewport.isVisible()) {
-      cc.viewport.toFront();
-      cc.viewport.requestFocus();
+  private void handleExtSSH()
+  {
+    if (tunnelCheckbox.isSelected()) {
+      sshClientInput.setEnabled(extSSHCheckbox.isSelected());
+      sshClientChooser.setEnabled(extSSHCheckbox.isSelected());
+      sshArgsDefaultButton.setEnabled(extSSHCheckbox.isSelected());
+      sshArgsCustomButton.setEnabled(extSSHCheckbox.isSelected());
+      sshConfigInput.setEnabled(!extSSHCheckbox.isSelected());
+      sshConfigChooser.setEnabled(!extSSHCheckbox.isSelected());
+      sshKeyFileInput.setEnabled(!extSSHCheckbox.isSelected());
+      sshKeyFileChooser.setEnabled(!extSSHCheckbox.isSelected());
+      if (sshArgsCustomButton.isSelected())
+        sshArgsInput.setEnabled(extSSHCheckbox.isSelected());
+      else
+        sshArgsInput.setEnabled(false);
     }
   }
 
-  public void actionPerformed(ActionEvent e) {
-    Object s = e.getSource();
-    if (s instanceof JButton) {
-      JButton button = (JButton)s;
-      if (button == okButton) {
-        JTextField[] fields =
-          { x509ca, x509crl, sshClient, sshConfig, sshKeyFile };
-        for (JTextField field : fields) {
-          if (field.getText() != null && !field.getText().equals("")) {
-            File f = new File(field.getText());
-            if (!f.exists() || !f.canRead()) {
-              String msg = new String("The file "+f.getAbsolutePath()+
-                           " specified for option "+field.getName()+
-                           " does not exist or cannot be read.  Please"+
-                           " correct before proceeding.");
-              JOptionPane.showMessageDialog(this, msg, "WARNING",
-                                            JOptionPane.WARNING_MESSAGE);
-              return;
-            }
-          }
-        }
-        if (cc != null) cc.getOptions();
-        endDialog();
-      } else if (button == cancelButton) {
-        endDialog();
-      } else if (button == cfLoadButton) {
-        JFileChooser fc = new JFileChooser();
-        fc.setDialogTitle("Path to configuration file");
-        fc.setApproveButtonText("OK");
-        fc.setFileHidingEnabled(false);
-        int ret = fc.showOpenDialog(this);
-        if (ret == JFileChooser.APPROVE_OPTION) {
-          String filename = fc.getSelectedFile().toString();
-          if (filename != null)
-            Configuration.load(filename);
-          cc.setOptions();
-        }
-      } else if (button == cfSaveAsButton) {
-        JFileChooser fc = new JFileChooser();
-        fc.setDialogTitle("Save current configuration as:");
-        fc.setApproveButtonText("OK");
-        fc.setFileHidingEnabled(false);
-        int ret = fc.showOpenDialog(this);
-        if (ret == JFileChooser.APPROVE_OPTION) {
-          String filename = fc.getSelectedFile().toString();
-          if (filename != null)
-            Configuration.save(filename);
-        }
-      } else if (button == defSaveButton) {
-        updatePreferences();
-        UserPreferences.save();
-      } else if (button == defReloadButton) {
-        restorePreferences();
-      } else if (button == defClearButton) {
-        UserPreferences.clear();
-        cc.setOptions();
-      } else if (button == caButton) {
-        JFileChooser fc =
-          new JFileChooser(new File(CSecurityTLS.getDefaultCA()));
-        fc.setDialogTitle("Path to X509 CA certificate");
-        fc.setApproveButtonText("OK");
-        fc.setFileHidingEnabled(false);
-        int ret = fc.showOpenDialog(this);
-        if (ret == JFileChooser.APPROVE_OPTION)
-          x509ca.setText(fc.getSelectedFile().toString());
-      } else if (button == crlButton) {
-        JFileChooser fc =
-          new JFileChooser(new File(CSecurityTLS.getDefaultCRL()));
-        fc.setDialogTitle("Path to X509 CRL file");
-        fc.setApproveButtonText("OK");
-        fc.setFileHidingEnabled(false);
-        int ret = fc.showOpenDialog(this);
-        if (ret == JFileChooser.APPROVE_OPTION)
-          x509crl.setText(fc.getSelectedFile().toString());
-      } else if (button == sshClientBrowser) {
-        JFileChooser fc = new JFileChooser();
-        fc.setDialogTitle("Path to external SSH client");
-        fc.setApproveButtonText("OK");
-        fc.setFileHidingEnabled(false);
-        int ret = fc.showOpenDialog(this);
-        if (ret == JFileChooser.APPROVE_OPTION)
-          sshClient.setText(fc.getSelectedFile().toString());
-      } else if (button == sshConfigBrowser) {
-        JFileChooser fc = new JFileChooser();
-        fc.setDialogTitle("Path to OpenSSH client config file");
-        fc.setApproveButtonText("OK");
-        fc.setFileHidingEnabled(false);
-        int ret = fc.showOpenDialog(this);
-        if (ret == JFileChooser.APPROVE_OPTION)
-          sshConfig.setText(fc.getSelectedFile().toString());
-      } else if (button == sshKeyFileBrowser) {
-        JFileChooser fc = new JFileChooser();
-        fc.setDialogTitle("Path to SSH key file");
-        fc.setApproveButtonText("OK");
-        fc.setFileHidingEnabled(false);
-        int ret = fc.showOpenDialog(this);
-        if (ret == JFileChooser.APPROVE_OPTION)
-          sshKeyFile.setText(fc.getSelectedFile().toString());
-      }
-    } else if (s instanceof JRadioButton) {
-      JRadioButton button = (JRadioButton)s;
-      if (button == sshArgsCustom || button == sshArgsDefault) {
-        sshArguments.setEnabled(sshArgsCustom.isSelected());
-      }
+  private void handleEmbed()
+  {
+    if (embed.getValue()) {
+      desktopSizeCheckbox.setEnabled(false);
+      desktopWidthInput.setEnabled(false);
+      desktopHeightInput.setEnabled(false);
+      fullScreenCheckbox.setEnabled(false);
+      fullScreenAllMonitorsCheckbox.setEnabled(false);
+      scalingFactorInput.setEnabled(false);
     }
   }
 
-  public void itemStateChanged(ItemEvent e) {
-    Object s = e.getSource();
-    if (s instanceof JCheckBox) {
-      JCheckBox item = (JCheckBox)s;
-      boolean enable = item.isSelected();
-      if (item == autoSelect) {
-        ButtonGroup[] groups = { encodingGroup, colourGroup };
-        for (ButtonGroup grp : groups) {
-          Enumeration<AbstractButton> elems = grp.getElements();
-          while (elems.hasMoreElements())
-            elems.nextElement().setEnabled(!enable);
-        }
-      } else if (item == customCompressLevel) {
-        compressLevel.setEnabled(enable);
-      } else if (item == desktopSize) {
-        desktopWidth.setEnabled(enable);
-        desktopHeight.setEnabled(enable);
-      } else if (item == noJpeg) {
-        qualityLevel.setEnabled(enable);
-      } else if (item == encX509) {
-        x509ca.setEnabled(enable);
-        caButton.setEnabled(enable);
-        x509crl.setEnabled(enable);
-        crlButton.setEnabled(enable);
-      } else if (item == secVeNCrypt) {
-        encNone.setEnabled(enable);
-        encTLS.setEnabled(enable);
-        encX509.setEnabled(enable);
-        x509ca.setEnabled(enable && encX509.isSelected());
-        caButton.setEnabled(enable && encX509.isSelected());
-        x509crl.setEnabled(enable && encX509.isSelected());
-        crlButton.setEnabled(enable && encX509.isSelected());
-        secIdent.setEnabled(enable);
-        secPlain.setEnabled(enable);
-        sendLocalUsername.setEnabled(enable);
-      } else if (item == encNone) {
-        secNone.setSelected(enable &&
-          UserPreferences.getBool("viewer", "secNone", true));
-        secVnc.setSelected(enable &&
-          UserPreferences.getBool("viewer", "secVnc", true));
-      } else if (item == secIdent || item == secPlain) {
-        sendLocalUsername.setEnabled(secIdent.isSelected() ||
-                                     secPlain.isSelected());
-      } else if (item == sshTunnel) {
-        sshUseGateway.setEnabled(enable);
-        sshUser.setEnabled(enable &&
-                           sshUseGateway.isEnabled() &&
-                           sshUseGateway.isSelected());
-        sshHost.setEnabled(enable &&
-                           sshUseGateway.isEnabled() &&
-                           sshUseGateway.isSelected());
-        sshPort.setEnabled(enable &&
-                           sshUseGateway.isEnabled() &&
-                           sshUseGateway.isSelected());
-        sshUseExt.setEnabled(enable);
-        sshClient.setEnabled(enable &&
-                             sshUseExt.isEnabled() &&
-                             sshUseExt.isSelected());
-        sshClientBrowser.setEnabled(enable &&
-                                    sshUseExt.isEnabled() &&
-                                    sshUseExt.isSelected());
-        sshArgsDefault.setEnabled(enable &&
-                                  sshUseExt.isEnabled() &&
-                                  sshUseExt.isSelected());
-        sshArgsCustom.setEnabled(enable &&
-                                 sshUseExt.isEnabled() &&
-                                 sshUseExt.isSelected());
-        sshArguments.setEnabled(enable &&
-                                sshUseExt.isEnabled() &&
-                                sshUseExt.isSelected() &&
-                                sshArgsCustom.isSelected());
-        sshConfig.setEnabled(enable &&
-                             sshUseExt.isEnabled() &&
-                             !sshUseExt.isSelected());
-        sshConfigBrowser.setEnabled(enable &&
-                                    sshUseExt.isEnabled() &&
-                                    !sshUseExt.isSelected());
-        sshKeyFile.setEnabled(enable &&
-                              sshUseExt.isEnabled() &&
-                              !sshUseExt.isSelected());
-        sshKeyFileBrowser.setEnabled(enable &&
-                                     sshUseExt.isEnabled() &&
-                                     !sshUseExt.isSelected());
-      } else if (item == sshUseExt) {
-        sshClient.setEnabled(enable);
-        sshClientBrowser.setEnabled(enable);
-        sshArgsDefault.setEnabled(enable);
-        sshArgsCustom.setEnabled(enable);
-        sshArguments.setEnabled(enable && sshArgsCustom.isSelected());
-        sshConfig.setEnabled(!enable);
-        sshConfigBrowser.setEnabled(!enable);
-        sshKeyFile.setEnabled(!enable);
-        sshKeyFileBrowser.setEnabled(!enable);
-      } else if (item == sshUseGateway) {
-        sshUser.setEnabled(enable);
-        sshHost.setEnabled(enable);
-        sshPort.setEnabled(enable);
-      }
+  private void handleRfbState()
+  {
+    CConn cc = VncViewer.cc;
+    if (cc != null && cc.state() == CConnection.RFBSTATE_NORMAL) {
+      JComponent[] components = {
+          encNoneCheckbox, encTLSCheckbox, encX509Checkbox, authNoneCheckbox,
+          authVncCheckbox, authVncCheckbox, authIdentCheckbox, authPlainCheckbox,
+          sendLocalUsernameCheckbox, caInput, caChooser, crlInput, crlChooser,
+          sharedCheckbox, tunnelCheckbox, viaCheckbox, viaUserInput, viaHostInput,
+          viaPortInput, extSSHCheckbox, sshClientInput, sshClientChooser,
+          sshArgsDefaultButton, sshArgsCustomButton, sshArgsInput, sshConfigInput,
+          sshKeyFileInput, sshConfigChooser, sshKeyFileChooser,
+        };
+      for (JComponent c : components)
+        c.setEnabled(false);
     }
   }
+
+  static LogWriter vlog = new LogWriter("OptionsDialog");
 }
diff --git a/java/com/tigervnc/vncviewer/OptionsDialogCallback.java b/java/com/tigervnc/vncviewer/OptionsDialogCallback.java
deleted file mode 100644 (file)
index efb8c06..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- *
- * This is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this software; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
- */
-
-package com.tigervnc.vncviewer;
-
-public interface OptionsDialogCallback {
-  public void setOptions();
-  public void getOptions();
-}
index fbaf99118b52f4215361d579834b0d13191f1cd8..9d5cc7cbfb938453c3d9f2f2ab15366b01917dea 100644 (file)
@@ -149,7 +149,6 @@ class PasswdDialog extends Dialog implements UserInfo,
                                             String instruction,
                                             String[] prompt,
                                             boolean[] echo) {
-    vlog.info("OK");
     Container panel = new JPanel(new GridBagLayout());
     ((JPanel)panel).setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
 
index e0e24f060571196c0ded5cdd39022984a236d29d..8fc2760b33845ce095bba7005888a16d953e0b17 100644 (file)
@@ -27,12 +27,11 @@ import com.tigervnc.rfb.*;
 
 abstract public class PlatformPixelBuffer extends PixelBuffer
 {
-  public PlatformPixelBuffer(int w, int h, CConn cc_, DesktopWindow desktop_) {
-    cc = cc_;
+  public PlatformPixelBuffer(PixelFormat pf, int w, int h, DesktopWindow desktop_) {
     desktop = desktop_;
     PixelFormat nativePF = getNativePF();
-    if (nativePF.depth > cc.serverPF.depth) {
-      setPF(cc.serverPF);
+    if (nativePF.depth > pf.depth) {
+      setPF(pf);
     } else {
       setPF(nativePF);
     }
@@ -103,7 +102,6 @@ abstract public class PlatformPixelBuffer extends PixelBuffer
   byte[] greens;
   byte[] blues;
 
-  CConn cc;
   DesktopWindow desktop;
   static LogWriter vlog = new LogWriter("PlatformPixelBuffer");
 }
index 172bde6f338d83e1516b879021e1b57e58b11bcb..338e6266a2d3f3f0f4aa2f6306c73ea007670d52 100644 (file)
@@ -21,6 +21,7 @@ package com.tigervnc.vncviewer;
 
 import java.awt.*;
 import java.awt.event.*;
+import java.io.File;
 import javax.swing.*;
 import javax.swing.border.*;
 import javax.swing.WindowConstants.*;
@@ -30,38 +31,41 @@ import com.tigervnc.rfb.*;
 
 import static java.awt.GridBagConstraints.HORIZONTAL;
 import static java.awt.GridBagConstraints.LINE_START;
+import static java.awt.GridBagConstraints.LINE_END;
 import static java.awt.GridBagConstraints.NONE;
 import static java.awt.GridBagConstraints.REMAINDER;
 
-class ServerDialog extends Dialog {
+import static com.tigervnc.vncviewer.Parameters.*;
 
-  @SuppressWarnings({"unchecked","rawtypes"})
-  public ServerDialog(OptionsDialog options_,
-                      String defaultServerName, CConn cc_) {
+class ServerDialog extends Dialog implements Runnable {
 
+  @SuppressWarnings({"unchecked","rawtypes"})
+  public ServerDialog() {
     super(true);
-    cc = cc_;
     setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
     setTitle("VNC Viewer: Connection Details");
     setResizable(false);
     addWindowListener(new WindowAdapter() {
       public void windowClosing(WindowEvent e) {
-        if (VncViewer.nViewers == 1) {
-          cc.viewer.exit(1);
-        } else {
-          ret = false;
-          endDialog();
-        }
+        endDialog();
+        System.exit(1);
       }
     });
 
-    options = options_;
-
-    JLabel serverLabel = new JLabel("VNC Server:", JLabel.RIGHT);
+    JLabel serverLabel = new JLabel("VNC server:", JLabel.RIGHT);
     String valueStr = new String("");
     if (UserPreferences.get("ServerDialog", "history") != null)
       valueStr = UserPreferences.get("ServerDialog", "history");
     server = new MyJComboBox(valueStr.split(","));
+    server.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        JComboBox s = (JComboBox)e.getSource();
+        if (e.getActionCommand().equals("comboBoxEdited")) {
+          s.insertItemAt(editor.getItem(), 0);
+          s.setSelectedIndex(0);
+        }
+      }
+    });
     if (valueStr.equals(""))
       server.setPrototypeDisplayValue("255.255.255.255:5900");
 
@@ -74,7 +78,7 @@ class ServerDialog extends Dialog {
         if (e.getKeyCode() == KeyEvent.VK_ENTER) {
           server.insertItemAt(editor.getItem(), 0);
           server.setSelectedIndex(0);
-          commit();
+          handleConnect();
         }
       }
     });
@@ -84,9 +88,41 @@ class ServerDialog extends Dialog {
 
     JLabel icon = new JLabel(VncViewer.logoIcon);
     optionsButton = new JButton("Options...");
+    optionsButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        handleOptions();
+      }
+    });
+    JButton loadButton = new JButton("Load...");
+    loadButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        handleLoad();
+      }
+    });
+    JButton saveAsButton = new JButton("Save As...");
+    saveAsButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        handleSaveAs();
+      }
+    });
     aboutButton = new JButton("About...");
-    okButton = new JButton("OK");
+    aboutButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        handleAbout();
+      }
+    });
     cancelButton = new JButton("Cancel");
+    cancelButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        handleCancel();
+      }
+    });
+    connectButton = new JButton("Connect   \u21B5");
+    connectButton.addActionListener(new ActionListener() {
+      public void actionPerformed(ActionEvent e) {
+        handleConnect();
+      }
+    });
 
     contentPane.add(icon,
                     new GridBagConstraints(0, 0,
@@ -109,81 +145,133 @@ class ServerDialog extends Dialog {
                                            LINE_START, HORIZONTAL,
                                            new Insets(5, 0, 5, 5),
                                            NONE, NONE));
-    JPanel buttonPane = new JPanel();
-    buttonPane.setLayout(new GridLayout(1, 4, 5, 5));
-    buttonPane.add(aboutButton);
-    buttonPane.add(optionsButton);
-    buttonPane.add(okButton);
-    buttonPane.add(cancelButton);
-    contentPane.add(buttonPane,
+    JPanel buttonPane1 = new JPanel();
+    Box box = Box.createHorizontalBox();
+    JSeparator separator1 = new JSeparator();
+    JSeparator separator2 = new JSeparator();
+    GroupLayout layout = new GroupLayout(buttonPane1);
+    buttonPane1.setLayout(layout);
+    layout.setAutoCreateGaps(false);
+    layout.setAutoCreateContainerGaps(false);
+               layout.setHorizontalGroup(
+                 layout.createSequentialGroup()
+                   .addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
+                     .addGroup(layout.createSequentialGroup()
+                       .addGap(10)
+                       .addComponent(optionsButton))
+                     .addComponent(separator1)
+                     .addGroup(layout.createSequentialGroup()
+                       .addGap(10)
+                       .addComponent(aboutButton)))
+                   .addGroup(layout.createParallelGroup(GroupLayout.Alignment.TRAILING)
+                     .addGroup(layout.createSequentialGroup()
+                       .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                       .addComponent(loadButton)
+                       .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                       .addComponent(saveAsButton)
+                       .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                       .addComponent(box)
+                       .addGap(10))
+                     .addComponent(separator2)
+                     .addGroup(layout.createSequentialGroup()
+                       .addComponent(cancelButton)
+                       .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                       .addComponent(connectButton)
+                       .addGap(10)))
+               );
+               layout.setVerticalGroup(
+                 layout.createSequentialGroup()
+                   .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+                     .addComponent(optionsButton)
+                     .addComponent(loadButton)
+                     .addComponent(saveAsButton)
+                     .addComponent(box))
+                   .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                   .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+                     .addComponent(separator1)
+                     .addComponent(separator2))
+                   .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+                   .addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
+                     .addComponent(aboutButton)
+                     .addComponent(cancelButton)
+                     .addComponent(connectButton))
+                   .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
+               );
+               layout.linkSize(SwingConstants.HORIZONTAL,
+                    optionsButton, loadButton, saveAsButton,
+                    aboutButton, cancelButton, box);
+    contentPane.add(buttonPane1,
                     new GridBagConstraints(0, 1,
                                            REMAINDER, 1,
                                            LIGHT, LIGHT,
                                            LINE_START, HORIZONTAL,
-                                           new Insets(5, 5, 5, 5),
+                                           new Insets(5, 0, 10, 0),
                                            NONE, NONE));
-    addListeners(this);
     pack();
   }
 
-  @SuppressWarnings({"unchecked","rawtypes"})
-  public void actionPerformed(ActionEvent e) {
-    Object s = e.getSource();
-    if (s instanceof JButton && (JButton)s == okButton) {
-      commit();
-    } else if (s instanceof JButton && (JButton)s == cancelButton) {
-      if (VncViewer.nViewers == 1)
-        cc.viewer.exit(1);
-      ret = false;
-      endDialog();
-    } else if (s instanceof JButton && (JButton)s == optionsButton) {
-      options.showDialog(this);
-    } else if (s instanceof JButton && (JButton)s == aboutButton) {
-      cc.showAbout();
-    } else if (s instanceof JComboBox && (JComboBox)s == server) {
-      if (e.getActionCommand().equals("comboBoxEdited")) {
-        server.insertItemAt(editor.getItem(), 0);
-        server.setSelectedIndex(0);
-      }
+  public void run() {
+    this.showDialog();
+  }
+
+  private void handleOptions() {
+    OptionsDialog.showDialog(this);
+  }
+
+  private void handleLoad() {
+    String title = "Select a TigerVNC configuration file";
+    File dflt = new File(FileUtils.getVncHomeDir().concat("default.tigervnc"));
+    File f = showChooser(title, dflt);
+    if (f != null && f.exists() && f.canRead())
+      loadViewerParameters(f.getAbsolutePath());
+  }
+
+  private void handleSaveAs() {
+    String title = "Save the TigerVNC configuration to file";
+    File dflt = new File(FileUtils.getVncHomeDir().concat("default.tigervnc"));
+    if (!dflt.exists() || !dflt.isFile())
+      dflt = new File(FileUtils.getVncHomeDir());
+    File f = showChooser(title, dflt);
+    while (f != null && f.exists() && f.isFile()) {
+      String msg = f.getAbsolutePath();
+      msg = msg.concat(" already exists. Do you want to overwrite?");
+      Object[] options = {"Overwrite", "No  \u21B5"};
+      JOptionPane op =
+        new JOptionPane(msg, JOptionPane.QUESTION_MESSAGE,
+                        JOptionPane.OK_CANCEL_OPTION, null, options, options[1]);
+      JDialog dlg = op.createDialog(this, "TigerVNC Viewer");
+      dlg.setIconImage(VncViewer.frameIcon);
+      dlg.setAlwaysOnTop(true);
+      dlg.setVisible(true);
+      if (op.getValue() == options[0])
+        break;
+      else
+        f = showChooser(title, f);
     }
+    if (f != null && (!f.exists() || f.canWrite()))
+      saveViewerParameters(f.getAbsolutePath(), (String)server.getSelectedItem());
+  }
+
+  private void handleAbout() {
+    VncViewer.showAbout(this);
   }
 
-  private void commit() {
+  private void handleCancel() {
+    vncServerName.setParam("");
+    endDialog();
+  }
+
+  private void handleConnect() {
     String serverName = (String)server.getSelectedItem();
-    if (serverName == null || serverName.equals("")) {
-      vlog.error("Invalid servername specified");
-      if (VncViewer.nViewers == 1)
-        cc.viewer.exit(1);
-      ret = false;
-      endDialog();
-    }
-    // set params
-    Configuration.setParam("Server", Hostname.getHost(serverName));
-    Configuration.setParam("Port",
-                            Integer.toString(Hostname.getPort(serverName)));
-    // Update the history list
-    String valueStr = UserPreferences.get("ServerDialog", "history");
-    String t = (valueStr == null) ? "" : valueStr;
-    StringTokenizer st = new StringTokenizer(t, ",");
-    StringBuffer sb =
-        new StringBuffer().append((String)server.getSelectedItem());
-    while (st.hasMoreTokens()) {
-      String str = st.nextToken();
-      if (!str.equals((String)server.getSelectedItem()) && !str.equals("")) {
-        sb.append(',');
-        sb.append(str);
-      }
-    }
-    UserPreferences.set("ServerDialog", "history", sb.toString());
-    UserPreferences.save("ServerDialog");
+    vncServerName.setParam(serverName);
+    saveViewerParameters(null, serverName);
     endDialog();
   }
 
-  CConn cc;
   @SuppressWarnings("rawtypes")
   MyJComboBox server;
   ComboBoxEditor editor;
-  JButton aboutButton, optionsButton, okButton, cancelButton;
+  JButton aboutButton, optionsButton, connectButton, cancelButton;
   OptionsDialog options;
   static LogWriter vlog = new LogWriter("ServerDialog");
 
index 90ab38efb64cce19554891f555320df076865f25..f036c1a5d4b1bbeb1f11910fa8b421bca97e5610 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright (C) 2012-2016 All Rights Reserved.
+ *  Copyright (C) 2012-2016 Brian P. Hinz. All Rights Reserved.
  *  Copyright (C) 2000 Const Kaplinsky.  All Rights Reserved.
  *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
  *
@@ -43,6 +43,8 @@ import com.jcraft.jsch.Logger;
 import com.jcraft.jsch.OpenSSHConfig;
 import com.jcraft.jsch.Session;
 
+import static com.tigervnc.vncviewer.Parameters.*;
+
 public class Tunnel {
 
   private final static String DEFAULT_TUNNEL_TEMPLATE
@@ -56,27 +58,27 @@ public class Tunnel {
     String remoteHost;
 
     remotePort = cc.getServerPort();
-    if (cc.viewer.tunnel.getValue()) {
+    if (tunnel.getValue()) {
       gatewayHost = cc.getServerName();
       remoteHost = "localhost";
     } else {
-      gatewayHost = getSshHost(cc);
+      gatewayHost = getSshHost();
       remoteHost = cc.getServerName();
     }
 
-    String pattern = cc.viewer.extSSHArgs.getValue();
-    if (pattern == null) {
-      if (cc.viewer.tunnel.getValue())
+    String pattern = extSSHArgs.getValue();
+    if (pattern == null || pattern.isEmpty()) {
+      if (tunnel.getValue())
         pattern = System.getProperty("VNC_TUNNEL_CMD");
       else
         pattern = System.getProperty("VNC_VIA_CMD");
     }
 
-    if (cc.viewer.extSSH.getValue() || 
+    if (extSSH.getValue() || 
         (pattern != null && pattern.length() > 0)) {
-      createTunnelExt(gatewayHost, remoteHost, remotePort, localPort, pattern, cc);
+      createTunnelExt(gatewayHost, remoteHost, remotePort, localPort, pattern);
     } else {
-      createTunnelJSch(gatewayHost, remoteHost, remotePort, localPort, cc);
+      createTunnelJSch(gatewayHost, remoteHost, remotePort, localPort);
     }
   }
 
@@ -99,10 +101,10 @@ public class Tunnel {
     }
   }
 
-  public static String getSshHost(CConn cc) {
-    String sshHost = cc.viewer.via.getValue();
-    if (sshHost == null)
-      return cc.getServerName();
+  public static String getSshHost() {
+    String sshHost = via.getValue();
+    if (sshHost.isEmpty())
+      return vncServerName.getValue();
     int end = sshHost.indexOf(":");
     if (end < 0)
       end = sshHost.length();
@@ -110,37 +112,42 @@ public class Tunnel {
     return sshHost;
   }
 
-  public static String getSshUser(CConn cc) {
+  public static String getSshUser() {
     String sshUser = (String)System.getProperties().get("user.name");
-    String via = cc.viewer.via.getValue();
-    if (via != null && via.indexOf("@") > 0)
-      sshUser = via.substring(0, via.indexOf("@"));
+    String viaStr = via.getValue();
+    if (!viaStr.isEmpty() && viaStr.indexOf("@") > 0)
+      sshUser = viaStr.substring(0, viaStr.indexOf("@"));
     return sshUser;
   }
 
-  public static int getSshPort(CConn cc) {
+  public static int getSshPort() {
     String sshPort = "22";
-    String via = cc.viewer.via.getValue();
-    if (via != null && via.indexOf(":") > 0)
-      sshPort = via.substring(via.indexOf(":")+1, via.length());
+    String viaStr = via.getValue();
+    if (!viaStr.isEmpty() && viaStr.indexOf(":") > 0)
+      sshPort = viaStr.substring(viaStr.indexOf(":")+1, viaStr.length());
     return Integer.parseInt(sshPort);
   }
 
-  public static String getSshKeyFile(CConn cc) {
-    if (cc.viewer.sshKeyFile.getValue() != null)
-      return cc.viewer.sshKeyFile.getValue();
+  public static String getSshKeyFile() {
+    if (!sshKeyFile.getValue().isEmpty())
+      return sshKeyFile.getValue();
     String[] ids = { "id_dsa", "id_rsa" };
     for (String id : ids) {
       File f = new File(FileUtils.getHomeDir()+".ssh/"+id);
       if (f.exists() && f.canRead())
         return(f.getAbsolutePath());
     }
-    return null;
+    return "";
+  }
+
+  public static String getSshKey() {
+    if (!sshKey.getValue().isEmpty())
+      return sshKeyFile.getValue().replaceAll("\\\\n", "\n");
+    return "";
   }
 
   private static void createTunnelJSch(String gatewayHost, String remoteHost,
-                                       int remotePort, int localPort,
-                                       CConn cc) throws Exception {
+                                       int remotePort, int localPort) throws Exception {
     JSch.setLogger(new MyJSchLogger());
     JSch jsch=new JSch();
 
@@ -152,44 +159,39 @@ public class Tunnel {
       if (knownHosts.exists() && knownHosts.canRead())
            jsch.setKnownHosts(knownHosts.getAbsolutePath());
       ArrayList<File> privateKeys = new ArrayList<File>();
-      String sshKeyFile = cc.options.sshKeyFile.getText();
-      String sshKey = cc.viewer.sshKey.getValue();
-      if (sshKey != null) {
-        String sshKeyPass = cc.viewer.sshKeyPass.getValue();
+      if (!getSshKey().isEmpty()) {
         byte[] keyPass = null, key;
-        if (sshKeyPass != null)
-          keyPass = sshKeyPass.getBytes();
-        sshKey = sshKey.replaceAll("\\\\n", "\n");
-        key = sshKey.getBytes();
-        jsch.addIdentity("TigerVNC", key, null, keyPass);
-      } else if (!sshKeyFile.equals("")) {
-        File f = new File(sshKeyFile);
+        if (!sshKeyPass.getValue().isEmpty())
+          keyPass = sshKeyPass.getValue().getBytes();
+        jsch.addIdentity("TigerVNC", getSshKey().getBytes(), null, keyPass);
+      } else if (!getSshKeyFile().isEmpty()) {
+        File f = new File(getSshKeyFile());
         if (!f.exists() || !f.canRead())
-          throw new Exception("Cannot access SSH key file "+ sshKeyFile);
+          throw new Exception("Cannot access SSH key file "+getSshKeyFile());
         privateKeys.add(f);
       }
       for (Iterator<File> i = privateKeys.iterator(); i.hasNext();) {
         File privateKey = (File)i.next();
         if (privateKey.exists() && privateKey.canRead())
-          if (cc.viewer.sshKeyPass.getValue() != null)
+          if (!sshKeyPass.getValue().isEmpty())
                jsch.addIdentity(privateKey.getAbsolutePath(),
-                             cc.viewer.sshKeyPass.getValue());
+                             sshKeyPass.getValue());
           else
                jsch.addIdentity(privateKey.getAbsolutePath());
       }
   
-      String user = getSshUser(cc);
+      String user = getSshUser();
       String label = new String("SSH Authentication");
       PasswdDialog dlg =
         new PasswdDialog(label, (user == null ? false : true), false);
       dlg.userEntry.setText(user != null ? user : "");
-      File ssh_config = new File(cc.viewer.sshConfig.getValue());
+      File ssh_config = new File(sshConfig.getValue());
       if (ssh_config.exists() && ssh_config.canRead()) {
         ConfigRepository repo =
           OpenSSHConfig.parse(ssh_config.getAbsolutePath());
         jsch.setConfigRepository(repo);
       }
-      Session session=jsch.getSession(user, gatewayHost, getSshPort(cc));
+      Session session=jsch.getSession(user, gatewayHost, getSshPort());
       session.setUserInfo(dlg);
       // OpenSSHConfig doesn't recognize StrictHostKeyChecking
       if (session.getConfig("StrictHostKeyChecking") == null)
@@ -201,93 +203,19 @@ public class Tunnel {
     }
   }
 
-  private static class MyExtProcess implements Runnable {
-
-    private String cmd = null;
-    private Process pid = null;
-
-    private static class MyProcessLogger extends Thread {
-      private final BufferedReader err;
-  
-      public MyProcessLogger(Process p) {
-        InputStreamReader reader = 
-          new InputStreamReader(p.getErrorStream());
-        err = new BufferedReader(reader);
-      }
-  
-      @Override
-      public void run() {
-        try {
-          while (true) {
-            String msg = err.readLine();
-            if (msg != null)
-              vlog.info(msg);
-          }
-        } catch(java.io.IOException e) {
-          vlog.info(e.getMessage());
-        } finally {
-          try {
-            if (err != null)
-              err.close();
-          } catch (java.io.IOException e ) { }
-        }
-      }
-    }
-
-    private static class MyShutdownHook extends Thread {
-
-      private Process proc = null;
-
-      public MyShutdownHook(Process p) {
-        proc = p;
-      }
-  
-      @Override
-      public void run() {
-        try {
-          proc.exitValue();
-        } catch (IllegalThreadStateException e) {
-          try {
-            // wait for CConn to shutdown the socket
-            Thread.sleep(500);
-          } catch(InterruptedException ie) { }
-          proc.destroy();
-        }
-      }
-    }
-
-    public MyExtProcess(String command) {
-      cmd = command;  
-    }
-
-    public void run() {
-      try {
-        Runtime runtime = Runtime.getRuntime();
-        pid = runtime.exec(cmd);
-        runtime.addShutdownHook(new MyShutdownHook(pid));
-        new MyProcessLogger(pid).start();
-        pid.waitFor();
-      } catch(InterruptedException e) {
-        vlog.info(e.getMessage());
-      } catch(java.io.IOException e) {
-        vlog.info(e.getMessage());
-      }
-    }
-  }
-
   private static void createTunnelExt(String gatewayHost, String remoteHost,
                                       int remotePort, int localPort,
-                                      String pattern, CConn cc) throws Exception {
+                                      String pattern) throws Exception {
     if (pattern == null || pattern.length() < 1) {
-      if (cc.viewer.tunnel.getValue())
+      if (tunnel.getValue())
         pattern = DEFAULT_TUNNEL_TEMPLATE;
       else
         pattern = DEFAULT_VIA_TEMPLATE;
     }
     String cmd = fillCmdPattern(pattern, gatewayHost, remoteHost,
-                                remotePort, localPort, cc);
+                                remotePort, localPort);
     try {
-      Thread t = new Thread(new MyExtProcess(cmd));
+      Thread t = new Thread(new ExtProcess(cmd, vlog, true));
       t.start();
       // wait for the ssh process to start
       Thread.sleep(1000);
@@ -298,21 +226,21 @@ public class Tunnel {
 
   private static String fillCmdPattern(String pattern, String gatewayHost,
                                        String remoteHost, int remotePort,
-                                       int localPort, CConn cc) {
+                                       int localPort) {
     boolean H_found = false, G_found = false, R_found = false, L_found = false;
     boolean P_found = false;
-    String cmd = cc.options.sshClient.getText() + " ";
+    String cmd = extSSHClient.getValue() + " ";
     pattern.replaceAll("^\\s+", "");
 
-    String user = getSshUser(cc);
-    int sshPort = getSshPort(cc);
+    String user = getSshUser();
+    int sshPort = getSshPort();
     gatewayHost = user + "@" + gatewayHost;
 
     for (int i = 0; i < pattern.length(); i++) {
       if (pattern.charAt(i) == '%') {
         switch (pattern.charAt(++i)) {
         case 'H':
-          cmd += (cc.viewer.tunnel.getValue() ? gatewayHost : remoteHost);
+          cmd += (tunnel.getValue() ? gatewayHost : remoteHost);
              H_found = true;
           continue;
         case 'G':
@@ -342,7 +270,7 @@ public class Tunnel {
     if (!H_found || !R_found || !L_found)
       throw new Exception("%H, %R or %L absent in tunneling command template.");
 
-    if (!cc.viewer.tunnel.getValue() && !G_found)
+    if (!tunnel.getValue() && !G_found)
       throw new Exception("%G pattern absent in tunneling command template.");
 
     vlog.info("SSH command line: "+cmd);
index b55744bdf8fb9ab935e66dec1eaa97b88ca7fcb9..3a5fb54ca445ac1eb647c2667606d42f1da57cc7 100644 (file)
@@ -42,6 +42,8 @@ import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER;
 import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED;
 import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED;
 
+import static com.tigervnc.vncviewer.Parameters.*;
+
 public class Viewport extends JFrame
 {
   public Viewport(String name, CConn cc_) {
@@ -58,7 +60,7 @@ public class Viewport extends JFrame
     sp.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
     getContentPane().add(sp);
     if (VncViewer.os.startsWith("mac os x")) {
-      if (!VncViewer.noLionFS.getValue())
+      if (!noLionFS.getValue())
         enableLionFS();
     }
     addWindowFocusListener(new WindowAdapter() {
@@ -72,20 +74,12 @@ public class Viewport extends JFrame
     });
     addWindowListener(new WindowAdapter() {
       public void windowClosing(WindowEvent e) {
-        if (VncViewer.nViewers == 1) {
-          if (cc.closeListener != null) {
-            cc.close();
-          } else {
-            cc.viewer.exit(1);
-          }
-        } else {
-          cc.close();
-        }
+        cc.close();
       }
     });
     addComponentListener(new ComponentAdapter() {
       public void componentResized(ComponentEvent e) {
-        String scaleString = cc.viewer.scalingFactor.getValue();
+        String scaleString = scalingFactor.getValue();
         if (scaleString.equalsIgnoreCase("Auto") ||
             scaleString.equalsIgnoreCase("FixedRatio")) {
           if ((sp.getSize().width != cc.desktop.scaledWidth) ||
@@ -95,7 +89,7 @@ public class Viewport extends JFrame
             sp.setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_NEVER);
             sp.validate();
             if (getExtendedState() != JFrame.MAXIMIZED_BOTH &&
-                !cc.fullScreen) {
+                !fullScreen.getValue()) {
               sp.setSize(new Dimension(cc.desktop.scaledWidth,
                                        cc.desktop.scaledHeight));
               int w = cc.desktop.scaledWidth + getInsets().left +
@@ -120,6 +114,10 @@ public class Viewport extends JFrame
     });
   }
 
+  public void setName(String name) {
+    setTitle(name + "- TigerVNC");
+  }
+
   boolean lionFSSupported() { return canDoLionFS; }
 
   void enableLionFS() {
@@ -168,7 +166,7 @@ public class Viewport extends JFrame
 
   public void setGeometry(int x, int y, int w, int h) {
     pack();
-    if (!cc.fullScreen)
+    if (!fullScreen.getValue())
       setLocation(x, y);
   }
 
@@ -181,7 +179,7 @@ public class Viewport extends JFrame
       GraphicsEnvironment.getLocalGraphicsEnvironment();
     Rectangle r = new Rectangle();
     setMaximizedBounds(null);
-    if (cc.viewer.fullScreenAllMonitors.getValue()) {
+    if (fullScreenAllMonitors.getValue()) {
       for (GraphicsDevice gd : ge.getScreenDevices())
         for (GraphicsConfiguration gc : gd.getConfigurations())
           r = r.union(gc.getBounds());
index fc9c7b590b4ff0ceb8c1f6db221820f878f59d24..a3daef31ffcc134356db55356dda14ed60369b94 100644 (file)
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
  * Copyright 2011 Pierre Ossman <ossman@cendio.se> for Cendio AB
  * Copyright (C) 2011-2013 D. R. Commander.  All Rights Reserved.
- * Copyright (C) 2011-2015 Brian P. Hinz
+ * Copyright (C) 2011-2016 Brian P. Hinz
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,22 +35,28 @@ import java.awt.event.*;
 import java.awt.Color;
 import java.awt.Graphics;
 import java.awt.Image;
+import java.io.BufferedReader;
 import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.IOException;
 import java.io.File;
 import java.lang.Character;
 import java.lang.reflect.*;
+import java.net.URL;
 import java.util.jar.Attributes;
 import java.util.jar.Manifest;
 import java.util.*;
 import javax.swing.*;
 import javax.swing.plaf.FontUIResource;
+import javax.swing.SwingUtilities;
 import javax.swing.UIManager.*;
 
 import com.tigervnc.rdr.*;
 import com.tigervnc.rfb.*;
 import com.tigervnc.network.*;
 
+import static com.tigervnc.vncviewer.Parameters.*;
+
 public class VncViewer extends javax.swing.JApplet 
   implements Runnable, ActionListener {
 
@@ -133,12 +139,12 @@ public class VncViewer extends javax.swing.JApplet
     viewer.start();
   }
 
+  public VncViewer() {
+    //this(new String[0]);
+    embed.setParam(true);
+  }
 
   public VncViewer(String[] argv) {
-    embed.setParam(false);
-
-    // load user preferences
-    UserPreferences.load("global");
 
     SecurityClient.setDefaults();
 
@@ -150,14 +156,24 @@ public class VncViewer extends javax.swing.JApplet
 
     Configuration.enableViewerParams();
 
+    /* Load the default parameter settings */
+    String defaultServerName;
+    try {
+      defaultServerName = loadViewerParameters(null);
+    } catch (com.tigervnc.rfb.Exception e) {
+      defaultServerName = "";
+      vlog.info(e.getMessage());
+    }
+
     // Override defaults with command-line options
     for (int i = 0; i < argv.length; i++) {
       if (argv[i].length() == 0)
         continue;
 
       if (argv[i].equalsIgnoreCase("-config")) {
-        if (++i >= argv.length) usage();
-          Configuration.load(argv[i]);
+        if (++i >= argv.length)
+          usage();
+        defaultServerName = loadViewerParameters(argv[i]);
         continue;
       }
 
@@ -181,28 +197,11 @@ public class VncViewer extends javax.swing.JApplet
         usage();
       }
 
-      if (vncServerName.getValue() != null)
+      if (!vncServerName.getValue().isEmpty())
         usage();
       vncServerName.setParam(argv[i]);
     }
 
-    if (!autoSelect.hasBeenSet()) {
-      // Default to AutoSelect=0 if -PreferredEncoding or -FullColor is used
-      autoSelect.setParam(!preferredEncoding.hasBeenSet() &&
-                          !fullColour.hasBeenSet() &&
-                          !fullColourAlias.hasBeenSet());
-    }
-    if (!fullColour.hasBeenSet() && !fullColourAlias.hasBeenSet()) {
-      // Default to FullColor=0 if AutoSelect=0 && LowColorLevel is set
-      if (!autoSelect.getValue() && (lowColourLevel.hasBeenSet() ||
-                          lowColourLevelAlias.hasBeenSet())) {
-        fullColour.setParam(false);
-      }
-    }
-    if (!customCompressLevel.hasBeenSet()) {
-      // Default to CustomCompressLevel=1 if CompressLevel is used.
-      customCompressLevel.setParam(compressLevel.hasBeenSet());
-    }
   }
 
   public static void usage() {
@@ -272,26 +271,27 @@ public class VncViewer extends javax.swing.JApplet
     System.exit(1);
   }
 
-  public VncViewer() {
-    UserPreferences.load("global");
-    embed.setParam(true);
-  }
-
-  public static void newViewer(VncViewer oldViewer, Socket sock, boolean close) {
-    VncViewer viewer = new VncViewer();
-    viewer.embed.setParam(oldViewer.embed.getValue());
-    viewer.sock = sock;
-    viewer.start();
-    if (close)
-      oldViewer.exit(0);
-  }
-
-  public static void newViewer(VncViewer oldViewer, Socket sock) {
-    newViewer(oldViewer, sock, false);
-  }
-
-  public static void newViewer(VncViewer oldViewer) {
-    newViewer(oldViewer, null);
+  public static void newViewer() {
+    String cmd = "java -jar ";
+    try {
+      URL url =
+        VncViewer.class.getProtectionDomain().getCodeSource().getLocation();
+      File f = new File(url.toURI());
+      if (!f.exists() || !f.canRead()) {
+        String msg = new String("The jar file "+f.getAbsolutePath()+
+                                " does not exist or cannot be read.");
+        JOptionPane.showMessageDialog(null, msg, "ERROR",
+                                      JOptionPane.ERROR_MESSAGE);
+        return;
+      }
+      cmd = cmd.concat(f.getAbsolutePath());
+      Thread t = new Thread(new ExtProcess(cmd, vlog));
+      t.start();
+    } catch (java.net.URISyntaxException e) {
+      vlog.info(e.getMessage());
+    } catch (java.lang.Exception e) {
+      vlog.info(e.getMessage());
+    }
   }
 
   public boolean isAppletDragStart(MouseEvent e) {
@@ -320,7 +320,6 @@ public class VncViewer extends javax.swing.JApplet
   }
 
   public void appletDragFinished() {
-    cc.setEmbeddedFeatures(true);
     JFrame f = (JFrame)JOptionPane.getFrameForComponent(this);
     if (f != null)
       f.dispose();
@@ -331,7 +330,6 @@ public class VncViewer extends javax.swing.JApplet
   }
 
   public void appletRestored() {
-    cc.setEmbeddedFeatures(false);
     cc.setCloseListener(null);
   }
 
@@ -361,37 +359,91 @@ public class VncViewer extends javax.swing.JApplet
     }
   }
 
+  public static void showAbout(Container parent) {
+    String pkgDate = "";
+    String pkgTime = "";
+    try {
+      Manifest manifest = new Manifest(VncViewer.timestamp);
+      Attributes attributes = manifest.getMainAttributes();
+      pkgDate = attributes.getValue("Package-Date");
+      pkgTime = attributes.getValue("Package-Time");
+    } catch (java.lang.Exception e) { }
+
+    Window fullScreenWindow = Viewport.getFullScreenWindow();
+    if (fullScreenWindow != null)
+      Viewport.setFullScreenWindow(null);
+    String msg =
+      String.format(VncViewer.aboutText, VncViewer.version, VncViewer.build,
+                    VncViewer.buildDate, VncViewer.buildTime);
+    Object[] options = {"Close  \u21B5"};
+    JOptionPane op =
+      new JOptionPane(msg, JOptionPane.INFORMATION_MESSAGE,
+                      JOptionPane.DEFAULT_OPTION, VncViewer.logoIcon, options);
+    JDialog dlg = op.createDialog(parent, "About TigerVNC Viewer for Java");
+    dlg.setIconImage(VncViewer.frameIcon);
+    dlg.setAlwaysOnTop(true);
+    dlg.setVisible(true);
+    if (fullScreenWindow != null)
+      Viewport.setFullScreenWindow(fullScreenWindow);
+  }
+
   public void start() {
     vlog.debug("start called");
     getTimestamp();
-    if (embed.getValue() && nViewers == 0) {
+    if (embed.getValue()) {
+      setupEmbeddedFrame();
       alwaysShowServerDialog.setParam(false);
-      Configuration.global().readAppletParams(this);
+      String servername = loadAppletParameters(this);
+      vncServerName.setParam(servername);
       fullScreen.setParam(false);
       scalingFactor.setParam("100");
-      String host = getCodeBase().getHost();
-      if (vncServerName.getValue() == null && vncServerPort.getValue() != 0) {
-        int port = vncServerPort.getValue();
-        vncServerName.setParam(host + ((port >= 5900 && port <= 5999)
-                                       ? (":"+(port-5900))
-                                       : ("::"+port)));
-      }
     }
-    nViewers++;
     thread = new Thread(this);
     thread.start();
   }
 
   public void exit(int n) {
-    nViewers--;
-    if (nViewers > 0)
-      return;
     if (embed.getValue())
       destroy();
     else
       System.exit(n);
   }
 
+  private void setupEmbeddedFrame() {
+    UIManager.getDefaults().put("ScrollPane.ancestorInputMap",
+      new UIDefaults.LazyInputMap(new Object[]{}));
+    sp = new JScrollPane();
+    sp.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
+    sp.getViewport().setBackground(Color.BLACK);
+    InputMap im = sp.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
+    int ctrlAltShiftMask = Event.SHIFT_MASK | Event.CTRL_MASK | Event.ALT_MASK;
+    if (im != null) {
+      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, ctrlAltShiftMask),
+             "unitScrollUp");
+      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, ctrlAltShiftMask),
+             "unitScrollDown");
+      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, ctrlAltShiftMask),
+             "unitScrollLeft");
+      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, ctrlAltShiftMask),
+             "unitScrollRight");
+      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, ctrlAltShiftMask),
+             "scrollUp");
+      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, ctrlAltShiftMask),
+             "scrollDown");
+      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, ctrlAltShiftMask),
+             "scrollLeft");
+      im.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, ctrlAltShiftMask),
+             "scrollRight");
+    }
+    sp.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
+    sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
+    add(sp);
+  }
+
+  public static JViewport getViewport() {
+    return sp.getViewport();
+  }
+
   // If "Reconnect" button is pressed
   public void actionPerformed(ActionEvent e) {
     getContentPane().removeAll();
@@ -425,14 +477,13 @@ public class VncViewer extends javax.swing.JApplet
     }
   }
 
-  CConn cc;
   public void run() {
     cc = null;
 
     if (listenMode.getValue()) {
       int port = 5500;
 
-      if (vncServerName.getValue() != null &&
+      if (!vncServerName.getValue().isEmpty() &&
           Character.isDigit(vncServerName.getValue().charAt(0)))
         port = Integer.parseInt(vncServerName.getValue());
 
@@ -446,15 +497,26 @@ public class VncViewer extends javax.swing.JApplet
 
       vlog.info("Listening on port "+port);
 
-      while (true) {
-        Socket new_sock = listener.accept();
-        if (new_sock != null)
-          newViewer(this, new_sock, true);
+      while (sock == null)
+        sock = listener.accept();
+    } else {
+      if (alwaysShowServerDialog.getValue() || sock == null) {
+        if (vncServerName.getValue().isEmpty()) {
+          try {
+            SwingUtilities.invokeAndWait(new ServerDialog());
+          } catch (InvocationTargetException e) {
+            reportException(e);
+          } catch (InterruptedException e) {
+            reportException(e);
+          }
+          if (vncServerName.getValue().isEmpty())
+            exit(0);
+        }
       }
     }
 
     try {
-      cc = new CConn(this, sock, vncServerName.getValue());
+      cc = new CConn(vncServerName.getValue(), sock);
       while (!cc.shuttingDown)
         cc.processMsg();
       exit(0);
@@ -471,243 +533,13 @@ public class VncViewer extends javax.swing.JApplet
     }
   }
 
-  static BoolParameter noLionFS
-  = new BoolParameter("NoLionFS",
-  "On Mac systems, setting this parameter will force the use of the old "+
-  "(pre-Lion) full-screen mode, even if the viewer is running on OS X 10.7 "+
-  "Lion or later.",
-  false);
-
-  BoolParameter embed
-  = new BoolParameter("Embed",
-  "If the viewer is being run as an applet, display its output to " +
-  "an embedded frame in the browser window rather than to a dedicated " +
-  "window. Embed=1 implies FullScreen=0 and Scale=100.",
-  false);
-
-  BoolParameter useLocalCursor
-  = new BoolParameter("UseLocalCursor",
-                      "Render the mouse cursor locally",
-                      true);
-  BoolParameter sendLocalUsername
-  = new BoolParameter("SendLocalUsername",
-                      "Send the local username for SecurityTypes "+
-                      "such as Plain rather than prompting",
-                      true);
-  StringParameter passwordFile
-  = new StringParameter("PasswordFile",
-                        "Password file for VNC authentication",
-                        "");
-  AliasParameter passwd
-  = new AliasParameter("passwd",
-                       "Alias for PasswordFile",
-                       passwordFile);
-  BoolParameter autoSelect
-  = new BoolParameter("AutoSelect",
-                      "Auto select pixel format and encoding",
-                      true);
-  BoolParameter fullColour
-  = new BoolParameter("FullColour",
-                      "Use full colour - otherwise 6-bit colour is "+
-                      "used until AutoSelect decides the link is "+
-                      "fast enough",
-                      true);
-  AliasParameter fullColourAlias
-  = new AliasParameter("FullColor",
-                       "Alias for FullColour",
-                       fullColour);
-  IntParameter lowColourLevel
-  = new IntParameter("LowColorLevel",
-                     "Color level to use on slow connections. "+
-                     "0 = Very Low (8 colors), 1 = Low (64 colors), "+
-                     "2 = Medium (256 colors)",
-                     2);
-  AliasParameter lowColourLevelAlias
-  = new AliasParameter("LowColourLevel",
-                       "Alias for LowColorLevel",
-                       lowColourLevel);
-  StringParameter preferredEncoding
-  = new StringParameter("PreferredEncoding",
-                        "Preferred encoding to use (Tight, ZRLE, "+
-                        "hextile or raw) - implies AutoSelect=0",
-                        "Tight");
-  BoolParameter viewOnly
-  = new BoolParameter("ViewOnly",
-                      "Don't send any mouse or keyboard events to "+
-                      "the server",
-                      false);
-  BoolParameter shared
-  = new BoolParameter("Shared",
-                      "Don't disconnect other viewers upon "+
-                      "connection - share the desktop instead",
-                      false);
-  BoolParameter fullScreen
-  = new BoolParameter("FullScreen",
-                      "Full Screen Mode",
-                      false);
-  BoolParameter fullScreenAllMonitors
-  = new BoolParameter("FullScreenAllMonitors",
-                      "Enable full screen over all monitors",
-                      true);
-  BoolParameter acceptClipboard
-  = new BoolParameter("AcceptClipboard",
-                      "Accept clipboard changes from the server",
-                      true);
-  BoolParameter sendClipboard
-  = new BoolParameter("SendClipboard",
-                      "Send clipboard changes to the server",
-                      true);
-  static IntParameter maxCutText
-  = new IntParameter("MaxCutText",
-                     "Maximum permitted length of an outgoing clipboard update",
-                     262144);
-  StringParameter menuKey
-  = new StringParameter("MenuKey",
-                        "The key which brings up the popup menu",
-                        "F8");
-  StringParameter desktopSize
-  = new StringParameter("DesktopSize",
-                        "Reconfigure desktop size on the server on "+
-                        "connect (if possible)", "");
-  BoolParameter listenMode
-  = new BoolParameter("listen",
-                      "Listen for connections from VNC servers",
-                      false);
-  StringParameter scalingFactor
-  = new StringParameter("ScalingFactor",
-                        "Reduce or enlarge the remote desktop image. "+
-                        "The value is interpreted as a scaling factor "+
-                        "in percent. If the parameter is set to "+
-                        "\"Auto\", then automatic scaling is "+
-                        "performed. Auto-scaling tries to choose a "+
-                        "scaling factor in such a way that the whole "+
-                        "remote desktop will fit on the local screen. "+
-                        "If the parameter is set to \"FixedRatio\", "+
-                        "then automatic scaling is performed, but the "+
-                        "original aspect ratio is preserved.",
-                        "100");
-  BoolParameter alwaysShowServerDialog
-  = new BoolParameter("AlwaysShowServerDialog",
-                      "Always show the server dialog even if a server "+
-                      "has been specified in an applet parameter or on "+
-                      "the command line",
-                      false);
-  StringParameter vncServerName
-  = new StringParameter("Server",
-                        "The VNC server <host>[:<dpyNum>] or "+
-                        "<host>::<port>",
-                        null);
-  IntParameter vncServerPort
-  = new IntParameter("Port",
-                     "The VNC server's port number, assuming it is on "+
-                     "the host from which the applet was downloaded",
-                     0);
-  BoolParameter acceptBell
-  = new BoolParameter("AcceptBell",
-                      "Produce a system beep when requested to by the server.",
-                      true);
-  StringParameter via
-  = new StringParameter("Via",
-    "Automatically create an encrypted TCP tunnel to "+
-    "the gateway machine, then connect to the VNC host "+
-    "through that tunnel. By default, this option invokes "+
-    "SSH local port forwarding using the embedded JSch "+
-    "client, however an external SSH client may be specified "+
-    "using the \"-extSSH\" parameter. Note that when using "+
-    "the -via option, the VNC host machine name should be "+
-    "specified from the point of view of the gateway machine, "+
-    "e.g. \"localhost\" denotes the gateway, "+
-    "not the machine on which the viewer was launched. "+
-    "See the System Properties section below for "+
-    "information on configuring the -Via option.", null);
-  BoolParameter tunnel
-  = new BoolParameter("Tunnel",
-    "The -Tunnel command is basically a shorthand for the "+
-    "-via command when the VNC server and SSH gateway are "+
-    "one and the same. -Tunnel creates an SSH connection "+
-    "to the server and forwards the VNC through the tunnel "+
-    "without the need to specify anything else.", false);
-  BoolParameter extSSH
-  = new BoolParameter("extSSH",
-    "By default, SSH tunneling uses the embedded JSch client "+
-    "for tunnel creation. This option causes the client to "+
-    "invoke an external SSH client application for all tunneling "+
-    "operations. By default, \"/usr/bin/ssh\" is used, however "+
-    "the path to the external application may be specified using "+
-    "the -SSHClient option.", false);
-  StringParameter extSSHClient
-  = new StringParameter("extSSHClient",
-    "Specifies the path to an external SSH client application "+
-    "that is to be used for tunneling operations when the -extSSH "+
-    "option is in effect.", "/usr/bin/ssh");
-  StringParameter extSSHArgs
-  = new StringParameter("extSSHArgs",
-    "Specifies the arguments string or command template to be used "+
-    "by the external SSH client application when the -extSSH option "+
-    "is in effect. The string will be processed according to the same "+
-    "pattern substitution rules as the VNC_TUNNEL_CMD and VNC_VIA_CMD "+
-    "system properties, and can be used to override those in a more "+
-    "command-line friendly way. If not specified, then the appropriate "+
-    "VNC_TUNNEL_CMD or VNC_VIA_CMD command template will be used.", null);
-  StringParameter sshConfig
-  = new StringParameter("SSHConfig",
-    "Specifies the path to an OpenSSH configuration file that to "+
-    "be parsed by the embedded JSch SSH client during tunneling "+
-    "operations.", FileUtils.getHomeDir()+".ssh/config");
-  StringParameter sshKey
-  = new StringParameter("SSHKey",
-    "When using the Via or Tunnel options with the embedded SSH client, "+
-    "this parameter specifies the text of the SSH private key to use when "+
-    "authenticating with the SSH server. You can use \\n within the string "+
-    "to specify a new line.", null);
-  StringParameter sshKeyFile
-  = new StringParameter("SSHKeyFile",
-    "When using the Via or Tunnel options with the embedded SSH client, "+
-    "this parameter specifies a file that contains an SSH private key "+
-    "(or keys) to use when authenticating with the SSH server. If not "+
-    "specified, ~/.ssh/id_dsa or ~/.ssh/id_rsa will be used (if they exist). "+
-    "Otherwise, the client will fallback to prompting for an SSH password.",
-    null);
-  StringParameter sshKeyPass
-  = new StringParameter("SSHKeyPass",
-    "When using the Via or Tunnel options with the embedded SSH client, "+
-    "this parameter specifies the passphrase for the SSH key.", null);
-  BoolParameter customCompressLevel
-  = new BoolParameter("CustomCompressLevel",
-                      "Use custom compression level. "+
-                      "Default if CompressLevel is specified.",
-                      false);
-  IntParameter compressLevel
-  = new IntParameter("CompressLevel",
-                     "Use specified compression level "+
-                     "0 = Low, 6 = High",
-                     1);
-  BoolParameter noJpeg
-  = new BoolParameter("NoJPEG",
-                      "Disable lossy JPEG compression in Tight encoding.",
-                      false);
-  IntParameter qualityLevel
-  = new IntParameter("QualityLevel",
-                     "JPEG quality level. "+
-                     "0 = Low, 9 = High",
-                     8);
-  StringParameter x509ca
-  = new StringParameter("X509CA",
-    "Path to CA certificate to use when authenticating remote servers "+
-    "using any of the X509 security schemes (X509None, X509Vnc, etc.). "+
-    "Must be in PEM format.",
-    FileUtils.getHomeDir()+".vnc/x509_ca.pem");
-  StringParameter x509crl
-  = new StringParameter("X509CRL",
-    "Path to certificate revocation list to use in conjunction with "+
-    "-X509CA. Must also be in PEM format.",
-    FileUtils.getHomeDir()+".vnc/x509_crl.pem");
-  StringParameter config
-  = new StringParameter("config",
+  public static CConn cc;
+  private static JScrollPane sp;
+  public static StringParameter config
+  = new StringParameter("Config",
   "Specifies a configuration file to load.", null);
 
   Thread thread;
   Socket sock;
-  static int nViewers;
   static LogWriter vlog = new LogWriter("VncViewer");
 }