]> source.dussan.org Git - tigervnc.git/commitdiff
Various fixes to Java viewer UI
authorBrian P. Hinz <bphinz@users.sf.net>
Fri, 18 Mar 2016 02:34:52 +0000 (22:34 -0400)
committerBrian P. Hinz <bphinz@users.sf.net>
Fri, 18 Mar 2016 02:34:52 +0000 (22:34 -0400)
Significant improvements to the Java viewer UI to
make it more robust and tolerant of new tabs, fonts,
etc.  TextFields and editable ComboBoxes that are
specific to integer values now only accept integer
input.

java/com/tigervnc/vncviewer/CConn.java
java/com/tigervnc/vncviewer/ClipboardDialog.java
java/com/tigervnc/vncviewer/Dialog.java
java/com/tigervnc/vncviewer/OptionsDialog.java
java/com/tigervnc/vncviewer/PasswdDialog.java
java/com/tigervnc/vncviewer/ServerDialog.java
java/com/tigervnc/vncviewer/VncViewer.java

index 9a7947379da0ec323539e435d0bc6eda57aa311b..dbb2a2935401434d40454928915485eb00ab0aa4 100644 (file)
@@ -39,6 +39,7 @@ import java.awt.event.*;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.util.jar.Attributes;
@@ -884,8 +885,10 @@ public class CConn extends CConnection implements
       options.encNone.setEnabled(false);
       options.encTLS.setEnabled(false);
       options.encX509.setEnabled(false);
-      options.ca.setEnabled(false);
-      options.crl.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);
@@ -966,11 +969,23 @@ public class CConn extends CConnection implements
           }
         }
       }
+      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.ca.setEnabled(options.secVeNCrypt.isSelected());
-      options.crl.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()||
@@ -1057,6 +1072,10 @@ public class CConn extends CConnection implements
       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());
index e7aa7e96be222b3dfa76e463f66788687b69e620..441846cff792156008d835feb387c7444680d848 100644 (file)
@@ -30,7 +30,7 @@ import javax.swing.text.*;
 
 import com.tigervnc.rfb.LogWriter;
 
-class ClipboardDialog extends Dialog implements ActionListener {
+class ClipboardDialog extends Dialog {
 
   private class VncTransferHandler extends TransferHandler {
     // Custom TransferHandler designed to limit the size of outbound
@@ -109,14 +109,12 @@ class ClipboardDialog extends Dialog implements ActionListener {
     JPanel pb = new JPanel();
     clearButton = new JButton("Clear");
     pb.add(clearButton);
-    clearButton.addActionListener(this);
     sendButton = new JButton("Send to VNC server");
     pb.add(sendButton);
-    sendButton.addActionListener(this);
     cancelButton = new JButton("Cancel");
     pb.add(cancelButton);
-    cancelButton.addActionListener(this);
     getContentPane().add("South", pb);
+    addListeners(this);
     pack();
   }
 
index 4b82eb933bef03c26a70302128f817e4484a1fc3..3d24619b20522942cda2670b75285a57dccba3c2 100644 (file)
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * Copyright (C) 2011-2014 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
@@ -30,9 +30,18 @@ package com.tigervnc.vncviewer;
 
 import java.awt.*;
 import java.awt.Dialog.*;
+import java.awt.event.*;
 import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.text.*;
 
-class Dialog extends JDialog {
+class Dialog extends JDialog implements ActionListener,
+                                        ItemListener,
+                                        KeyListener {
+
+  // GridBag weights
+  static double HEAVY = 1.0;
+  static double LIGHT = 0.0;
 
   public Dialog(boolean modal) {
     setIconImage(VncViewer.frameIcon);
@@ -78,30 +87,98 @@ class Dialog extends JDialog {
 
   // initDialog() can be overridden in a derived class.  Typically it is used
   // to make sure that checkboxes have the right state, etc.
-  public void initDialog() {
+  public void initDialog() { }
+
+  public void actionPerformed(ActionEvent e) { }
+  public void itemStateChanged(ItemEvent e) { }
+  public void keyTyped(KeyEvent event) { }
+  public void keyReleased(KeyEvent event) { }
+  public void keyPressed(KeyEvent event) { }
+
+  protected void addListeners(Container c) {
+    for (Component ch : c.getComponents()) {
+      if (ch instanceof JCheckBox)
+        ((JCheckBox)ch).addItemListener(this);
+      else if (ch instanceof JButton)
+        ((JButton)ch).addActionListener(this);
+      else if (ch instanceof JComboBox)
+        ((JComboBox)ch).addActionListener(this);
+      else if (ch instanceof JTextField)
+        ((JTextField)ch).addKeyListener(this);
+      else if (ch instanceof Container)
+        addListeners((Container)ch);
+    }
+  }
+
+  public int getButtonLabelInset(AbstractButton b) {
+    // Aligning components vertically to the label of
+    // a JCheckbox is absurdly difficult.  JCheckBox's
+    // getIcon() method generally returns null, so we 
+    // have to resort to querying the UIManager in 
+    // order to determine the width of the checkbox.
+    // The default values are based on Nimbus.
+    int width = 18;
+    int gap = 4;
+
+    Icon ico = b.getIcon();
+    if (ico == null) {
+      if (b instanceof JCheckBox)
+        ico = (Icon)UIManager.get("CheckBox.icon");
+      else if (b instanceof JRadioButton)
+        ico = (Icon)UIManager.get("RadioButton.icon");
+    }
+    if (ico != null)
+      width = Math.max(width, ico.getIconWidth());
+    if (b != null)
+      gap = Math.max(gap, b.getIconTextGap());
+
+    return width + gap;
   }
 
-  public void addGBComponent(JComponent c, JComponent cp,
-                             int gx, int gy,
-                             int gw, int gh,
-                             int gipx, int gipy,
-                             double gwx, double gwy,
-                             int fill, int anchor,
-                             Insets insets)
-  {
-      GridBagConstraints gbc = new GridBagConstraints();
-      gbc.anchor = anchor;
-      gbc.fill = fill;
-      gbc.gridx = gx;
-      gbc.gridy = gy;
-      gbc.gridwidth = gw;
-      gbc.gridheight = gh;
-      gbc.insets = insets;
-      gbc.ipadx = gipx;
-      gbc.ipady = gipy;
-      gbc.weightx = gwx;
-      gbc.weighty = gwy;
-      cp.add(c, gbc);
+  protected class GroupedJRadioButton extends JRadioButton {
+    public GroupedJRadioButton(String l, ButtonGroup g, JComponent c) {
+      super(l);
+      c.add(this);
+      if (g != null)
+        g.add(this);
+    }
+  }
+
+  protected class MyJComboBox extends JComboBox {
+    public MyJComboBox(Object[] items) {
+      super(items);
+      // Hack to set the left inset on editable JComboBox
+      if (UIManager.getLookAndFeel().getID().equals("Windows")) {
+        this.setBorder(BorderFactory.createCompoundBorder(this.getBorder(),
+          BorderFactory.createEmptyBorder(0,1,0,0)));
+      } else if (UIManager.getLookAndFeel().getID().equals("Metal")) {
+        ComboBoxEditor editor = this.getEditor();
+        JTextField jtf = (JTextField)editor.getEditorComponent();
+        jtf.setBorder(new CompoundBorder(jtf.getBorder(), new EmptyBorder(0,2,0,0)));
+      }
+    }
+
+    public MyJComboBox() {
+      new MyJComboBox(null);
+    }
+
+    @Override
+    public void setPrototypeDisplayValue(Object prototypeDisplayValue) {
+      // Even with setPrototypeDisplayValue set JComboxBox resizes 
+      // itself when setEditable(true) is called.
+      super.setPrototypeDisplayValue(prototypeDisplayValue);
+      boolean e = isEditable();
+      setEditable(false);
+      Dimension d = getPreferredSize();
+      setPreferredSize(d);
+      setEditable(e);
+    }
+
+    public void setDocument(PlainDocument doc) {
+      ComboBoxEditor editor = this.getEditor();
+      JTextField jtf = (JTextField)editor.getEditorComponent();
+      jtf.setDocument(doc);
+    }
   }
 
   private Window fullScreenWindow;
index e2cd7c8b682bd37b24f8efa7bd099a0ee9ac9f12..3bd74e277626f0d04994acc9b3c77ea0808c9923 100644 (file)
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  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
@@ -26,18 +26,58 @@ import java.text.Format;
 import java.text.NumberFormat;
 import javax.swing.*;
 import javax.swing.border.*;
+import javax.swing.UIManager.*;
+import javax.swing.text.*;
+import java.util.*;
+import java.util.Map.Entry;
+
 
 import com.tigervnc.rfb.*;
 
-class OptionsDialog extends Dialog implements
-                            ActionListener,
-                            ItemListener
-{
+import static java.awt.GridBagConstraints.BOTH;
+import static java.awt.GridBagConstraints.CENTER;
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.LINE_END;
+import static java.awt.GridBagConstraints.LINE_START;
+import static java.awt.GridBagConstraints.PAGE_START;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.RELATIVE;
+import static java.awt.GridBagConstraints.REMAINDER;
+import static java.awt.GridBagConstraints.VERTICAL;
+
+class OptionsDialog extends Dialog {
+
+  private class IntegerDocument extends PlainDocument {
+    private int limit;
+
+    IntegerDocument(int limit) {
+      super();
+      this.limit = limit;
+    }
+
+    public void insertString(int offset, String  str, AttributeSet a)
+          throws BadLocationException {
+      if (str == null || !str.matches("^[0-9]+$")) return;
+      if ((getLength() + str.length()) > limit)
+        java.awt.Toolkit.getDefaultToolkit().beep();
+      else
+        super.insertString(offset, str, a);
+    }
+  }
 
   private class IntegerTextField extends JFormattedTextField {
-    public IntegerTextField(Format format) {
-      super(format);
+    public IntegerTextField(int digits) {
+      super();
+      this.setDocument(new IntegerDocument(digits));
+      Font f = getFont();
+      String template = String.format("%0"+digits+"d", 0);
+      int w = getFontMetrics(f).stringWidth(template) +
+              getMargin().left + getMargin().right + 
+              getInsets().left + getInsets().right;
+      int h = getPreferredSize().height;
+      setPreferredSize(new Dimension(w, h));
     }
+
     @Override
     protected void processFocusEvent(final FocusEvent e) {
       if (e.isTemporary())
@@ -50,277 +90,603 @@ class OptionsDialog extends Dialog implements
   }
 
   // Constants
-  // Static variables
   static LogWriter vlog = new LogWriter("OptionsDialog");
 
   CConn cc;
-  JPanel FormatPanel, InputsPanel, MiscPanel, DefaultsPanel, SecPanel, ScreenPanel;
-  JCheckBox autoSelect, customCompressLevel, noJpeg;
   @SuppressWarnings({"rawtypes"})
   JComboBox menuKey, compressLevel, qualityLevel, scalingFactor;
   ButtonGroup encodingGroup, colourGroup;
-  JRadioButton zrle, hextile, tight, raw;
-  JRadioButton fullColour, mediumColour, lowColour, veryLowColour;
-  JCheckBox viewOnly, acceptClipboard, sendClipboard, acceptBell;
-  JCheckBox desktopSize, fullScreen, fullScreenAllMonitors, shared, useLocalCursor;
-  JCheckBox secVeNCrypt, encNone, encTLS, encX509;
-  JCheckBox secNone, secVnc, secPlain, secIdent, sendLocalUsername;
-  JButton okButton, cancelButton;
-  JButton ca, crl;
-  JButton cfLoadButton, cfSaveAsButton, defSaveButton, defReloadButton, defClearButton;
-  JTextField desktopWidth, desktopHeight;
+  JRadioButton zrle, hextile, tight, raw, fullColour, mediumColour,
+               lowColour, veryLowColour;
+  JCheckBox autoSelect, customCompressLevel, noJpeg, viewOnly,
+            acceptClipboard, sendClipboard, acceptBell, desktopSize,
+            fullScreen, fullScreenAllMonitors, shared, useLocalCursor,
+            secVeNCrypt, encNone, encTLS, encX509, secNone, secVnc,
+            secPlain, secIdent, sendLocalUsername;
+  JButton okButton, cancelButton, caButton, crlButton, cfLoadButton,
+          cfSaveAsButton, defSaveButton, defReloadButton, defClearButton;
+  JTextField desktopWidth, desktopHeight, x509ca, x509crl;
+  JTabbedPane tabPane;
 
   @SuppressWarnings({"rawtypes","unchecked"})
   public OptionsDialog(CConn cc_) {
     super(true);
     cc = cc_;
-    setResizable(false);
     setTitle("VNC Viewer Options");
+    setResizable(false);
 
     getContentPane().setLayout(
       new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));
 
-    JTabbedPane tabPane = new JTabbedPane();
+    tabPane = new JTabbedPane();
     tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
 
-    ButtonGroup encodingGroup = new ButtonGroup();
-    ButtonGroup colourGroup = new ButtonGroup();
+    encodingGroup = new ButtonGroup();
+    colourGroup = new ButtonGroup();
+    int indent = 0;
 
     // Compression tab
-    FormatPanel=new JPanel(new GridBagLayout());
-
+    JPanel FormatPanel = new JPanel();
+    FormatPanel.setLayout(new BoxLayout(FormatPanel,
+                                        BoxLayout.PAGE_AXIS));
+    FormatPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
+
+    JPanel autoSelectPane = new JPanel();
+    autoSelectPane.setLayout(new BoxLayout(autoSelectPane,
+                                           BoxLayout.LINE_AXIS));
+    autoSelectPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
     autoSelect = new JCheckBox("Auto Select");
-    autoSelect.addItemListener(this);
-
-    JPanel encodingPanel = new JPanel(new GridBagLayout());
-    encodingPanel.setBorder(BorderFactory.createTitledBorder("Preferred encoding"));
-    tight = addRadioCheckbox("Tight", encodingGroup, encodingPanel);
-    zrle = addRadioCheckbox("ZRLE", encodingGroup, encodingPanel);
-    hextile = addRadioCheckbox("Hextile", encodingGroup, encodingPanel);
-    raw = addRadioCheckbox("Raw", encodingGroup, encodingPanel);
+    autoSelectPane.add(autoSelect);
+    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);
+
+    JPanel encodingPane = new JPanel(new GridLayout(1, 2, 5, 0));
+    encodingPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
+    encodingPane.add(encodingPanel);
+    encodingPane.add(colourPanel);
 
     JPanel tightPanel = new JPanel(new GridBagLayout());
     customCompressLevel = new JCheckBox("Custom Compression Level");
-    customCompressLevel.addItemListener(this);
     Object[] compressionLevels = { 1, 2, 3, 4, 5, 6 };
-    compressLevel  = new JComboBox(compressionLevels);
-    JLabel compressionLabel = new JLabel("Level (1=fast, 6=best [4-6 are rarely useful])");
+    compressLevel  = new MyJComboBox(compressionLevels);
+    ((MyJComboBox)compressLevel).setDocument(new IntegerDocument(1));
+    compressLevel.setPrototypeDisplayValue("0.");
+    compressLevel.setEditable(true);
+    JLabel compressionLabel =
+      new JLabel("Level (1=fast, 6=best [4-6 are rarely useful])");
     noJpeg = new JCheckBox("Allow JPEG Compression");
-    noJpeg.addItemListener(this);
     Object[] qualityLevels = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
-    qualityLevel  = new JComboBox(qualityLevels);
+    qualityLevel  = new MyJComboBox(qualityLevels);
+    qualityLevel.setPrototypeDisplayValue("0.");
     JLabel qualityLabel = new JLabel("Quality (0=poor, 9=best)");
-    // Hack to set the left inset on editable JComboBox
-    if (UIManager.getLookAndFeel().getID() == "Windows") {
-      compressLevel.setBorder(BorderFactory.createCompoundBorder(compressLevel.getBorder(),
-        BorderFactory.createEmptyBorder(0,1,0,0)));
-    } else if (UIManager.getLookAndFeel().getID() == "Metal") {
-      ComboBoxEditor editor = compressLevel.getEditor();
-      JTextField jtf = (JTextField)editor.getEditorComponent();
-      jtf.setBorder(new CompoundBorder(jtf.getBorder(), new EmptyBorder(0,2,0,0)));
-    }
-    Dimension size = compressLevel.getPreferredSize();
-    compressLevel.setEditable(true);
-    compressLevel.setPreferredSize(size);
-    addGBComponent(customCompressLevel, tightPanel, 0, 0, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,0,0,0));
-    addGBComponent(compressLevel, tightPanel,       0, 1, 1, 1, 2, 2, 0, 0, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(0,20,0,0));
-    addGBComponent(compressionLabel, tightPanel,    1, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,5,0,0));
-    addGBComponent(noJpeg, tightPanel,              0, 2, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(4,0,0,0));
-    addGBComponent(qualityLevel, tightPanel,        0, 3, 1, 1, 2, 2, 0, 0, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(0,20,0,0));
-    addGBComponent(qualityLabel, tightPanel,        1, 3, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,5,0,0));
 
+    tightPanel.add(customCompressLevel,
+                   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,
+                   new GridBagConstraints(0, 1,
+                                          1, 1,
+                                          LIGHT, LIGHT,
+                                          LINE_START, NONE,
+                                          new Insets(0, indent, 0, 0),
+                                          NONE, NONE));
+    tightPanel.add(compressionLabel,
+                   new GridBagConstraints(1, 1,
+                                          1, 1,
+                                          HEAVY, LIGHT,
+                                          LINE_START, HORIZONTAL,
+                                          new Insets(0, 5, 0, 0),
+                                          NONE, NONE));
+    tightPanel.add(noJpeg,
+                   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,
+                   new GridBagConstraints(0, 3,
+                                          1, 1,
+                                          LIGHT, LIGHT,
+                                          LINE_START, NONE,
+                                          new Insets(0, indent, 0, 0),
+                                          NONE, NONE));
+    tightPanel.add(qualityLabel,
+                   new GridBagConstraints(1, 3,
+                                          1, 1,
+                                          HEAVY, NONE,
+                                          LINE_START, HORIZONTAL,
+                                          new Insets(0, 5, 0, 0),
+                                          NONE, NONE));
+    tightPanel.add(Box.createRigidArea(new Dimension(5,0)),
+                   new GridBagConstraints(0, 4,
+                                          REMAINDER, REMAINDER,
+                                          HEAVY, HEAVY,
+                                          LINE_START, BOTH,
+                                          new Insets(0, 0, 0, 0),
+                                          NONE, NONE));
+    FormatPanel.add(autoSelectPane);
+    FormatPanel.add(encodingPane);
+    FormatPanel.add(tightPanel);
 
-    JPanel colourPanel = new JPanel(new GridBagLayout());
-    colourPanel.setBorder(BorderFactory.createTitledBorder("Color level"));
-    fullColour = addRadioCheckbox("Full (all available colors)", colourGroup, colourPanel);
-    mediumColour = addRadioCheckbox("Medium (256 colors)", colourGroup, colourPanel);
-    lowColour = addRadioCheckbox("Low (64 colours)", colourGroup, colourPanel);
-    veryLowColour = addRadioCheckbox("Very low(8 colors)", colourGroup, colourPanel);
+    // security tab
+    JPanel SecPanel = new JPanel();
+    SecPanel.setLayout(new BoxLayout(SecPanel,
+                                     BoxLayout.PAGE_AXIS));
+    SecPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
+
+    JPanel vencryptPane = new JPanel();
+    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");
+    JLabel caLabel = new JLabel("X.509 CA Certificate");
+    x509ca = new JTextField();
+    x509ca.setName(Configuration.getParam("x509ca").getName());
+    caButton = new JButton("Browse");
+    JLabel crlLabel = new JLabel("X.509 CRL file");
+    x509crl = new JTextField();
+    x509crl.setName(Configuration.getParam("x509crl").getName());
+    crlButton = new JButton("Browse");
+    encrPanel.add(encNone,
+                  new GridBagConstraints(0, 0,
+                                         REMAINDER, 1,
+                                         HEAVY, LIGHT,
+                                         LINE_START, NONE,
+                                         new Insets(0, 0, 4, 0),
+                                         NONE, NONE));
+    encrPanel.add(encTLS,
+                  new GridBagConstraints(0, 1,
+                                         REMAINDER, 1,
+                                         HEAVY, LIGHT,
+                                         LINE_START, NONE,
+                                         new Insets(0, 0, 4, 0),
+                                         NONE, NONE));
+    encrPanel.add(encX509,
+                  new GridBagConstraints(0, 2,
+                                         3, 1,
+                                         HEAVY, LIGHT,
+                                         LINE_START, NONE,
+                                         new Insets(0, 0, 0, 0),
+                                         NONE, NONE));
+    indent = getButtonLabelInset(encX509);
+    encrPanel.add(caLabel,
+                  new GridBagConstraints(0, 3,
+                                         1, 1,
+                                         LIGHT, LIGHT,
+                                         LINE_END, NONE,
+                                         new Insets(0, indent, 5, 0),
+                                         0, 0));
+    encrPanel.add(x509ca,
+                  new GridBagConstraints(1, 3,
+                                         1, 1,
+                                         HEAVY, LIGHT,
+                                         LINE_START, HORIZONTAL,
+                                         new Insets(0, 5, 5, 0),
+                                         0, 0));
+    encrPanel.add(caButton,
+                  new GridBagConstraints(2, 3,
+                                         1, 1,
+                                         LIGHT, LIGHT,
+                                         LINE_START, VERTICAL,
+                                         new Insets(0, 5, 5, 0),
+                                         0, 0));
+    encrPanel.add(crlLabel,
+                  new GridBagConstraints(0, 4,
+                                         1, 1,
+                                         LIGHT, LIGHT,
+                                         LINE_END, NONE,
+                                         new Insets(0, indent, 0, 0),
+                                         0, 0));
+    encrPanel.add(x509crl,
+                  new GridBagConstraints(1, 4,
+                                         1, 1,
+                                         HEAVY, LIGHT,
+                                         LINE_START, HORIZONTAL,
+                                         new Insets(0, 5, 0, 0),
+                                         0, 0));
+    encrPanel.add(crlButton,
+                  new GridBagConstraints(2, 4,
+                                         1, 1,
+                                         LIGHT, LIGHT,
+                                         LINE_START, VERTICAL,
+                                         new Insets(0, 5, 0, 0),
+                                         0, 0));
 
-    addGBComponent(autoSelect,FormatPanel,    0, 0, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(4,5,0,5));
-    addGBComponent(encodingPanel,FormatPanel, 0, 1, 1, 1, 2, 2, 3, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,5,0,5));
-    addGBComponent(colourPanel,FormatPanel,   1, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_END, new Insets(0,0,0,5));
-    addGBComponent(tightPanel,FormatPanel,    0, 2, 2, GridBagConstraints.REMAINDER, 2, 2, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(0,5,0,5));
+    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,
+                  new GridBagConstraints(0, 0,
+                                         REMAINDER, 1,
+                                         LIGHT, LIGHT,
+                                         LINE_START, NONE,
+                                         new Insets(0, 0, 4, 0),
+                                         NONE, NONE));
+    authPanel.add(secVnc,
+                  new GridBagConstraints(0, 1,
+                                         REMAINDER, 1,
+                                         LIGHT, LIGHT,
+                                         LINE_START, NONE,
+                                         new Insets(0, 0, 4, 0),
+                                         NONE, NONE));
+    authPanel.add(secPlain,
+                  new GridBagConstraints(0, 2,
+                                         1, 1,
+                                         LIGHT, LIGHT,
+                                         LINE_START, NONE,
+                                         new Insets(0, 0, 2, 0),
+                                         NONE, NONE));
+    authPanel.add(secIdent,
+                  new GridBagConstraints(0, 3,
+                                         1, 1,
+                                         LIGHT, LIGHT,
+                                         LINE_START, NONE,
+                                         new Insets(2, 0, 0, 0),
+                                         NONE, NONE));
+    authPanel.add(sendLocalUsername,
+                  new GridBagConstraints(1, 2,
+                                         1, 2,
+                                         HEAVY, LIGHT,
+                                         LINE_START, NONE,
+                                         new Insets(2, 20, 2, 0),
+                                         NONE, NONE));
+
+    SecPanel.add(vencryptPane,
+                 new GridBagConstraints(0, 0,
+                                        REMAINDER, 1,
+                                        LIGHT, LIGHT,
+                                        LINE_START, HORIZONTAL,
+                                        new Insets(0, 0, 4, 0),
+                                        NONE, NONE));
+    SecPanel.add(encrPanel,
+                 new GridBagConstraints(0, 1,
+                                        REMAINDER, 1,
+                                        LIGHT, LIGHT,
+                                        LINE_START, HORIZONTAL,
+                                        new Insets(0, 0, 4, 0),
+                                        NONE, NONE));
+    SecPanel.add(authPanel,
+                 new GridBagConstraints(0, 2,
+                                        REMAINDER, 1,
+                                        LIGHT, LIGHT,
+                                        LINE_START, HORIZONTAL,
+                                        new Insets(0, 0, 4, 0),
+                                        NONE, NONE));
+    SecPanel.add(Box.createRigidArea(new Dimension(0,0)),
+                 new GridBagConstraints(0, RELATIVE,
+                                        REMAINDER, REMAINDER,
+                                        HEAVY, HEAVY,
+                                        LINE_START, BOTH,
+                                        new Insets(0, 0, 0, 0),
+                                        NONE, NONE));
 
     // Input tab
-    InputsPanel=new JPanel(new GridBagLayout());
+    JPanel inputPanel = new JPanel(new GridBagLayout());
+    inputPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
 
     viewOnly = new JCheckBox("View Only (ignore mouse & keyboard)");
-    viewOnly.addItemListener(this);
     acceptClipboard = new JCheckBox("Accept clipboard from server");
-    acceptClipboard.addItemListener(this);
     sendClipboard = new JCheckBox("Send clipboard to server");
-    sendClipboard.addItemListener(this);
     JLabel menuKeyLabel = new JLabel("Menu Key");
     String[] menuKeys = new String[MenuKey.getMenuKeySymbolCount()];
     for (int i = 0; i < MenuKey.getMenuKeySymbolCount(); i++)
       menuKeys[i] = KeyEvent.getKeyText(MenuKey.getMenuKeySymbols()[i].keycode);
     menuKey  = new JComboBox(menuKeys);
-    menuKey.addItemListener(this);
-    addGBComponent(viewOnly,InputsPanel,        0, 0, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,5,0,5));
-    addGBComponent(acceptClipboard,InputsPanel, 0, 1, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,5,0,5));
-    addGBComponent(sendClipboard,InputsPanel,   0, 2, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,5,0,5));
-    addGBComponent(menuKeyLabel,InputsPanel,    0, 3, 1, GridBagConstraints.REMAINDER, 2, 2, 1, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(8,8,0,5));
-    addGBComponent(menuKey,InputsPanel,         1, 3, 1, GridBagConstraints.REMAINDER, 2, 2, 25, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(4,5,0,5));
+
+    inputPanel.add(viewOnly,
+                   new GridBagConstraints(0, 0,
+                                          REMAINDER, 1,
+                                          HEAVY, LIGHT,
+                                          LINE_START, NONE,
+                                          new Insets(0, 0, 4, 0),
+                                          NONE, NONE));
+    inputPanel.add(acceptClipboard,
+                   new GridBagConstraints(0, 1,
+                                          REMAINDER, 1,
+                                          HEAVY, LIGHT,
+                                          LINE_START, NONE,
+                                          new Insets(0, 0, 4, 0),
+                                          NONE, NONE));
+    inputPanel.add(sendClipboard,
+                   new GridBagConstraints(0, 2,
+                                          REMAINDER, 1,
+                                          HEAVY, LIGHT,
+                                          LINE_START, NONE,
+                                          new Insets(0, 0, 4, 0),
+                                          NONE, NONE));
+    inputPanel.add(menuKeyLabel,
+                   new GridBagConstraints(0, 3,
+                                          1, 1,
+                                          LIGHT, LIGHT,
+                                          LINE_START, NONE,
+                                          new Insets(0, 0, 0, 0),
+                                          NONE, NONE));
+    inputPanel.add(menuKey,
+                   new GridBagConstraints(1, 3,
+                                          1, 1,
+                                          HEAVY, LIGHT,
+                                          LINE_START, NONE,
+                                          new Insets(0, 5, 0, 0),
+                                          NONE, NONE));
+    inputPanel.add(Box.createRigidArea(new Dimension(5, 0)),
+                   new GridBagConstraints(0, 4,
+                                          REMAINDER, REMAINDER,
+                                          HEAVY, HEAVY,
+                                          LINE_START, BOTH,
+                                          new Insets(0, 0, 0, 0),
+                                          NONE, NONE));
 
     // Screen tab
-    ScreenPanel=new JPanel(new GridBagLayout());
+    JPanel ScreenPanel = new JPanel(new GridBagLayout());
+    ScreenPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
     desktopSize = new JCheckBox("Resize remote session on connect");
-    desktopSize.addItemListener(this);
     desktopSize.setEnabled(!cc.viewer.embed.getValue() &&
                            (cc.viewer.desktopSize.getValue() != null));
-    NumberFormat format = NumberFormat.getIntegerInstance();
-    format.setMaximumIntegerDigits(5);
-    format.setMinimumIntegerDigits(0);
-    format.setGroupingUsed(false);
-    desktopWidth = new IntegerTextField(format);
-    desktopWidth.setColumns(4);
+    desktopWidth = new IntegerTextField(5);
     desktopWidth.setEnabled(desktopSize.isSelected());
-    desktopHeight = new IntegerTextField(format);
-    desktopHeight.setColumns(4);
+    desktopHeight = new IntegerTextField(5);
     desktopHeight.setEnabled(desktopSize.isSelected());
-    JPanel desktopSizePanel = new JPanel();
+    JPanel desktopSizePanel =
+      new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
     desktopSizePanel.add(desktopWidth);
-    desktopSizePanel.add(new JLabel("x"));
+    desktopSizePanel.add(new JLabel(" x "));
     desktopSizePanel.add(desktopHeight);
     fullScreen = new JCheckBox("Full-screen mode");
-    fullScreen.addItemListener(this);
     fullScreen.setEnabled(!cc.viewer.embed.getValue());
-    fullScreenAllMonitors = new JCheckBox("Enable full-screen mode over all monitors");
-    fullScreenAllMonitors.addItemListener(this);
+    fullScreenAllMonitors =
+      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 JComboBox(scalingFactors);
-    // Hack to set the left inset on editable JComboBox
-    if (UIManager.getLookAndFeel().getID() == "Windows") {
-      scalingFactor.setBorder(BorderFactory.createCompoundBorder(scalingFactor.getBorder(),
-        BorderFactory.createEmptyBorder(0,1,0,0)));
-    } else if (UIManager.getLookAndFeel().getID() == "Metal") {
-      ComboBoxEditor sfe = scalingFactor.getEditor();
-      JTextField sfeTextField = (JTextField)sfe.getEditorComponent();
-      sfeTextField.setBorder(new CompoundBorder(sfeTextField.getBorder(),
-                                                new EmptyBorder(0,2,0,0)));
-    }
+    scalingFactor = new MyJComboBox(scalingFactors);
     scalingFactor.setEditable(true);
-    scalingFactor.addItemListener(this);
     scalingFactor.setEnabled(!cc.viewer.embed.getValue());
-    addGBComponent(desktopSize,ScreenPanel, 0, 0, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,5,0,5));
-    addGBComponent(desktopSizePanel,ScreenPanel, 0, 1, 2, 1, 2, 2, 1, 0, GridBagConstraints.REMAINDER, GridBagConstraints.LINE_START, new Insets(0,20,0,0));
-    addGBComponent(fullScreen,ScreenPanel, 0, 2, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,5,0,5));
-    addGBComponent(fullScreenAllMonitors,ScreenPanel, 0, 3, 4, 1, 2, 2, 1, 0, GridBagConstraints.REMAINDER, GridBagConstraints.LINE_START, new Insets(4,25,0,5));
-    addGBComponent(scalingFactorLabel,ScreenPanel, 0, 4, 1, GridBagConstraints.REMAINDER, 2, 2, 1, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(8,8,0,5));
-    addGBComponent(scalingFactor,ScreenPanel, 1, 4, 1, GridBagConstraints.REMAINDER, 2, 2, 25, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(4,5,0,5));
+    ScreenPanel.add(desktopSize,
+                    new GridBagConstraints(0, 0,
+                                           REMAINDER, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(0, 0, 0, 0),
+                                           NONE, NONE));
+    indent = getButtonLabelInset(desktopSize);
+    ScreenPanel.add(desktopSizePanel,
+                    new GridBagConstraints(0, 1,
+                                           REMAINDER, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(0, indent, 0, 0),
+                                           NONE, NONE));
+    ScreenPanel.add(fullScreen,
+                    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,
+                    new GridBagConstraints(0, 3,
+                                           REMAINDER, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(0, indent, 4, 0),
+                                           NONE, NONE));
+    ScreenPanel.add(scalingFactorLabel,
+                    new GridBagConstraints(0, 4,
+                                           1, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(0, 0, 4, 0),
+                                           NONE, NONE));
+    ScreenPanel.add(scalingFactor,
+                    new GridBagConstraints(1, 4,
+                                           1, 1,
+                                           HEAVY, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(0, 5, 4, 0),
+                                           NONE, NONE));
+    ScreenPanel.add(Box.createRigidArea(new Dimension(5, 0)),
+                    new GridBagConstraints(0, 5,
+                                           REMAINDER, REMAINDER,
+                                           HEAVY, HEAVY,
+                                           LINE_START, BOTH,
+                                           new Insets(0, 0, 0, 0),
+                                           NONE, NONE));
 
     // Misc tab
-    MiscPanel=new JPanel(new GridBagLayout());
-
-    shared = new JCheckBox("Shared connection (do not disconnect other viewers)");
-    shared.addItemListener(this);
+    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");
-    useLocalCursor.addItemListener(this);
     acceptBell = new JCheckBox("Beep when requested by the server");
-    acceptBell.addItemListener(this);
-    addGBComponent(shared,MiscPanel,         0, 0, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,5,0,5));
-    addGBComponent(useLocalCursor,MiscPanel, 0, 1, 2, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(4,5,0,5));
-    addGBComponent(acceptBell,MiscPanel,     0, 2, 2, 1, 2, 2, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(4,5,0,5));
+    MiscPanel.add(shared,
+                  new GridBagConstraints(0, 0,
+                                         1, 1,
+                                         LIGHT, LIGHT,
+                                         LINE_START, NONE,
+                                         new Insets(0, 0, 4, 0),
+                                         NONE, NONE));
+    MiscPanel.add(useLocalCursor,
+                  new GridBagConstraints(0, 1,
+                                         1, 1,
+                                         LIGHT, LIGHT,
+                                         LINE_START, NONE,
+                                         new Insets(0, 0, 4, 0),
+                                         NONE, NONE));
+    MiscPanel.add(acceptBell,
+                  new GridBagConstraints(0, 2,
+                                         1, 1,
+                                         LIGHT, LIGHT,
+                                         LINE_START, NONE,
+                                         new Insets(0, 0, 4, 0),
+                                         NONE, NONE));
+    MiscPanel.add(Box.createRigidArea(new Dimension(5, 0)),
+                  new GridBagConstraints(0, 3,
+                                         REMAINDER, REMAINDER,
+                                         HEAVY, HEAVY,
+                                         LINE_START, BOTH,
+                                         new Insets(0, 0, 0, 0),
+                                         NONE, NONE));
 
-    // load/save tab
-    DefaultsPanel=new JPanel(new GridBagLayout());
 
+    // 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"));
+    configPanel.
+      setBorder(BorderFactory.createTitledBorder("Configuration File"));
     cfLoadButton = new JButton("Load");
-    cfLoadButton.addActionListener(this);
-    addGBComponent(cfLoadButton,configPanel, 0, 0, 1, 1, 0, 0, 0, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));
     cfSaveAsButton = new JButton("Save As...");
-    cfSaveAsButton.addActionListener(this);
-    addGBComponent(cfSaveAsButton,configPanel, 0, 1, 1, 1, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));
+    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));
 
     JPanel defaultsPanel = new JPanel(new GridBagLayout());
     defaultsPanel.setBorder(BorderFactory.createTitledBorder("Defaults"));
     defClearButton = new JButton("Clear");
-    defClearButton.addActionListener(this);
-    addGBComponent(defClearButton,defaultsPanel, 0, 0, 1, 1, 0, 0, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));
     defReloadButton = new JButton("Reload");
-    defReloadButton.addActionListener(this);
-    addGBComponent(defReloadButton,defaultsPanel, 0, 1, 1, 1, 0, 0, 0, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));
     defSaveButton = new JButton("Save");
-    defSaveButton.addActionListener(this);
-    addGBComponent(defSaveButton,defaultsPanel, 0, 2, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(4,8,4,8));
-
-    addGBComponent(configPanel,DefaultsPanel, 0, 0, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.PAGE_START, new Insets(4,5,4,5));
-    addGBComponent(defaultsPanel,DefaultsPanel, 1, 0, 1, GridBagConstraints.REMAINDER, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.PAGE_START, new Insets(4,0,4,5));
-
-    // security tab
-    SecPanel=new JPanel(new GridBagLayout());
-
-    JPanel encryptionPanel = new JPanel(new GridBagLayout());
-    encryptionPanel.setBorder(BorderFactory.createTitledBorder("Encryption"));
-    encNone = addCheckbox("None", null, encryptionPanel);
-    encTLS = addCheckbox("Anonymous TLS", null, encryptionPanel);
-    encX509 = addJCheckBox("TLS with X.509 certificates", null, encryptionPanel, new GridBagConstraints(0,2,3,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.REMAINDER,new Insets(0,0,0,0),0,0));
-
-    ca = new JButton("Load CA certificate");
-    ca.addActionListener(this);
-    crl = new JButton("Load CRL certificate");
-    crl.addActionListener(this);
-    addGBComponent(ca, encryptionPanel,  0, 3, 1, 1, 2, 2, 1, 0, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(2,20,2,2));
-    addGBComponent(crl, encryptionPanel, 1, 3, 1, 1, 2, 2, 1, 0, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(2,2,2,2));
-
-    JPanel authPanel = new JPanel(new GridBagLayout());
-    authPanel.setBorder(BorderFactory.createTitledBorder("Authentication"));
-    secNone = addCheckbox("None", null, authPanel);
-    secVnc = addCheckbox("Standard VNC", null, authPanel);
-    secPlain = addJCheckBox("Plaintext", null, authPanel, new GridBagConstraints(0,2,1,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.NONE,new Insets(0,0,0,5),0,0));
-    secIdent = addJCheckBox("Ident", null, authPanel, new GridBagConstraints(0,3,1,1,1,1,GridBagConstraints.LINE_START,GridBagConstraints.NONE,new Insets(0,0,0,5),0,0));
-    sendLocalUsername = new JCheckBox("Send Local Username");
-    sendLocalUsername.addItemListener(this);
-    addGBComponent(sendLocalUsername, authPanel, 1, 2, 1, 2, 0, 0, 2, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(0,20,0,0));
-
-    secVeNCrypt = new JCheckBox("Extended encryption and authentication methods (VeNCrypt)");
-    secVeNCrypt.addItemListener(this);
-    addGBComponent(secVeNCrypt,SecPanel,     0, 0, 1, 1, 2, 2, 1, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.FIRST_LINE_START, new Insets(4,5,0,30));
-    addGBComponent(encryptionPanel,SecPanel, 0, 1, 1, 1, 2, 2, 1, 0, GridBagConstraints.NONE, GridBagConstraints.LINE_START, new Insets(0,10,2,5));
-    addGBComponent(authPanel,SecPanel,       0, 3, 1, 1, 2, 2, 1, 1, GridBagConstraints.NONE, GridBagConstraints.FIRST_LINE_START, new Insets(2,10,2,5));
-
-    tabPane.add(FormatPanel);
-    tabPane.add(ScreenPanel);
-    tabPane.add(InputsPanel);
-    tabPane.add(MiscPanel);
-    tabPane.add(DefaultsPanel);
-    tabPane.add(SecPanel);
+    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));
+
+    // tabPane
     tabPane.addTab("Compression", FormatPanel);
     tabPane.addTab("Security", SecPanel);
-    tabPane.addTab("Input", InputsPanel);
+    tabPane.addTab("Input", inputPanel);
     tabPane.addTab("Screen", ScreenPanel);
     tabPane.addTab("Misc", MiscPanel);
-    tabPane.addTab("Load / Save", DefaultsPanel);
-    tabPane.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
-
+    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));
+
+    // button pane
     okButton = new JButton("OK");
-    okButton.setPreferredSize(new Dimension(90,30));
-    okButton.addActionListener(this);
     cancelButton = new JButton("Cancel");
-    cancelButton.setPreferredSize(new Dimension(90,30));
-    cancelButton.addActionListener(this);
 
-    JPanel buttonPane = new JPanel();
-    buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
-    buttonPane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
-    buttonPane.add(Box.createHorizontalGlue());
+    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(Box.createRigidArea(new Dimension(5,0)));
     buttonPane.add(cancelButton);
-    buttonPane.add(Box.createRigidArea(new Dimension(5,0)));
-
-    this.getContentPane().add(tabPane);
-    this.getContentPane().add(buttonPane);
 
+    this.add(tabPane);
+    this.add(buttonPane);
+    addListeners(this);
     pack();
-
   }
 
   public void initDialog() {
@@ -367,21 +733,29 @@ class OptionsDialog extends Dialog implements
       }
     }
     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",
+            "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",
+            "AcceptClipboard", acceptClipboard.isSelected());
     UserPreferences.set("global", "SendClipboard", sendClipboard.isSelected());
-    String menuKeyStr = MenuKey.getMenuKeySymbols()[menuKey.getSelectedIndex()].name;
+    String menuKeyStr =
+      MenuKey.getMenuKeySymbols()[menuKey.getSelectedIndex()].name;
     UserPreferences.set("global", "MenuKey", menuKeyStr);
     String desktopSizeString =
-      desktopSize.isSelected() ? desktopWidth.getText() + "x" + desktopHeight.getText() : "";
+      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",
+            "FullScreenAllMonitors", fullScreenAllMonitors.isSelected());
     UserPreferences.set("global", "Shared", shared.isSelected());
-    UserPreferences.set("global", "UseLocalCursor", useLocalCursor.isSelected());
+    UserPreferences.set("global",
+            "UseLocalCursor", useLocalCursor.isSelected());
     UserPreferences.set("global", "AcceptBell", acceptBell.isSelected());
     String scaleString = scalingFactor.getSelectedItem().toString();
     if (scaleString.equalsIgnoreCase("Auto")) {
@@ -400,11 +774,14 @@ class OptionsDialog extends Dialog implements
     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() != "")
-      UserPreferences.set("viewer", "x509ca", CSecurityTLS.x509ca.getValueStr());
-    if (CSecurityTLS.x509crl.getValueStr() != "")
-      UserPreferences.set("viewer", "x509crl", CSecurityTLS.x509crl.getValueStr());
+    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());
   }
 
   private void restorePreferences() {
@@ -443,24 +820,32 @@ class OptionsDialog extends Dialog implements
       }
     }
     noJpeg.setSelected(!UserPreferences.getBool("global", "NoJPEG"));
-    qualityLevel.setSelectedItem(UserPreferences.getInt("global", "QualityLevel"));
-    customCompressLevel.setSelected(UserPreferences.getBool("global", "CustomCompressLevel"));
-    compressLevel.setSelectedItem(UserPreferences.getInt("global", "CompressLevel"));
+    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"));
+    acceptClipboard.setSelected(UserPreferences.getBool("global",
+            "AcceptClipboard"));
+    sendClipboard.setSelected(UserPreferences.getBool("global",
+            "SendClipboard"));
     menuKey.setSelectedItem(UserPreferences.get("global", "MenuKey"));
-    desktopSize.setSelected(UserPreferences.get("global", "DesktopSize") != null);
+    desktopSize.setSelected(UserPreferences.get("global", "DesktopSize")
+            != null);
     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"));
+    fullScreenAllMonitors.setSelected(UserPreferences.getBool("global",
+            "FullScreenAllMonitors"));
     if (shared.isEnabled())
       shared.setSelected(UserPreferences.getBool("global", "Shared"));
-    useLocalCursor.setSelected(UserPreferences.getBool("global", "UseLocalCursor"));
+    useLocalCursor.setSelected(UserPreferences.getBool("global",
+            "UseLocalCursor"));
     acceptBell.setSelected(UserPreferences.getBool("global", "AcceptBell"));
     String scaleString = UserPreferences.get("global", "ScalingFactor");
     if (scaleString != null) {
@@ -473,14 +858,16 @@ class OptionsDialog extends Dialog implements
       }
     }
     if (secVeNCrypt.isEnabled()) {
-      secVeNCrypt.setSelected(UserPreferences.getBool("viewer", "secVeNCrypt", true));
+      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"));
+        sendLocalUsername.setSelected(UserPreferences.getBool("global",
+                "SendLocalUsername"));
       }
     }
     if (secNone.isEnabled())
@@ -489,44 +876,6 @@ class OptionsDialog extends Dialog implements
       secVnc.setSelected(UserPreferences.getBool("viewer", "secVnc", true));
   }
 
-  JRadioButton addRadioCheckbox(String str, ButtonGroup group, JPanel panel) {
-    JRadioButton c = new JRadioButton(str);
-    GridBagConstraints gbc = new GridBagConstraints();
-    gbc.anchor = GridBagConstraints.LINE_START;
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gbc.weightx = 1;
-    gbc.weighty = 1;
-    panel.add(c,gbc);
-    group.add(c);
-    c.addItemListener(this);
-    return c;
-  }
-
-  JCheckBox addCheckbox(String str, ButtonGroup group, JPanel panel) {
-    JCheckBox c = new JCheckBox(str);
-    GridBagConstraints gbc = new GridBagConstraints();
-    gbc.anchor = GridBagConstraints.LINE_START;
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gbc.weightx = 1;
-    gbc.weighty = 1;
-    panel.add(c,gbc);
-    if (group != null)
-      group.add(c);
-    c.addItemListener(this);
-    return c;
-  }
-
-  JCheckBox addJCheckBox(String str, ButtonGroup group, JPanel panel,
-      GridBagConstraints gbc) {
-    JCheckBox c = new JCheckBox(str);
-    panel.add(c,gbc);
-    if (group != null)
-      group.add(c);
-    c.addItemListener(this);
-
-    return c;
-  }
-
   public void endDialog() {
     super.endDialog();
     if (cc.viewport != null && cc.viewport.isVisible()) {
@@ -537,103 +886,126 @@ class OptionsDialog extends Dialog implements
 
   public void actionPerformed(ActionEvent e) {
     Object s = e.getSource();
-    if (s instanceof JButton && (JButton)s == okButton) {
-      if (cc != null) cc.getOptions();
-      endDialog();
-    } else if (s instanceof JButton && (JButton)s == cancelButton) {
-      endDialog();
-    } else if (s instanceof JButton && (JButton)s == 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);
+    if (s instanceof JButton) {
+      JButton button = (JButton)s;
+      if (button == okButton) {
+        JTextField[] fields =
+          { x509ca, x509crl };
+        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 (s instanceof JButton && (JButton)s == 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 (s instanceof JButton && (JButton)s == defSaveButton) {
-      updatePreferences();
-      UserPreferences.save();
-    } else if (s instanceof JButton && (JButton)s == defReloadButton) {
-      restorePreferences();
-    } else if (s instanceof JButton && (JButton)s == defClearButton) {
-      UserPreferences.clear();
-      cc.setOptions();
-    } else if (s instanceof JButton && (JButton)s == ca) {
-      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)
-        CSecurityTLS.x509ca.setParam(fc.getSelectedFile().toString());
-    } else if (s instanceof JButton && (JButton)s == crl) {
-      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)
-        CSecurityTLS.x509crl.setParam(fc.getSelectedFile().toString());
     }
   }
 
   public void itemStateChanged(ItemEvent e) {
     Object s = e.getSource();
-    if (s instanceof JCheckBox && (JCheckBox)s == autoSelect) {
-      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());
-    }
-    if (s instanceof JCheckBox && (JCheckBox)s == customCompressLevel) {
-      compressLevel.setEnabled(customCompressLevel.isSelected());
-    }
-    if (s instanceof JCheckBox && (JCheckBox)s == desktopSize) {
-      desktopWidth.setEnabled(desktopSize.isSelected());
-      desktopHeight.setEnabled(desktopSize.isSelected());
-    }
-    if (s instanceof JCheckBox && (JCheckBox)s == noJpeg) {
-      qualityLevel.setEnabled(noJpeg.isSelected());
-    }
-    if (s instanceof JCheckBox && (JCheckBox)s == secVeNCrypt) {
-      encNone.setEnabled(secVeNCrypt.isSelected());
-      encTLS.setEnabled(secVeNCrypt.isSelected());
-      encX509.setEnabled(secVeNCrypt.isSelected());
-      ca.setEnabled(secVeNCrypt.isSelected());
-      crl.setEnabled(secVeNCrypt.isSelected());
-      secIdent.setEnabled(secVeNCrypt.isSelected());
-      secPlain.setEnabled(secVeNCrypt.isSelected());
-      sendLocalUsername.setEnabled(secVeNCrypt.isSelected());
-    }
-    if (s instanceof JCheckBox && (JCheckBox)s == encNone) {
-      secNone.setSelected(encNone.isSelected() &&
-        UserPreferences.getBool("viewer", "secNone", true));
-      secVnc.setSelected(encNone.isSelected() &&
-        UserPreferences.getBool("viewer", "secVnc", true));
-    }
-    if (s instanceof JCheckBox && (JCheckBox)s == secIdent ||
-        s instanceof JCheckBox && (JCheckBox)s == secPlain) {
-      sendLocalUsername.setEnabled(secIdent.isSelected()||secPlain.isSelected());
+    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);
+        x509crl.setEnabled(enable);
+        caButton.setEnabled(enable);
+        crlButton.setEnabled(enable);
+      } else if (item == secVeNCrypt) {
+        encNone.setEnabled(enable);
+        encTLS.setEnabled(enable);
+        encX509.setEnabled(enable);
+        x509ca.setEnabled(enable && encX509.isSelected());
+        x509crl.setEnabled(enable && encX509.isSelected());
+        caButton.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());
+      }
     }
   }
-
 }
index edd955403e440569bc504d75e93a7f8aa32f6525..26a138d6f6e6cca556b4058a33694b7669f07f06 100644 (file)
@@ -22,13 +22,20 @@ package com.tigervnc.vncviewer;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
+
 import com.jcraft.jsch.*;
+import com.tigervnc.rfb.*;
+
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.LINE_START;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.REMAINDER;
 
-class PasswdDialog extends Dialog implements KeyListener,
-                                             UserInfo,
+class PasswdDialog extends Dialog implements UserInfo,
                                              UIKeyboardInteractive {
 
-  public PasswdDialog(String title, boolean userDisabled, boolean passwdDisabled) {
+  public PasswdDialog(String title,
+                      boolean userDisabled, boolean passwdDisabled) {
     super(true);
     setResizable(false);
     setTitle(title);
@@ -38,28 +45,45 @@ class PasswdDialog extends Dialog implements KeyListener,
       }
     });
 
-    JPanel p1 = new JPanel();
+    JPanel p1 = new JPanel(new GridBagLayout());
+    p1.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
     userLabel = new JLabel("Username:");
-    p1.add(userLabel);
+    userLabel.setEnabled(!userDisabled);
+    p1.add(userLabel, new GridBagConstraints(0, 0,
+                                             1, 1,
+                                             LIGHT, LIGHT,
+                                             LINE_START, NONE,
+                                             new Insets(0, 0, 0, 0),
+                                             NONE, NONE));
     userEntry = new JTextField(30);
     userEntry.setEnabled(!userDisabled);
-    userLabel.setEnabled(!userDisabled);
-    p1.add(userEntry);
-    userEntry.addKeyListener(this);
+    p1.add(userEntry, new GridBagConstraints(1, 0,
+                                             1, 1,
+                                             HEAVY, LIGHT,
+                                             LINE_START, REMAINDER,
+                                             new Insets(0, 5, 0, 0),
+                                             NONE, NONE));
 
-    JPanel p2 = new JPanel();
     passwdLabel = new JLabel("Password:");
-    passwdLabel.setPreferredSize(userLabel.getPreferredSize());
-    p2.add(passwdLabel);
+    passwdLabel.setEnabled(!passwdDisabled);
+    p1.add(passwdLabel, new GridBagConstraints(0, 1,
+                                               1, 1,
+                                               LIGHT, LIGHT,
+                                               LINE_START, NONE,
+                                               new Insets(5, 0, 0, 0),
+                                               NONE, NONE));
     passwdEntry = new JPasswordField(30);
     passwdEntry.setEnabled(!passwdDisabled);
-    passwdLabel.setEnabled(!passwdDisabled);
-    p2.add(passwdEntry);
-    passwdEntry.addKeyListener(this);
+    p1.add(passwdEntry, new GridBagConstraints(1, 1,
+                                               1, 1,
+                                               HEAVY, LIGHT,
+                                               LINE_START, REMAINDER,
+                                               new Insets(5, 5, 0, 0),
+                                               NONE, NONE));
 
-    getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.Y_AXIS));
-    getContentPane().add(p1);
-    getContentPane().add(p2);
+    this.add(p1);
+    addListeners(this);
     pack();
     if (userEntry.isEnabled()) {
       userEntry.requestFocus();
@@ -68,10 +92,6 @@ class PasswdDialog extends Dialog implements KeyListener,
     }
   }
 
-  /** Handle the key-typed event. */
-  public void keyTyped(KeyEvent event) { }
-  /** Handle the key-released event. */
-  public void keyReleased(KeyEvent event) { }
   /** Handle the key-pressed event. */
   public void keyPressed(KeyEvent event) {
     Object s = event.getSource();
@@ -79,7 +99,8 @@ class PasswdDialog extends Dialog implements KeyListener,
        if (event.getKeyCode() == KeyEvent.VK_ENTER) {
          endDialog();
         }
-    } else if (s instanceof JPasswordField && (JPasswordField)s == passwdEntry) {
+    } else if (s instanceof JPasswordField
+              && (JPasswordField)s == passwdEntry) {
         if (event.getKeyCode() == KeyEvent.VK_ENTER) {
          endDialog();
         }
@@ -89,81 +110,90 @@ class PasswdDialog extends Dialog implements KeyListener,
   public String getPassword() {
     return new String(passwdEntry.getPassword());
   }
-  public String getPassphrase(){ return null; }
-  public boolean promptPassphrase(String message){ return false; }
-  public boolean promptPassword(String message){
+
+  public String getPassphrase() { return null; }
+  public boolean promptPassphrase(String message) { return false; }
+
+  public boolean promptPassword(String message) {
     setTitle(message);
     showDialog();
-    if (passwdEntry != null)
-      return true;
+    if (userEntry.isEnabled())
+      if (userEntry.getText().equals(""))
+        return false;
+    if (passwdEntry.isEnabled())
+      if (!passwdEntry.getText().equals(""))
+        return true;
     return false;
   }
-  public void showMessage(String message){
-    JOptionPane.showMessageDialog(null, message);
+
+  public void showMessage(String message) {
+    JOptionPane.showMessageDialog(null, message, "Message",
+                                  JOptionPane.PLAIN_MESSAGE);
   }
-  public boolean promptYesNo(String str){
-    Object[] options={ "yes", "no" };
-    int foo=JOptionPane.showOptionDialog(null,
+
+  public boolean promptYesNo(String str) {
+    Object[] options={ "YES", "NO" };
+    int ret=JOptionPane.showOptionDialog(null,
            str,
            "Warning",
            JOptionPane.DEFAULT_OPTION,
            JOptionPane.WARNING_MESSAGE,
            null, options, options[0]);
-     return foo==0;
+     return (ret == 0);
   }
+
   public String[] promptKeyboardInteractive(String destination,
                                             String name,
                                             String instruction,
                                             String[] prompt,
-                                            boolean[] echo){
-    Container panel = new JPanel();
-    panel.setLayout(new GridBagLayout());
-
-    GridBagConstraints gbc =
-      new GridBagConstraints(0,0,1,1,1,1,
-                             GridBagConstraints.NORTHWEST,
-                             GridBagConstraints.NONE,
-                             new Insets(0,0,0,0),0,0);
-    gbc.weightx = 1.0;
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gbc.gridx = 0;
-    panel.add(new JLabel(instruction), gbc);
-    gbc.gridy++;
-
-    gbc.gridwidth = GridBagConstraints.RELATIVE;
+                                            boolean[] echo) {
+    vlog.info("OK");
+    Container panel = new JPanel(new GridBagLayout());
+    ((JPanel)panel).setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
+
+    panel.add(new JLabel(instruction),
+              new GridBagConstraints(0, 0,
+                                     REMAINDER, 1,
+                                     LIGHT, LIGHT,
+                                     LINE_START, NONE,
+                                     new Insets(0, 0, 0, 0),
+                                     NONE, NONE));
 
     JTextField[] texts=new JTextField[prompt.length];
-    for(int i=0; i<prompt.length; i++){
-      gbc.fill = GridBagConstraints.NONE;
-      gbc.gridx = 0;
-      gbc.weightx = 1;
-      panel.add(new JLabel(prompt[i]),gbc);
-
-      gbc.gridx = 1;
-      gbc.fill = GridBagConstraints.HORIZONTAL;
-      gbc.weighty = 1;
-      if(echo[i]){
+    for (int i = 0; i < prompt.length; i++) {
+      panel.add(new JLabel(prompt[i]),
+                new GridBagConstraints(0, i+1,
+                                       1, 1,
+                                       LIGHT, LIGHT,
+                                       LINE_START, NONE,
+                                       new Insets(5, 0, 0, 0),
+                                       NONE, NONE));
+
+      if(echo[i])
         texts[i]=new JTextField(20);
-      }
-      else{
+      else
         texts[i]=new JPasswordField(20);
-      }
-      panel.add(texts[i], gbc);
-      gbc.gridy++;
+
+      panel.add(texts[i],
+                new GridBagConstraints(1, i+1,
+                                       1, 1,
+                                       HEAVY, LIGHT,
+                                       LINE_START, HORIZONTAL,
+                                       new Insets(5, 5, 0, 0),
+                                       NONE, NONE));
     }
 
-    if(JOptionPane.showConfirmDialog(null, panel,
-                                     destination+": "+name,
-                                     JOptionPane.OK_CANCEL_OPTION,
-                                     JOptionPane.QUESTION_MESSAGE)
-       ==JOptionPane.OK_OPTION){
+    if (JOptionPane.showConfirmDialog(null, panel,
+                                      destination+": "+name,
+                                      JOptionPane.OK_CANCEL_OPTION,
+                                      JOptionPane.QUESTION_MESSAGE)
+        == JOptionPane.OK_OPTION) {
       String[] response=new String[prompt.length];
       for(int i=0; i<prompt.length; i++){
         response[i]=texts[i].getText();
       }
-       return response;
-    }
-    else{
+           return response;
+    } else{
       return null;  // cancel
     }
   }
@@ -172,4 +202,5 @@ class PasswdDialog extends Dialog implements KeyListener,
   JTextField userEntry;
   JLabel passwdLabel;
   JPasswordField passwdEntry;
+  static LogWriter vlog = new LogWriter("PasswdDialog");
 }
index 7b8ee0c776e94e0024072699ddc144f0d48ee181..172bde6f338d83e1516b879021e1b57e58b11bcb 100644 (file)
@@ -1,5 +1,5 @@
 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
- * Copyright (C) 2011-2013 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
@@ -28,9 +28,12 @@ import java.util.*;
 
 import com.tigervnc.rfb.*;
 
-class ServerDialog extends Dialog implements
-                           ActionListener
-{
+import static java.awt.GridBagConstraints.HORIZONTAL;
+import static java.awt.GridBagConstraints.LINE_START;
+import static java.awt.GridBagConstraints.NONE;
+import static java.awt.GridBagConstraints.REMAINDER;
+
+class ServerDialog extends Dialog {
 
   @SuppressWarnings({"unchecked","rawtypes"})
   public ServerDialog(OptionsDialog options_,
@@ -39,9 +42,8 @@ class ServerDialog extends Dialog implements
     super(true);
     cc = cc_;
     setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
-    setResizable(false);
-    setSize(new Dimension(340, 135));
     setTitle("VNC Viewer: Connection Details");
+    setResizable(false);
     addWindowListener(new WindowAdapter() {
       public void windowClosing(WindowEvent e) {
         if (VncViewer.nViewers == 1) {
@@ -54,25 +56,14 @@ class ServerDialog extends Dialog implements
     });
 
     options = options_;
-    getContentPane().setLayout(new GridBagLayout());
-
-    JLabel serverLabel = new JLabel("Server:", JLabel.RIGHT);
-    if (UserPreferences.get("ServerDialog", "history") != null) {
-      String valueStr = UserPreferences.get("ServerDialog", "history");
-      server = new JComboBox(valueStr.split(","));
-    } else {
-      server = new JComboBox();
-    }
 
-    // Hack to set the left inset on editable JComboBox
-    if (UIManager.getLookAndFeel().getID() == "Windows") {
-      server.setBorder(BorderFactory.createCompoundBorder(server.getBorder(),
-        BorderFactory.createEmptyBorder(0,2,0,0)));
-    } else if (UIManager.getLookAndFeel().getID() == "Metal") {
-      ComboBoxEditor editor = server.getEditor();
-      JTextField jtf = (JTextField)editor.getEditorComponent();
-      jtf.setBorder(new CompoundBorder(jtf.getBorder(), new EmptyBorder(0,2,0,0)));
-    }
+    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(","));
+    if (valueStr.equals(""))
+      server.setPrototypeDisplayValue("255.255.255.255:5900");
 
     server.setEditable(true);
     editor = server.getEditor();
@@ -88,42 +79,50 @@ class ServerDialog extends Dialog implements
       }
     });
 
-    JPanel topPanel = new JPanel(new GridBagLayout());
-
-    addGBComponent(new JLabel(VncViewer.logoIcon),topPanel, 0, 0, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_START, new Insets(5,5,5,15));
-    addGBComponent(serverLabel,topPanel, 1, 0, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.LINE_END, new Insets(10,0,5,5));
-    addGBComponent(server,topPanel, 2, 0, 1, 1, 0, 0, 1, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(10,0,5,40));
+    Container contentPane = this.getContentPane();
+    contentPane.setLayout(new GridBagLayout());
 
+    JLabel icon = new JLabel(VncViewer.logoIcon);
     optionsButton = new JButton("Options...");
     aboutButton = new JButton("About...");
     okButton = new JButton("OK");
     cancelButton = new JButton("Cancel");
-    JPanel buttonPanel = new JPanel(new GridBagLayout());
-    buttonPanel.setPreferredSize(new Dimension(340, 40));
-    addGBComponent(aboutButton,buttonPanel, 0, 3, 1, 1, 0, 0, 0.2, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));
-    addGBComponent(optionsButton,buttonPanel, 1, 3, 1, 1, 0, 0, 0, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));
-    addGBComponent(okButton,buttonPanel, 2, 3, 1, 1, 0, 0, 0.8, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));
-    addGBComponent(cancelButton,buttonPanel, 3, 3, 1, 1, 0, 0, 0.5, 1, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER, new Insets(0,5,0,5));
-
-    GridBagConstraints gbc = new GridBagConstraints();
-    gbc.anchor = GridBagConstraints.LINE_START;
-    gbc.fill = GridBagConstraints.BOTH;
-    gbc.gridwidth = GridBagConstraints.REMAINDER;
-    gbc.gridheight = 1;
-    gbc.insets = new Insets(0,0,0,0);
-    gbc.ipadx = 0;
-    gbc.ipady = 0;
-    gbc.weightx = 1;
-    gbc.weighty = 1;
-    getContentPane().add(topPanel,gbc);
-    getContentPane().add(buttonPanel);
-
-    server.addActionListener(this);
-    optionsButton.addActionListener(this);
-    aboutButton.addActionListener(this);
-    okButton.addActionListener(this);
-    cancelButton.addActionListener(this);
 
+    contentPane.add(icon,
+                    new GridBagConstraints(0, 0,
+                                           1, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(5, 5, 5, 5),
+                                           NONE, NONE));
+    contentPane.add(serverLabel,
+                    new GridBagConstraints(1, 0,
+                                           1, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, NONE,
+                                           new Insets(5, 10, 5, 5),
+                                           NONE, NONE));
+    contentPane.add(server,
+                    new GridBagConstraints(2, 0,
+                                           REMAINDER, 1,
+                                           HEAVY, LIGHT,
+                                           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,
+                    new GridBagConstraints(0, 1,
+                                           REMAINDER, 1,
+                                           LIGHT, LIGHT,
+                                           LINE_START, HORIZONTAL,
+                                           new Insets(5, 5, 5, 5),
+                                           NONE, NONE));
+    addListeners(this);
     pack();
   }
 
@@ -160,12 +159,14 @@ class ServerDialog extends Dialog implements
     }
     // set params
     Configuration.setParam("Server", Hostname.getHost(serverName));
-    Configuration.setParam("Port", Integer.toString(Hostname.getPort(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());
+    StringBuffer sb =
+        new StringBuffer().append((String)server.getSelectedItem());
     while (st.hasMoreTokens()) {
       String str = st.nextToken();
       if (!str.equals((String)server.getSelectedItem()) && !str.equals("")) {
@@ -180,7 +181,7 @@ class ServerDialog extends Dialog implements
 
   CConn cc;
   @SuppressWarnings("rawtypes")
-  JComboBox server;
+  MyJComboBox server;
   ComboBoxEditor editor;
   JButton aboutButton, optionsButton, okButton, cancelButton;
   OptionsDialog options;
index 0c54d79685a7f27539fc46abb40e3b11f6bdf302..078076f73bd72fe699f98b19c55ee23a44b23b27 100644 (file)
@@ -657,7 +657,17 @@ public class VncViewer extends javax.swing.JApplet
                      "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",
   "Specifies a configuration file to load.", null);
@@ -665,5 +675,5 @@ public class VncViewer extends javax.swing.JApplet
   Thread thread;
   Socket sock;
   static int nViewers;
-  static LogWriter vlog = new LogWriter("main");
+  static LogWriter vlog = new LogWriter("VncViewer");
 }