1 /* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
2  * Copyright (C) 2011-2019 Brian P. Hinz
3  *
4  * This is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This software is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this software; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
17  * USA.
18  */
19 
20 package com.tigervnc.vncviewer;
21 
22 import java.awt.*;
23 import java.awt.event.*;
24 import java.io.File;
25 import java.lang.reflect.*;
26 import java.text.Format;
27 import java.text.NumberFormat;
28 import javax.swing.*;
29 import javax.swing.border.*;
30 import javax.swing.filechooser.*;
31 import javax.swing.UIManager.*;
32 import javax.swing.text.*;
33 import java.util.*;
34 import java.util.List;
35 import java.util.Map.Entry;
36 import java.util.prefs.*;
37 
38 import com.tigervnc.rfb.*;
39 
40 import static java.awt.GridBagConstraints.BOTH;
41 import static java.awt.GridBagConstraints.CENTER;
42 import static java.awt.GridBagConstraints.HORIZONTAL;
43 import static java.awt.GridBagConstraints.LINE_END;
44 import static java.awt.GridBagConstraints.LINE_START;
45 import static java.awt.GridBagConstraints.PAGE_START;
46 import static java.awt.GridBagConstraints.NONE;
47 import static java.awt.GridBagConstraints.RELATIVE;
48 import static java.awt.GridBagConstraints.REMAINDER;
49 import static java.awt.GridBagConstraints.VERTICAL;
50 
51 import static com.tigervnc.vncviewer.Parameters.*;
52 
53 class OptionsDialog extends Dialog {
54 
55   private class IntegerDocument extends PlainDocument {
56     private int limit;
57 
IntegerDocument(int max)58     public IntegerDocument(int max) {
59       super();
60       limit = max;
61     }
62 
insertString(int offset, String str, AttributeSet a)63     public void insertString(int offset, String str, AttributeSet a)
64           throws BadLocationException {
65       if (str == null || !str.matches("^[0-9]+$")) return;
66       if ((getLength() + str.length()) > limit)
67         Toolkit.getDefaultToolkit().beep();
68       else
69         super.insertString(offset, str, a);
70     }
71   }
72 
73   private class IntegerTextField extends JFormattedTextField {
IntegerTextField(int digits)74     public IntegerTextField(int digits) {
75       super();
76       setDocument(new IntegerDocument(digits));
77       Font f = getFont();
78       String template = String.format("%0"+digits+"d", 0);
79       int w = getFontMetrics(f).stringWidth(template) +
80               getMargin().left + getMargin().right +
81               getInsets().left + getInsets().right;
82       int h = getPreferredSize().height;
83       setPreferredSize(new Dimension(w, h));
84     }
85 
86     @Override
processFocusEvent(final FocusEvent e)87     protected void processFocusEvent(final FocusEvent e) {
88       if (e.isTemporary())
89         return;
90       if (e.getID() == FocusEvent.FOCUS_LOST)
91         if (getText() == null || getText().isEmpty())
92           setValue(null);
93       super.processFocusEvent(e);
94     }
95   }
96 
97   private static Map<Object, String> callbacks = new HashMap<Object, String>();
98   /* Compression */
99   JCheckBox autoselectCheckbox;
100 
101   ButtonGroup encodingGroup;
102   JRadioButton tightButton;
103   JRadioButton zrleButton;
104   JRadioButton hextileButton;
105   JRadioButton rawButton;
106 
107   ButtonGroup colorlevelGroup;
108   JRadioButton fullcolorButton;
109   JRadioButton mediumcolorButton;
110   JRadioButton lowcolorButton;
111   JRadioButton verylowcolorButton;
112 
113   JCheckBox compressionCheckbox;
114   JCheckBox jpegCheckbox;
115   JComboBox compressionInput;
116   JComboBox jpegInput;
117 
118   /* Security */
119   JCheckBox encNoneCheckbox;
120   JCheckBox encTLSCheckbox;
121   JCheckBox encX509Checkbox;
122   JTextField caInput;
123   JTextField crlInput;
124   JButton caChooser;
125   JButton crlChooser;
126 
127   JCheckBox authNoneCheckbox;
128   JCheckBox authVncCheckbox;
129   JCheckBox authPlainCheckbox;
130   JCheckBox authIdentCheckbox;
131   JCheckBox sendLocalUsernameCheckbox;
132 
133   /* Input */
134   JCheckBox viewOnlyCheckbox;
135   JCheckBox acceptClipboardCheckbox;
136   JCheckBox sendClipboardCheckbox;
137   JComboBox menuKeyChoice;
138 
139   /* Screen */
140   JCheckBox desktopSizeCheckbox;
141   JTextField desktopWidthInput;
142   JTextField desktopHeightInput;
143 
144   ButtonGroup sizingGroup;
145   JRadioButton remoteResizeButton;
146   JRadioButton remoteScaleButton;
147   JComboBox scalingFactorInput;
148 
149   JCheckBox fullScreenCheckbox;
150   JCheckBox fullScreenAllMonitorsCheckbox;
151 
152   /* Misc. */
153   JCheckBox sharedCheckbox;
154   JCheckBox dotWhenNoCursorCheckbox;
155   JCheckBox acceptBellCheckbox;
156 
157   /* SSH */
158   JCheckBox tunnelCheckbox;
159   JCheckBox viaCheckbox;
160   JTextField viaUserInput;
161   JTextField viaHostInput;
162   JTextField viaPortInput;
163   JCheckBox extSSHCheckbox;
164   JTextField sshClientInput;
165   JButton sshClientChooser;
166   JRadioButton sshArgsDefaultButton;
167   JRadioButton sshArgsCustomButton;
168   JTextField sshArgsInput;
169   JTextField sshConfigInput;
170   JTextField sshKeyFileInput;
171   JButton sshConfigChooser;
172   JButton sshKeyFileChooser;
173 
174   @SuppressWarnings({"rawtypes","unchecked"})
OptionsDialog()175   public OptionsDialog() {
176     super(true);
177     setTitle("VNC Viewer Options");
178     setResizable(false);
179 
180     getContentPane().setLayout(
181       new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));
182 
183     JTabbedPane tabPane = new JTabbedPane();
184     tabPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
185 
186     encodingGroup = new ButtonGroup();
187     colorlevelGroup = new ButtonGroup();
188 
189     // tabPane
190     tabPane.addTab("Compression", createCompressionPanel());
191     tabPane.addTab("Security", createSecurityPanel());
192     tabPane.addTab("Input", createInputPanel());
193     tabPane.addTab("Screen", createScreenPanel());
194     tabPane.addTab("Misc", createMiscPanel());
195     tabPane.addTab("SSH", createSshPanel());
196     tabPane.setBorder(BorderFactory.createEmptyBorder());
197     // Resize the tabPane if necessary to prevent scrolling
198     int minWidth = 0;
199     Object tpi = UIManager.get("TabbedPane:TabbedPaneTabArea.contentMargins");
200     if (tpi != null)
201       minWidth += ((Insets)tpi).left + ((Insets)tpi).right;
202     for (int i = 0; i < tabPane.getTabCount(); i++)
203       minWidth += tabPane.getBoundsAt(i).width;
204     int minHeight = tabPane.getPreferredSize().height;
205     if (tabPane.getPreferredSize().width < minWidth)
206       tabPane.setPreferredSize(new Dimension(minWidth, minHeight));
207 
208     // button pane
209     JButton okButton = new JButton("OK  \u21B5");
210     okButton.addActionListener(new ActionListener() {
211       public void actionPerformed(ActionEvent e) {
212         storeOptions();
213         endDialog();
214       }
215     });
216     JButton cancelButton = new JButton("Cancel");
217     cancelButton.addActionListener(new ActionListener() {
218       public void actionPerformed(ActionEvent e) {
219         endDialog();
220       }
221     });
222 
223     JPanel buttonPane = new JPanel(new GridLayout(1, 5, 10, 10));
224     buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 5, 5, 5));
225     buttonPane.add(Box.createRigidArea(new Dimension()));
226     buttonPane.add(Box.createRigidArea(new Dimension()));
227     buttonPane.add(Box.createRigidArea(new Dimension()));
228     buttonPane.add(cancelButton);
229     buttonPane.add(okButton);
230 
231     this.add(tabPane);
232     this.add(buttonPane);
233     addListeners(this);
234     pack();
235   }
236 
showDialog(Container c)237   public static void showDialog(Container c) {
238     OptionsDialog dialog = new OptionsDialog();
239     dialog.show(c);
240   }
241 
show(Container c)242   public void show(Container c) {
243     loadOptions();
244     super.showDialog(c);
245   }
246 
addCallback(String cb, Object obj)247   public static void addCallback(String cb, Object obj)
248   {
249     callbacks.put(obj, cb);
250   }
251 
removeCallback(Object obj)252   public static void removeCallback(Object obj)
253   {
254     callbacks.remove(obj);
255   }
256 
endDialog()257   public void endDialog() {
258     super.endDialog();
259     // Making a new dialog is so cheap that it's not worth keeping
260     this.dispose();
261   }
262 
loadOptions()263   private void loadOptions()
264   {
265     /* Compression */
266     autoselectCheckbox.setSelected(autoSelect.getValue());
267 
268     int encNum = Encodings.encodingNum(preferredEncoding.getValueStr());
269 
270     switch (encNum) {
271     case Encodings.encodingTight:
272       tightButton.setSelected(true);
273       break;
274     case Encodings.encodingZRLE:
275       zrleButton.setSelected(true);
276       break;
277     case Encodings.encodingHextile:
278       hextileButton.setSelected(true);
279       break;
280     case Encodings.encodingRaw:
281       rawButton.setSelected(true);
282       break;
283     }
284 
285     if (fullColor.getValue())
286       fullcolorButton.setSelected(true);
287     else {
288       switch (lowColorLevel.getValue()) {
289       case 0:
290         verylowcolorButton.setSelected(true);
291         break;
292       case 1:
293         lowcolorButton.setSelected(true);
294         break;
295       case 2:
296         mediumcolorButton.setSelected(true);
297         break;
298       }
299     }
300 
301     int digit = 0;
302 
303     compressionCheckbox.setSelected(customCompressLevel.getValue());
304     jpegCheckbox.setSelected(!noJpeg.getValue());
305     digit = 0 + compressLevel.getValue();
306     compressionInput.setSelectedItem(digit);
307     digit = 0 + qualityLevel.getValue();
308     jpegInput.setSelectedItem(digit);
309 
310     handleAutoselect();
311     handleCompression();
312     handleJpeg();
313 
314     /* Security */
315     Security security = new Security(SecurityClient.secTypes);
316 
317     List<Integer> secTypes;
318     Iterator<Integer> iter;
319 
320     List<Integer> secTypesExt;
321     Iterator<Integer> iterExt;
322 
323     encNoneCheckbox.setSelected(false);
324     encTLSCheckbox.setSelected(false);
325     encX509Checkbox.setSelected(false);
326 
327     authNoneCheckbox.setSelected(false);
328     authVncCheckbox.setSelected(false);
329     authPlainCheckbox.setSelected(false);
330     authIdentCheckbox.setSelected(false);
331     sendLocalUsernameCheckbox.setSelected(sendLocalUsername.getValue());
332 
333     secTypes = security.GetEnabledSecTypes();
334     for (iter = secTypes.iterator(); iter.hasNext(); ) {
335       switch ((Integer)iter.next()) {
336       case Security.secTypeNone:
337         encNoneCheckbox.setSelected(true);
338         authNoneCheckbox.setSelected(true);
339         break;
340       case Security.secTypeVncAuth:
341         encNoneCheckbox.setSelected(true);
342         authVncCheckbox.setSelected(true);
343         break;
344       }
345     }
346 
347     secTypesExt = security.GetEnabledExtSecTypes();
348     for (iterExt = secTypesExt.iterator(); iterExt.hasNext(); ) {
349       switch ((Integer)iterExt.next()) {
350       case Security.secTypePlain:
351         encNoneCheckbox.setSelected(true);
352         authPlainCheckbox.setSelected(true);
353         break;
354       case Security.secTypeIdent:
355         encNoneCheckbox.setSelected(true);
356         authIdentCheckbox.setSelected(true);
357         break;
358       case Security.secTypeTLSNone:
359         encTLSCheckbox.setSelected(true);
360         authNoneCheckbox.setSelected(true);
361         break;
362       case Security.secTypeTLSVnc:
363         encTLSCheckbox.setSelected(true);
364         authVncCheckbox.setSelected(true);
365         break;
366       case Security.secTypeTLSPlain:
367         encTLSCheckbox.setSelected(true);
368         authPlainCheckbox.setSelected(true);
369         break;
370       case Security.secTypeTLSIdent:
371         encTLSCheckbox.setSelected(true);
372         authIdentCheckbox.setSelected(true);
373         break;
374       case Security.secTypeX509None:
375         encX509Checkbox.setSelected(true);
376         authNoneCheckbox.setSelected(true);
377         break;
378       case Security.secTypeX509Vnc:
379         encX509Checkbox.setSelected(true);
380         authVncCheckbox.setSelected(true);
381         break;
382       case Security.secTypeX509Plain:
383         encX509Checkbox.setSelected(true);
384         authPlainCheckbox.setSelected(true);
385         break;
386       case Security.secTypeX509Ident:
387         encX509Checkbox.setSelected(true);
388         authIdentCheckbox.setSelected(true);
389         break;
390       }
391     }
392 
393     File caFile = new File(CSecurityTLS.X509CA.getValueStr());
394     if (caFile.exists() && caFile.canRead())
395       caInput.setText(caFile.getAbsolutePath());
396     File crlFile = new File(CSecurityTLS.X509CRL.getValueStr());
397     if (crlFile.exists() && crlFile.canRead())
398       crlInput.setText(crlFile.getAbsolutePath());
399 
400     handleX509();
401     handleSendLocalUsername();
402 
403     /* Input */
404     viewOnlyCheckbox.setSelected(viewOnly.getValue());
405     acceptClipboardCheckbox.setSelected(acceptClipboard.getValue());
406     sendClipboardCheckbox.setSelected(sendClipboard.getValue());
407 
408     menuKeyChoice.setSelectedIndex(0);
409 
410     String menuKeyStr = menuKey.getValueStr();
411     for (int i = 0; i < menuKeyChoice.getItemCount(); i++)
412       if (menuKeyStr.equals(menuKeyChoice.getItemAt(i)))
413         menuKeyChoice.setSelectedIndex(i);
414 
415     /* Screen */
416     String width, height;
417 
418     if (desktopSize.getValueStr().isEmpty() ||
419         desktopSize.getValueStr().split("x").length != 2) {
420       desktopSizeCheckbox.setSelected(false);
421       desktopWidthInput.setText("1024");
422       desktopHeightInput.setText("768");
423     } else {
424       desktopSizeCheckbox.setSelected(true);
425       width = desktopSize.getValueStr().split("x")[0];
426       desktopWidthInput.setText(width);
427       height = desktopSize.getValueStr().split("x")[1];
428       desktopHeightInput.setText(height);
429     }
430     if (remoteResize.getValue())
431       remoteResizeButton.setSelected(true);
432     else
433       remoteScaleButton.setSelected(true);
434     fullScreenCheckbox.setSelected(fullScreen.getValue());
435     fullScreenAllMonitorsCheckbox.setSelected(fullScreenAllMonitors.getValue());
436 
437     scalingFactorInput.setSelectedItem("100%");
438     String scaleStr = scalingFactor.getValueStr();
439     if (scaleStr.matches("^[0-9]+$"))
440       scaleStr = scaleStr.concat("%");
441     if (scaleStr.matches("^FixedRatio$"))
442       scaleStr = new String("Fixed Aspect Ratio");
443     for (int i = 0; i < scalingFactorInput.getItemCount(); i++)
444       if (scaleStr.equals(scalingFactorInput.getItemAt(i)))
445         scalingFactorInput.setSelectedIndex(i);
446 
447     handleDesktopSize();
448 
449     /* Misc. */
450     sharedCheckbox.setSelected(shared.getValue());
451     dotWhenNoCursorCheckbox.setSelected(dotWhenNoCursor.getValue());
452     acceptBellCheckbox.setSelected(acceptBell.getValue());
453 
454     /* SSH */
455     File f;
456     tunnelCheckbox.setSelected(tunnel.getValue() || !via.getValueStr().isEmpty());
457     viaCheckbox.setSelected(!via.getValueStr().isEmpty());
458     if (viaCheckbox.isSelected()) {
459       viaUserInput.setText(Tunnel.getSshUser());
460       viaHostInput.setText(Tunnel.getSshHost());
461       viaPortInput.setText(Integer.toString(Tunnel.getSshPort()));
462     }
463     extSSHCheckbox.setSelected(extSSH.getValue());
464     f = new File(extSSHClient.getValueStr());
465     if (f.exists() && f.isFile() && f.canExecute())
466       sshClientInput.setText(f.getAbsolutePath());
467     if (extSSHArgs.getValueStr().isEmpty()) {
468       sshArgsDefaultButton.setSelected(true);
469     } else {
470       sshArgsCustomButton.setSelected(true);
471       sshArgsInput.setText(extSSHArgs.getValueStr());
472     }
473     f = new File(sshKeyFile.getValueStr());
474     if (f.exists() && f.isFile() && f.canRead())
475       sshKeyFileInput.setText(f.getAbsolutePath());
476     f = new File(sshConfig.getValueStr());
477     if (f.exists() && f.isFile() && f.canRead())
478       sshConfigInput.setText(f.getAbsolutePath());
479 
480     handleTunnel();
481     handleVia();
482     handleExtSSH();
483     handleRfbState();
484   }
485 
storeOptions()486   private void storeOptions() {
487     /* Compression */
488     autoSelect.setParam(autoselectCheckbox.isSelected());
489 
490     if (tightButton.isSelected())
491       preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingTight));
492     else if (zrleButton.isSelected())
493       preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingZRLE));
494     else if (hextileButton.isSelected())
495       preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingHextile));
496     else if (rawButton.isSelected())
497       preferredEncoding.setParam(Encodings.encodingName(Encodings.encodingRaw));
498 
499     fullColor.setParam(fullcolorButton.isSelected());
500     if (verylowcolorButton.isSelected())
501       lowColorLevel.setParam(0);
502     else if (lowcolorButton.isSelected())
503       lowColorLevel.setParam(1);
504     else if (mediumcolorButton.isSelected())
505       lowColorLevel.setParam(2);
506 
507     customCompressLevel.setParam(compressionCheckbox.isSelected());
508     noJpeg.setParam(!jpegCheckbox.isSelected());
509     compressLevel.setParam((Integer)compressionInput.getSelectedItem());
510     qualityLevel.setParam((Integer)jpegInput.getSelectedItem());
511 
512     /* Security */
513     Security security = new Security();
514 
515     /* Process security types which don't use encryption */
516     if (encNoneCheckbox.isSelected()) {
517       if (authNoneCheckbox.isSelected())
518         security.EnableSecType(Security.secTypeNone);
519       if (authVncCheckbox.isSelected())
520         security.EnableSecType(Security.secTypeVncAuth);
521       if (authPlainCheckbox.isSelected())
522         security.EnableSecType(Security.secTypePlain);
523       if (authIdentCheckbox.isSelected())
524         security.EnableSecType(Security.secTypeIdent);
525     }
526 
527     /* Process security types which use TLS encryption */
528     if (encTLSCheckbox.isSelected()) {
529       if (authNoneCheckbox.isSelected())
530         security.EnableSecType(Security.secTypeTLSNone);
531       if (authVncCheckbox.isSelected())
532         security.EnableSecType(Security.secTypeTLSVnc);
533       if (authPlainCheckbox.isSelected())
534         security.EnableSecType(Security.secTypeTLSPlain);
535       if (authIdentCheckbox.isSelected())
536         security.EnableSecType(Security.secTypeTLSIdent);
537     }
538 
539     /* Process security types which use X509 encryption */
540     if (encX509Checkbox.isSelected()) {
541       if (authNoneCheckbox.isSelected())
542         security.EnableSecType(Security.secTypeX509None);
543       if (authVncCheckbox.isSelected())
544         security.EnableSecType(Security.secTypeX509Vnc);
545       if (authPlainCheckbox.isSelected())
546         security.EnableSecType(Security.secTypeX509Plain);
547       if (authIdentCheckbox.isSelected())
548         security.EnableSecType(Security.secTypeX509Ident);
549     }
550 
551     if (authIdentCheckbox.isSelected() ||
552         authPlainCheckbox.isSelected()) {
553       sendLocalUsername.setParam(sendLocalUsernameCheckbox.isSelected());
554     }
555 
556     SecurityClient.secTypes.setParam(security.ToString());
557 
558     File caFile = new File(caInput.getText());
559     if (caFile.exists() && caFile.canRead())
560       CSecurityTLS.X509CA.setParam(caFile.getAbsolutePath());
561     File crlFile = new File(crlInput.getText());
562     if (crlFile.exists() && crlFile.canRead())
563       CSecurityTLS.X509CRL.setParam(crlFile.getAbsolutePath());
564 
565     /* Input */
566     viewOnly.setParam(viewOnlyCheckbox.isSelected());
567     acceptClipboard.setParam(acceptClipboardCheckbox.isSelected());
568     sendClipboard.setParam(sendClipboardCheckbox.isSelected());
569 
570     String menuKeyStr =
571       MenuKey.getMenuKeySymbols()[menuKeyChoice.getSelectedIndex()].name;
572     menuKey.setParam(menuKeyStr);
573 
574     /* Screen */
575     if (desktopSizeCheckbox.isSelected() &&
576         !desktopWidthInput.getText().isEmpty() &&
577         !desktopHeightInput.getText().isEmpty()) {
578       String width = desktopWidthInput.getText();
579       String height = desktopHeightInput.getText();
580       desktopSize.setParam(width.concat("x").concat(height));
581     } else {
582       desktopSize.setParam("");
583     }
584     remoteResize.setParam(remoteResizeButton.isSelected());
585     fullScreen.setParam(fullScreenCheckbox.isSelected());
586     fullScreenAllMonitors.setParam(fullScreenAllMonitorsCheckbox.isSelected());
587 
588     String scaleStr =
589       ((String)scalingFactorInput.getSelectedItem()).replace("%", "");
590     scaleStr.replace("Fixed Aspect Ratio", "FixedRatio");
591     scalingFactor.setParam(scaleStr);
592 
593     /* Misc. */
594     shared.setParam(sharedCheckbox.isSelected());
595     dotWhenNoCursor.setParam(dotWhenNoCursorCheckbox.isSelected());
596     acceptBell.setParam(acceptBellCheckbox.isSelected());
597 
598     /* SSH */
599     tunnel.setParam(tunnelCheckbox.isSelected());
600     if (viaCheckbox.isSelected() &&
601         !viaUserInput.getText().isEmpty() &&
602         !viaHostInput.getText().isEmpty() &&
603         !viaPortInput.getText().isEmpty()) {
604       String sshUser = viaUserInput.getText();
605       String sshHost = viaHostInput.getText();
606       String sshPort = viaPortInput.getText();
607       String viaStr = sshUser.concat("@").concat(sshHost).concat(":").concat(sshPort);
608       via.setParam(viaStr);
609     } else {
610       via.setParam("");
611     }
612     extSSH.setParam(extSSHCheckbox.isSelected());
613     if (!sshClientInput.getText().isEmpty())
614       extSSHClient.setParam(sshClientInput.getText());
615     if (sshArgsCustomButton.isSelected() &&
616         !sshArgsInput.getText().isEmpty()) {
617         extSSHArgs.setParam(sshArgsInput.getText());
618     } else {
619       extSSHArgs.setParam(new String());
620     }
621     if (!sshConfigInput.getText().isEmpty())
622       sshConfig.setParam(sshConfigInput.getText());
623     if (!sshKeyFileInput.getText().isEmpty())
624       sshKeyFile.setParam(sshKeyFileInput.getText());
625 
626     try {
627       for (Map.Entry<Object, String> iter : callbacks.entrySet()) {
628         Object obj = iter.getKey();
629         Method cb = obj.getClass().getMethod(iter.getValue(), new Class[]{});
630         if (cb == null)
631           vlog.info(obj.getClass().getName());
632         cb.invoke(obj);
633       }
634     } catch (NoSuchMethodException e) {
635       vlog.error("NoSuchMethodException: "+e.getMessage());
636     } catch (IllegalAccessException e) {
637       vlog.error("IllegalAccessException: "+e.getMessage());
638     } catch (InvocationTargetException e) {
639       vlog.error("InvocationTargetException: "+e.getMessage());
640     }
641   }
642 
createCompressionPanel()643   private JPanel createCompressionPanel() {
644     JPanel FormatPanel = new JPanel();
645     FormatPanel.setLayout(new BoxLayout(FormatPanel,
646                                         BoxLayout.PAGE_AXIS));
647     FormatPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
648 
649     JPanel autoSelectPane = new JPanel();
650     autoSelectPane.setLayout(new BoxLayout(autoSelectPane,
651                                            BoxLayout.LINE_AXIS));
652     autoSelectPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
653     autoselectCheckbox = new JCheckBox("Auto Select");
654     autoselectCheckbox.addItemListener(new ItemListener() {
655       public void itemStateChanged(ItemEvent e) {
656         handleAutoselect();
657       }
658     });
659     autoSelectPane.add(autoselectCheckbox);
660     autoSelectPane.add(Box.createHorizontalGlue());
661 
662     JPanel encodingPanel = new JPanel(new GridLayout(4, 1));
663     encodingPanel.setBorder(BorderFactory.createTitledBorder("Preferred encoding"));
664     tightButton = new GroupedJRadioButton("Tight", encodingGroup, encodingPanel);
665     zrleButton = new GroupedJRadioButton("ZRLE", encodingGroup, encodingPanel);
666     hextileButton = new GroupedJRadioButton("Hextile", encodingGroup, encodingPanel);
667     rawButton = new GroupedJRadioButton("Raw", encodingGroup, encodingPanel);
668 
669     JPanel colorPanel = new JPanel(new GridLayout(4, 1));
670     colorPanel.setBorder(BorderFactory.createTitledBorder("Color level"));
671     fullcolorButton = new GroupedJRadioButton("Full", colorlevelGroup, colorPanel);
672     mediumcolorButton = new GroupedJRadioButton("Medium", colorlevelGroup, colorPanel);
673     lowcolorButton = new GroupedJRadioButton("Low", colorlevelGroup, colorPanel);
674     verylowcolorButton = new GroupedJRadioButton("Very low", colorlevelGroup, colorPanel);
675 
676     JPanel encodingPane = new JPanel(new GridLayout(1, 2, 5, 0));
677     encodingPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
678     encodingPane.add(encodingPanel);
679     encodingPane.add(colorPanel);
680 
681     JPanel tightPanel = new JPanel(new GridBagLayout());
682     compressionCheckbox = new JCheckBox("Custom Compression Level");
683     compressionCheckbox.addItemListener(new ItemListener() {
684       public void itemStateChanged(ItemEvent e) {
685         handleCompression();
686       }
687     });
688     Object[] compressionLevels = { 1, 2, 3, 4, 5, 6 };
689     compressionInput = new MyJComboBox(compressionLevels);
690     ((MyJComboBox)compressionInput).setDocument(new IntegerDocument(1));
691     compressionInput.setPrototypeDisplayValue("0.");
692     compressionInput.setEditable(true);
693     JLabel compressionLabel =
694       new JLabel("Level (0=fast, 9=best)");
695     jpegCheckbox = new JCheckBox("Allow JPEG Compression");
696     jpegCheckbox.addItemListener(new ItemListener() {
697       public void itemStateChanged(ItemEvent e) {
698         handleJpeg();
699       }
700     });
701     Object[] qualityLevels = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
702     jpegInput = new MyJComboBox(qualityLevels);
703     jpegInput.setPrototypeDisplayValue("0.");
704     JLabel qualityLabel = new JLabel("Quality (0=poor, 9=best)");
705 
706     tightPanel.add(compressionCheckbox,
707                    new GridBagConstraints(0, 0,
708                                           REMAINDER, 1,
709                                           LIGHT, LIGHT,
710                                           LINE_START, NONE,
711                                           new Insets(0, 0, 0, 0),
712                                           NONE, NONE));
713     int indent = getButtonLabelInset(compressionCheckbox);
714     tightPanel.add(compressionInput,
715                    new GridBagConstraints(0, 1,
716                                           1, 1,
717                                           LIGHT, LIGHT,
718                                           LINE_START, NONE,
719                                           new Insets(0, indent, 0, 0),
720                                           NONE, NONE));
721     tightPanel.add(compressionLabel,
722                    new GridBagConstraints(1, 1,
723                                           1, 1,
724                                           HEAVY, LIGHT,
725                                           LINE_START, HORIZONTAL,
726                                           new Insets(0, 5, 0, 0),
727                                           NONE, NONE));
728     tightPanel.add(jpegCheckbox,
729                    new GridBagConstraints(0, 2,
730                                           REMAINDER, 1,
731                                           LIGHT, LIGHT,
732                                           LINE_START, NONE,
733                                           new Insets(5, 0, 0, 0),
734                                           NONE, NONE));
735     indent = getButtonLabelInset(jpegCheckbox);
736     tightPanel.add(jpegInput,
737                    new GridBagConstraints(0, 3,
738                                           1, 1,
739                                           LIGHT, LIGHT,
740                                           LINE_START, NONE,
741                                           new Insets(0, indent, 0, 0),
742                                           NONE, NONE));
743     tightPanel.add(qualityLabel,
744                    new GridBagConstraints(1, 3,
745                                           1, 1,
746                                           HEAVY, NONE,
747                                           LINE_START, HORIZONTAL,
748                                           new Insets(0, 5, 0, 0),
749                                           NONE, NONE));
750     tightPanel.add(Box.createRigidArea(new Dimension(5,0)),
751                    new GridBagConstraints(0, 4,
752                                           REMAINDER, REMAINDER,
753                                           HEAVY, HEAVY,
754                                           LINE_START, BOTH,
755                                           new Insets(0, 0, 0, 0),
756                                           NONE, NONE));
757     FormatPanel.add(autoSelectPane);
758     FormatPanel.add(encodingPane);
759     FormatPanel.add(tightPanel);
760     return FormatPanel;
761   }
762 
createSecurityPanel()763   private JPanel createSecurityPanel() {
764     JPanel SecPanel = new JPanel();
765     SecPanel.setLayout(new BoxLayout(SecPanel,
766                                      BoxLayout.PAGE_AXIS));
767     SecPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
768 
769     JPanel vencryptPane = new JPanel();
770     vencryptPane.setLayout(new BoxLayout(vencryptPane,
771                                          BoxLayout.LINE_AXIS));
772     vencryptPane.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
773 
774     JPanel encrPanel = new JPanel(new GridBagLayout());
775     encrPanel.setBorder(BorderFactory.createTitledBorder("Encryption"));
776     encNoneCheckbox = new JCheckBox("None");
777     encTLSCheckbox = new JCheckBox("Anonymous TLS");
778     encX509Checkbox = new JCheckBox("TLS with X.509 certificates");
779     encX509Checkbox.addItemListener(new ItemListener() {
780       public void itemStateChanged(ItemEvent e) {
781         handleX509();
782       }
783     });
784     JLabel caLabel = new JLabel("X.509 CA Certificate");
785     caInput = new JTextField();
786     caChooser = new JButton("Browse");
787     caChooser.addActionListener(new ActionListener() {
788       public void actionPerformed(ActionEvent e) {
789         JComponent c = ((JButton)e.getSource()).getRootPane();
790         File dflt = new File(CSecurityTLS.X509CA.getValueStr());
791         FileNameExtensionFilter filter =
792           new FileNameExtensionFilter("X.509 certificate", "crt", "cer", "pem");
793         File f = showChooser("Path to X509 CA certificate", dflt, c, filter);
794         if (f != null && f.exists() && f.canRead())
795           caInput.setText(f.getAbsolutePath());
796       }
797     });
798     JLabel crlLabel = new JLabel("X.509 CRL file");
799     crlInput = new JTextField();
800     crlChooser = new JButton("Browse");
801     crlChooser.addActionListener(new ActionListener() {
802       public void actionPerformed(ActionEvent e) {
803         JComponent c = ((JButton)e.getSource()).getRootPane();
804         File dflt = new File(CSecurityTLS.X509CRL.getValueStr());
805         FileNameExtensionFilter filter =
806           new FileNameExtensionFilter("X.509 CRL", "crl");
807         File f = showChooser("Path to X509 CRL file", dflt, c, filter);
808         if (f != null && f.exists() && f.canRead())
809           crlInput.setText(f.getAbsolutePath());
810       }
811     });
812     encrPanel.add(encNoneCheckbox,
813                   new GridBagConstraints(0, 0,
814                                          REMAINDER, 1,
815                                          HEAVY, LIGHT,
816                                          LINE_START, NONE,
817                                          new Insets(0, 0, 4, 0),
818                                          NONE, NONE));
819     encrPanel.add(encTLSCheckbox,
820                   new GridBagConstraints(0, 1,
821                                          REMAINDER, 1,
822                                          HEAVY, LIGHT,
823                                          LINE_START, NONE,
824                                          new Insets(0, 0, 4, 0),
825                                          NONE, NONE));
826     encrPanel.add(encX509Checkbox,
827                   new GridBagConstraints(0, 2,
828                                          3, 1,
829                                          HEAVY, LIGHT,
830                                          LINE_START, NONE,
831                                          new Insets(0, 0, 0, 0),
832                                          NONE, NONE));
833     int indent = getButtonLabelInset(encX509Checkbox);
834     encrPanel.add(caLabel,
835                   new GridBagConstraints(0, 3,
836                                          1, 1,
837                                          LIGHT, LIGHT,
838                                          LINE_END, NONE,
839                                          new Insets(0, indent, 5, 0),
840                                          0, 0));
841     encrPanel.add(caInput,
842                   new GridBagConstraints(1, 3,
843                                          1, 1,
844                                          HEAVY, LIGHT,
845                                          LINE_START, HORIZONTAL,
846                                          new Insets(0, 5, 5, 0),
847                                          0, 0));
848     encrPanel.add(caChooser,
849                   new GridBagConstraints(2, 3,
850                                          1, 1,
851                                          LIGHT, LIGHT,
852                                          LINE_START, VERTICAL,
853                                          new Insets(0, 5, 5, 0),
854                                          0, 0));
855     encrPanel.add(crlLabel,
856                   new GridBagConstraints(0, 4,
857                                          1, 1,
858                                          LIGHT, LIGHT,
859                                          LINE_END, NONE,
860                                          new Insets(0, indent, 0, 0),
861                                          0, 0));
862     encrPanel.add(crlInput,
863                   new GridBagConstraints(1, 4,
864                                          1, 1,
865                                          HEAVY, LIGHT,
866                                          LINE_START, HORIZONTAL,
867                                          new Insets(0, 5, 0, 0),
868                                          0, 0));
869     encrPanel.add(crlChooser,
870                   new GridBagConstraints(2, 4,
871                                          1, 1,
872                                          LIGHT, LIGHT,
873                                          LINE_START, VERTICAL,
874                                          new Insets(0, 5, 0, 0),
875                                          0, 0));
876 
877     JPanel authPanel = new JPanel(new GridBagLayout());
878     authPanel.setBorder(BorderFactory.createTitledBorder("Authentication"));
879 
880     authNoneCheckbox = new JCheckBox("None");
881     authVncCheckbox = new JCheckBox("Standard VNC");
882     authPlainCheckbox = new JCheckBox("Plaintext");
883     authPlainCheckbox.addItemListener(new ItemListener() {
884       public void itemStateChanged(ItemEvent e) {
885         handleSendLocalUsername();
886       }
887     });
888     authIdentCheckbox = new JCheckBox("Ident");
889     authIdentCheckbox.addItemListener(new ItemListener() {
890       public void itemStateChanged(ItemEvent e) {
891         handleSendLocalUsername();
892       }
893     });
894     sendLocalUsernameCheckbox = new JCheckBox("Send Local Username");
895     authPanel.add(authNoneCheckbox,
896                   new GridBagConstraints(0, 0,
897                                          REMAINDER, 1,
898                                          LIGHT, LIGHT,
899                                          LINE_START, NONE,
900                                          new Insets(0, 0, 4, 0),
901                                          NONE, NONE));
902     authPanel.add(authVncCheckbox,
903                   new GridBagConstraints(0, 1,
904                                          REMAINDER, 1,
905                                          LIGHT, LIGHT,
906                                          LINE_START, NONE,
907                                          new Insets(0, 0, 4, 0),
908                                          NONE, NONE));
909     authPanel.add(authPlainCheckbox,
910                   new GridBagConstraints(0, 2,
911                                          1, 1,
912                                          LIGHT, LIGHT,
913                                          LINE_START, NONE,
914                                          new Insets(0, 0, 2, 0),
915                                          NONE, NONE));
916     authPanel.add(authIdentCheckbox,
917                   new GridBagConstraints(0, 3,
918                                          1, 1,
919                                          LIGHT, LIGHT,
920                                          LINE_START, NONE,
921                                          new Insets(2, 0, 0, 0),
922                                          NONE, NONE));
923     authPanel.add(sendLocalUsernameCheckbox,
924                   new GridBagConstraints(1, 2,
925                                          1, 2,
926                                          HEAVY, LIGHT,
927                                          LINE_START, NONE,
928                                          new Insets(2, 20, 2, 0),
929                                          NONE, NONE));
930 
931     SecPanel.add(vencryptPane,
932                  new GridBagConstraints(0, 0,
933                                         REMAINDER, 1,
934                                         LIGHT, LIGHT,
935                                         LINE_START, HORIZONTAL,
936                                         new Insets(0, 0, 4, 0),
937                                         NONE, NONE));
938     SecPanel.add(encrPanel,
939                  new GridBagConstraints(0, 1,
940                                         REMAINDER, 1,
941                                         LIGHT, LIGHT,
942                                         LINE_START, HORIZONTAL,
943                                         new Insets(0, 0, 4, 0),
944                                         NONE, NONE));
945     SecPanel.add(authPanel,
946                  new GridBagConstraints(0, 2,
947                                         REMAINDER, 1,
948                                         LIGHT, LIGHT,
949                                         LINE_START, HORIZONTAL,
950                                         new Insets(0, 0, 4, 0),
951                                         NONE, NONE));
952     SecPanel.add(Box.createRigidArea(new Dimension(0,0)),
953                  new GridBagConstraints(0, RELATIVE,
954                                         REMAINDER, REMAINDER,
955                                         HEAVY, HEAVY,
956                                         LINE_START, BOTH,
957                                         new Insets(0, 0, 0, 0),
958                                         NONE, NONE));
959     return SecPanel;
960   }
961 
createInputPanel()962   private JPanel createInputPanel() {
963     JPanel inputPanel = new JPanel(new GridBagLayout());
964     inputPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
965 
966     viewOnlyCheckbox = new JCheckBox("View only (ignore mouse and keyboard)");
967     acceptClipboardCheckbox = new JCheckBox("Accept clipboard from server");
968     sendClipboardCheckbox = new JCheckBox("Send clipboard to server");
969     JLabel menuKeyLabel = new JLabel("Menu key");
970     String[] menuKeys = new String[MenuKey.getMenuKeySymbolCount()];
971     //menuKeys[0] = "None";
972     for (int i = 0; i < MenuKey.getMenuKeySymbolCount(); i++)
973       menuKeys[i] = MenuKey.getKeyText(MenuKey.getMenuKeySymbols()[i]);
974     menuKeyChoice = new JComboBox(menuKeys);
975 
976     inputPanel.add(viewOnlyCheckbox,
977                    new GridBagConstraints(0, 0,
978                                           REMAINDER, 1,
979                                           HEAVY, LIGHT,
980                                           LINE_START, NONE,
981                                           new Insets(0, 0, 4, 0),
982                                           NONE, NONE));
983     inputPanel.add(acceptClipboardCheckbox,
984                    new GridBagConstraints(0, 1,
985                                           REMAINDER, 1,
986                                           HEAVY, LIGHT,
987                                           LINE_START, NONE,
988                                           new Insets(0, 0, 4, 0),
989                                           NONE, NONE));
990     inputPanel.add(sendClipboardCheckbox,
991                    new GridBagConstraints(0, 2,
992                                           REMAINDER, 1,
993                                           HEAVY, LIGHT,
994                                           LINE_START, NONE,
995                                           new Insets(0, 0, 4, 0),
996                                           NONE, NONE));
997     inputPanel.add(menuKeyLabel,
998                    new GridBagConstraints(0, 3,
999                                           1, 1,
1000                                           LIGHT, LIGHT,
1001                                           LINE_START, NONE,
1002                                           new Insets(0, 0, 0, 0),
1003                                           NONE, NONE));
1004     inputPanel.add(menuKeyChoice,
1005                    new GridBagConstraints(1, 3,
1006                                           1, 1,
1007                                           HEAVY, LIGHT,
1008                                           LINE_START, NONE,
1009                                           new Insets(0, 5, 0, 0),
1010                                           NONE, NONE));
1011     inputPanel.add(Box.createRigidArea(new Dimension(5, 0)),
1012                    new GridBagConstraints(0, 4,
1013                                           REMAINDER, REMAINDER,
1014                                           HEAVY, HEAVY,
1015                                           LINE_START, BOTH,
1016                                           new Insets(0, 0, 0, 0),
1017                                           NONE, NONE));
1018     return inputPanel;
1019   }
1020 
createScreenPanel()1021   private JPanel createScreenPanel() {
1022     JPanel ScreenPanel = new JPanel(new GridBagLayout());
1023     ScreenPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
1024 
1025     JPanel SizingPanel = new JPanel(new GridBagLayout());
1026     SizingPanel.setBorder(BorderFactory.createTitledBorder("Desktop Sizing"));
1027     desktopSizeCheckbox = new JCheckBox("Resize remote session on connect");
1028     desktopSizeCheckbox.addItemListener(new ItemListener() {
1029       public void itemStateChanged(ItemEvent e) {
1030         handleDesktopSize();
1031       }
1032     });
1033     desktopWidthInput = new IntegerTextField(5);
1034     desktopHeightInput = new IntegerTextField(5);
1035     JPanel desktopSizePanel =
1036       new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0));
1037     desktopSizePanel.add(desktopWidthInput);
1038     desktopSizePanel.add(new JLabel(" x "));
1039     desktopSizePanel.add(desktopHeightInput);
1040     sizingGroup = new ButtonGroup();
1041     remoteResizeButton =
1042       new JRadioButton("Resize remote session to the local window");
1043     sizingGroup.add(remoteResizeButton);
1044     remoteScaleButton =
1045       new JRadioButton("Scale remote session to the local window");
1046     sizingGroup.add(remoteScaleButton);
1047     remoteResizeButton.addItemListener(new ItemListener() {
1048       public void itemStateChanged(ItemEvent e) {
1049         handleRemoteResize();
1050       }
1051     });
1052     JLabel scalingFactorLabel = new JLabel("Scaling Factor");
1053     Object[] scalingFactors = {
1054       "Auto", "Fixed Aspect Ratio", "50%", "75%", "95%", "100%", "105%",
1055       "125%", "150%", "175%", "200%", "250%", "300%", "350%", "400%" };
1056     scalingFactorInput = new MyJComboBox(scalingFactors);
1057     scalingFactorInput.setEditable(true);
1058     fullScreenCheckbox = new JCheckBox("Full-screen mode");
1059     fullScreenAllMonitorsCheckbox =
1060       new JCheckBox("Enable full-screen mode over all monitors");
1061     SizingPanel.add(desktopSizeCheckbox,
1062                     new GridBagConstraints(0, 0,
1063                                            REMAINDER, 1,
1064                                            LIGHT, LIGHT,
1065                                            LINE_START, NONE,
1066                                            new Insets(0, 0, 0, 0),
1067                                            NONE, NONE));
1068     int indent = getButtonLabelInset(desktopSizeCheckbox);
1069     SizingPanel.add(desktopSizePanel,
1070                     new GridBagConstraints(0, 1,
1071                                            REMAINDER, 1,
1072                                            LIGHT, LIGHT,
1073                                            LINE_START, NONE,
1074                                            new Insets(0, indent, 0, 0),
1075                                            NONE, NONE));
1076     SizingPanel.add(remoteResizeButton,
1077                     new GridBagConstraints(0, 2,
1078                                            REMAINDER, 1,
1079                                            LIGHT, LIGHT,
1080                                            LINE_START, NONE,
1081                                            new Insets(0, 0, 4, 0),
1082                                            NONE, NONE));
1083     SizingPanel.add(remoteScaleButton,
1084                     new GridBagConstraints(0, 3,
1085                                            REMAINDER, 1,
1086                                            LIGHT, LIGHT,
1087                                            LINE_START, NONE,
1088                                            new Insets(0, 0, 4, 0),
1089                                            NONE, NONE));
1090     indent = getButtonLabelInset(remoteScaleButton);
1091     SizingPanel.add(scalingFactorLabel,
1092                     new GridBagConstraints(0, 4,
1093                                            1, 1,
1094                                            LIGHT, LIGHT,
1095                                            LINE_START, NONE,
1096                                            new Insets(0, indent, 4, 0),
1097                                            NONE, NONE));
1098     SizingPanel.add(scalingFactorInput,
1099                     new GridBagConstraints(1, 4,
1100                                            1, 1,
1101                                            HEAVY, LIGHT,
1102                                            LINE_START, NONE,
1103                                            new Insets(0, 5, 4, 0),
1104                                            NONE, NONE));
1105     ScreenPanel.add(SizingPanel,
1106                     new GridBagConstraints(0, 0,
1107                                            REMAINDER, 1,
1108                                            LIGHT, LIGHT,
1109                                            LINE_START, HORIZONTAL,
1110                                            new Insets(0, 0, 4, 0),
1111                                            NONE, NONE));
1112     ScreenPanel.add(fullScreenCheckbox,
1113                     new GridBagConstraints(0, 1,
1114                                            REMAINDER, 1,
1115                                            LIGHT, LIGHT,
1116                                            LINE_START, NONE,
1117                                            new Insets(0, 0, 4, 0),
1118                                            NONE, NONE));
1119     indent = getButtonLabelInset(fullScreenCheckbox);
1120     ScreenPanel.add(fullScreenAllMonitorsCheckbox,
1121                     new GridBagConstraints(0, 2,
1122                                            REMAINDER, 1,
1123                                            LIGHT, LIGHT,
1124                                            LINE_START, NONE,
1125                                            new Insets(0, indent, 4, 0),
1126                                            NONE, NONE));
1127     ScreenPanel.add(Box.createRigidArea(new Dimension(5, 0)),
1128                     new GridBagConstraints(0, 3,
1129                                            REMAINDER, REMAINDER,
1130                                            HEAVY, HEAVY,
1131                                            LINE_START, BOTH,
1132                                            new Insets(0, 0, 0, 0),
1133                                            NONE, NONE));
1134     return ScreenPanel;
1135   }
1136 
createMiscPanel()1137   private JPanel createMiscPanel() {
1138     JPanel MiscPanel = new JPanel(new GridBagLayout());
1139     MiscPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
1140     sharedCheckbox =
1141       new JCheckBox("Shared (don't disconnect other viewers)");
1142     dotWhenNoCursorCheckbox = new JCheckBox("Show dot when no cursor");
1143     acceptBellCheckbox = new JCheckBox("Beep when requested by the server");
1144     MiscPanel.add(sharedCheckbox,
1145                   new GridBagConstraints(0, 0,
1146                                          1, 1,
1147                                          LIGHT, LIGHT,
1148                                          LINE_START, NONE,
1149                                          new Insets(0, 0, 4, 0),
1150                                          NONE, NONE));
1151     MiscPanel.add(dotWhenNoCursorCheckbox,
1152                   new GridBagConstraints(0, 1,
1153                                          1, 1,
1154                                          LIGHT, LIGHT,
1155                                          LINE_START, NONE,
1156                                          new Insets(0, 0, 4, 0),
1157                                          NONE, NONE));
1158     MiscPanel.add(acceptBellCheckbox,
1159                   new GridBagConstraints(0, 2,
1160                                          1, 1,
1161                                          LIGHT, LIGHT,
1162                                          LINE_START, NONE,
1163                                          new Insets(0, 0, 4, 0),
1164                                          NONE, NONE));
1165     MiscPanel.add(Box.createRigidArea(new Dimension(5, 0)),
1166                   new GridBagConstraints(0, 3,
1167                                          REMAINDER, REMAINDER,
1168                                          HEAVY, HEAVY,
1169                                          LINE_START, BOTH,
1170                                          new Insets(0, 0, 0, 0),
1171                                          NONE, NONE));
1172     return MiscPanel;
1173   }
1174 
createSshPanel()1175   private JPanel createSshPanel() {
1176     JPanel sshPanel = new JPanel(new GridBagLayout());
1177     sshPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 0, 5));
1178     ButtonGroup sshArgsGroup = new ButtonGroup();
1179     tunnelCheckbox = new JCheckBox("Tunnel VNC over SSH");
1180     tunnelCheckbox.addItemListener(new ItemListener() {
1181       public void itemStateChanged(ItemEvent e) {
1182         handleTunnel();
1183       }
1184     });
1185 
1186     JPanel tunnelPanel = new JPanel(new GridBagLayout());
1187 
1188     viaCheckbox = new JCheckBox("Use SSH gateway");
1189     viaCheckbox.addItemListener(new ItemListener() {
1190       public void itemStateChanged(ItemEvent e) {
1191         handleVia();
1192       }
1193     });
1194     JLabel sshUserLabel = new JLabel("Username");
1195     viaUserInput = new JTextField();
1196     JLabel sshUserAtLabel = new JLabel("@");
1197     JLabel sshHostLabel = new JLabel("Hostname (or IP address)");
1198     viaHostInput = new JTextField("");
1199     JLabel sshPortLabel = new JLabel("Port");
1200     viaPortInput = new IntegerTextField(5);
1201 
1202     extSSHCheckbox = new JCheckBox("Use external SSH client");
1203     extSSHCheckbox.addItemListener(new ItemListener() {
1204       public void itemStateChanged(ItemEvent e) {
1205         handleExtSSH();
1206       }
1207     });
1208     sshClientInput = new JTextField();
1209     sshClientChooser = new JButton("Browse");
1210     sshClientChooser.addActionListener(new ActionListener() {
1211       public void actionPerformed(ActionEvent e) {
1212         JComponent c = ((JButton)e.getSource()).getRootPane();
1213         File dflt = new File(extSSHClient.getValueStr());
1214         File f = showChooser("Path to external SSH client", dflt, c);
1215         if (f != null && f.exists() && f.isFile() && f.canExecute())
1216           sshClientInput.setText(f.getAbsolutePath());
1217       }
1218     });
1219     JLabel sshConfigLabel = new JLabel("SSH config file");
1220     sshConfigInput = new JTextField();
1221     sshConfigChooser = new JButton("Browse");
1222     sshConfigChooser.addActionListener(new ActionListener() {
1223       public void actionPerformed(ActionEvent e) {
1224         JComponent c = ((JButton)e.getSource()).getRootPane();
1225         File dflt = new File(sshConfig.getValueStr());
1226         File f = showChooser("Path to OpenSSH client config file", dflt, c);
1227         if (f != null && f.exists() && f.isFile() && f.canRead())
1228           sshConfigInput.setText(f.getAbsolutePath());
1229       }
1230     });
1231     JLabel sshKeyFileLabel = new JLabel("SSH identity file");
1232     sshKeyFileInput = new JTextField();
1233     sshKeyFileChooser = new JButton("Browse");
1234     sshKeyFileChooser.addActionListener(new ActionListener() {
1235       public void actionPerformed(ActionEvent e) {
1236         JComponent c = ((JButton)e.getSource()).getRootPane();
1237         File f = showChooser("Path to SSH key file", null, c);
1238         if (f != null && f.exists() && f.isFile() && f.canRead())
1239           sshKeyFileInput.setText(f.getAbsolutePath());
1240       }
1241     });
1242     JPanel sshArgsPanel = new JPanel(new GridBagLayout());
1243     JLabel sshArgsLabel = new JLabel("Arguments:");
1244     sshArgsDefaultButton = new GroupedJRadioButton("Default", sshArgsGroup, sshArgsPanel);
1245     sshArgsDefaultButton.addActionListener(new ActionListener() {
1246       public void actionPerformed(ActionEvent e) {
1247         sshArgsInput.setEnabled(sshArgsCustomButton.isSelected());
1248       }
1249     });
1250     sshArgsCustomButton = new GroupedJRadioButton("Custom", sshArgsGroup, sshArgsPanel);
1251     sshArgsCustomButton.addActionListener(new ActionListener() {
1252       public void actionPerformed(ActionEvent e) {
1253         sshArgsInput.setEnabled(sshArgsCustomButton.isSelected());
1254       }
1255     });
1256     sshArgsInput = new JTextField();
1257 
1258     JPanel gatewayPanel = new JPanel(new GridBagLayout());
1259     gatewayPanel.add(viaCheckbox,
1260                     new GridBagConstraints(0, 0,
1261                                            REMAINDER, 1,
1262                                            LIGHT, LIGHT,
1263                                            LINE_START, NONE,
1264                                            new Insets(0, 0, 4, 0),
1265                                            NONE, NONE));
1266     int indent = getButtonLabelInset(viaCheckbox);
1267     gatewayPanel.add(sshUserLabel,
1268                  new GridBagConstraints(0, 1,
1269                                         1, 1,
1270                                         LIGHT, LIGHT,
1271                                         LINE_START, HORIZONTAL,
1272                                         new Insets(0, indent, 4, 0),
1273                                         NONE, NONE));
1274     gatewayPanel.add(sshHostLabel,
1275                  new GridBagConstraints(2, 1,
1276                                         1, 1,
1277                                         HEAVY, LIGHT,
1278                                         LINE_START, HORIZONTAL,
1279                                         new Insets(0, 0, 4, 0),
1280                                         NONE, NONE));
1281     gatewayPanel.add(sshPortLabel,
1282                  new GridBagConstraints(3, 1,
1283                                         1, 1,
1284                                         LIGHT, LIGHT,
1285                                         LINE_START, HORIZONTAL,
1286                                         new Insets(0, 5, 4, 0),
1287                                         NONE, NONE));
1288     gatewayPanel.add(viaUserInput,
1289                  new GridBagConstraints(0, 2,
1290                                         1, 1,
1291                                         LIGHT, LIGHT,
1292                                         LINE_START, HORIZONTAL,
1293                                         new Insets(0, indent, 0, 0),
1294                                         NONE, NONE));
1295     gatewayPanel.add(sshUserAtLabel,
1296                  new GridBagConstraints(1, 2,
1297                                         1, 1,
1298                                         LIGHT, LIGHT,
1299                                         LINE_START, HORIZONTAL,
1300                                         new Insets(0, 2, 0, 2),
1301                                         NONE, NONE));
1302     gatewayPanel.add(viaHostInput,
1303                  new GridBagConstraints(2, 2,
1304                                         1, 1,
1305                                         HEAVY, LIGHT,
1306                                         LINE_START, HORIZONTAL,
1307                                         new Insets(0, 0, 0, 0),
1308                                         NONE, NONE));
1309     gatewayPanel.add(viaPortInput,
1310                  new GridBagConstraints(3, 2,
1311                                         1, 1,
1312                                         LIGHT, LIGHT,
1313                                         LINE_START, HORIZONTAL,
1314                                         new Insets(0, 5, 0, 0),
1315                                         NONE, NONE));
1316 
1317     JPanel clientPanel = new JPanel(new GridBagLayout());
1318     clientPanel.add(extSSHCheckbox,
1319                     new GridBagConstraints(0, 0,
1320                                            1, 1,
1321                                            LIGHT, LIGHT,
1322                                            LINE_START, NONE,
1323                                            new Insets(0, 0, 0, 0),
1324                                            NONE, NONE));
1325     clientPanel.add(sshClientInput,
1326                     new GridBagConstraints(1, 0,
1327                                            1, 1,
1328                                            HEAVY, LIGHT,
1329                                            LINE_START, HORIZONTAL,
1330                                            new Insets(0, 5, 0, 0),
1331                                            NONE, NONE));
1332     clientPanel.add(sshClientChooser,
1333                     new GridBagConstraints(2, 0,
1334                                            1, 1,
1335                                            LIGHT, LIGHT,
1336                                            LINE_START, NONE,
1337                                            new Insets(0, 5, 0, 0),
1338                                            NONE, NONE));
1339     sshArgsPanel.add(sshArgsLabel,
1340                     new GridBagConstraints(0, 1,
1341                                            1, 1,
1342                                            LIGHT, LIGHT,
1343                                            LINE_START, NONE,
1344                                            new Insets(0, 0, 0, 0),
1345                                            NONE, NONE));
1346     sshArgsPanel.add(sshArgsDefaultButton,
1347                     new GridBagConstraints(1, 1,
1348                                            1, 1,
1349                                            LIGHT, LIGHT,
1350                                            LINE_START, NONE,
1351                                            new Insets(0, 5, 0, 0),
1352                                            NONE, NONE));
1353     sshArgsPanel.add(sshArgsCustomButton,
1354                     new GridBagConstraints(2, 1,
1355                                            1, 1,
1356                                            LIGHT, LIGHT,
1357                                            LINE_START, NONE,
1358                                            new Insets(0, 5, 0, 0),
1359                                            NONE, NONE));
1360     sshArgsPanel.add(sshArgsInput,
1361                     new GridBagConstraints(3, 1,
1362                                            1, 1,
1363                                            HEAVY, LIGHT,
1364                                            LINE_START, HORIZONTAL,
1365                                            new Insets(0, 5, 0, 0),
1366                                            NONE, NONE));
1367     indent = getButtonLabelInset(extSSHCheckbox);
1368     clientPanel.add(sshArgsPanel,
1369                     new GridBagConstraints(0, 1,
1370                                            REMAINDER, 1,
1371                                            LIGHT, LIGHT,
1372                                            LINE_START, HORIZONTAL,
1373                                            new Insets(4, indent, 0, 0),
1374                                            NONE, NONE));
1375 
1376     JPanel opensshPanel = new JPanel(new GridBagLayout());
1377     TitledBorder border =
1378       BorderFactory.createTitledBorder("Embedded SSH client configuration");
1379     opensshPanel.setBorder(border);
1380     opensshPanel.add(sshConfigLabel,
1381                      new GridBagConstraints(0, 0,
1382                                             1, 1,
1383                                             LIGHT, LIGHT,
1384                                             LINE_START, NONE,
1385                                             new Insets(0, 0, 5, 0),
1386                                             NONE, NONE));
1387     opensshPanel.add(sshConfigInput,
1388                      new GridBagConstraints(1, 0,
1389                                             1, 1,
1390                                             HEAVY, LIGHT,
1391                                             LINE_START, HORIZONTAL,
1392                                             new Insets(0, 5, 5, 0),
1393                                             NONE, NONE));
1394     opensshPanel.add(sshConfigChooser,
1395                      new GridBagConstraints(2, 0,
1396                                             1, 1,
1397                                             LIGHT, LIGHT,
1398                                             LINE_START, VERTICAL,
1399                                             new Insets(0, 5, 5, 0),
1400                                             NONE, NONE));
1401     opensshPanel.add(sshKeyFileLabel,
1402                      new GridBagConstraints(0, 1,
1403                                             1, 1,
1404                                             LIGHT, LIGHT,
1405                                             LINE_START, NONE,
1406                                             new Insets(0, 0, 0, 0),
1407                                             NONE, NONE));
1408     opensshPanel.add(sshKeyFileInput,
1409                      new GridBagConstraints(1, 1,
1410                                             1, 1,
1411                                             HEAVY, LIGHT,
1412                                             LINE_START, HORIZONTAL,
1413                                             new Insets(0, 5, 0, 0),
1414                                             NONE, NONE));
1415     opensshPanel.add(sshKeyFileChooser,
1416                      new GridBagConstraints(2, 1,
1417                                             1, 1,
1418                                             LIGHT, LIGHT,
1419                                             LINE_START, VERTICAL,
1420                                             new Insets(0, 5, 0, 0),
1421                                             NONE, NONE));
1422     tunnelPanel.add(gatewayPanel,
1423                     new GridBagConstraints(0, 0,
1424                                            REMAINDER, 1,
1425                                            HEAVY, LIGHT,
1426                                            LINE_START, HORIZONTAL,
1427                                            new Insets(0, 0, 4, 0),
1428                                            NONE, NONE));
1429     tunnelPanel.add(clientPanel,
1430                     new GridBagConstraints(0, 1,
1431                                            REMAINDER, 1,
1432                                            HEAVY, LIGHT,
1433                                            LINE_START, HORIZONTAL,
1434                                            new Insets(0, 0, 4, 0),
1435                                            NONE, NONE));
1436     tunnelPanel.add(opensshPanel,
1437                     new GridBagConstraints(0, 2,
1438                                            REMAINDER, 1,
1439                                            HEAVY, LIGHT,
1440                                            LINE_START, HORIZONTAL,
1441                                            new Insets(0, 0, 0, 0),
1442                                            NONE, NONE));
1443 
1444     sshPanel.add(tunnelCheckbox,
1445                  new GridBagConstraints(0, 0,
1446                                         REMAINDER, 1,
1447                                         LIGHT, LIGHT,
1448                                         LINE_START, NONE,
1449                                         new Insets(0, 0, 4, 0),
1450                                         NONE, NONE));
1451     indent = getButtonLabelInset(tunnelCheckbox);
1452     sshPanel.add(tunnelPanel,
1453                  new GridBagConstraints(0, 2,
1454                                         REMAINDER, 1,
1455                                         LIGHT, LIGHT,
1456                                         LINE_START, HORIZONTAL,
1457                                         new Insets(0, indent, 4, 0),
1458                                         NONE, NONE));
1459     sshPanel.add(Box.createRigidArea(new Dimension(5, 0)),
1460                  new GridBagConstraints(0, RELATIVE,
1461                                         REMAINDER, REMAINDER,
1462                                         HEAVY, HEAVY,
1463                                         LINE_START, BOTH,
1464                                         new Insets(0, 0, 0, 0),
1465                                         NONE, NONE));
1466     return sshPanel;
1467   }
1468 
handleAutoselect()1469   private void handleAutoselect()
1470   {
1471     ButtonGroup[] groups = { encodingGroup, colorlevelGroup };
1472     for (ButtonGroup grp : groups) {
1473       Enumeration<AbstractButton> elems = grp.getElements();
1474       while (elems.hasMoreElements())
1475         elems.nextElement().setEnabled(!autoselectCheckbox.isSelected());
1476     }
1477 
1478     // JPEG setting is also affected by autoselection
1479     jpegCheckbox.setEnabled(!autoselectCheckbox.isSelected());
1480     handleJpeg();
1481   }
1482 
handleCompression()1483   private void handleCompression()
1484   {
1485     compressionInput.setEnabled(compressionCheckbox.isSelected());
1486   }
1487 
handleJpeg()1488   private void handleJpeg()
1489   {
1490     if (jpegCheckbox.isSelected() &&
1491         !autoselectCheckbox.isSelected())
1492       jpegInput.setEnabled(true);
1493     else
1494       jpegInput.setEnabled(false);
1495   }
1496 
handleX509()1497   private void handleX509()
1498   {
1499     caInput.setEnabled(encX509Checkbox.isSelected());
1500     caChooser.setEnabled(encX509Checkbox.isSelected());
1501     crlInput.setEnabled(encX509Checkbox.isSelected());
1502     crlChooser.setEnabled(encX509Checkbox.isSelected());
1503   }
1504 
handleSendLocalUsername()1505   private void handleSendLocalUsername()
1506   {
1507     boolean value = authIdentCheckbox.isSelected() ||
1508                     authPlainCheckbox.isSelected();
1509         sendLocalUsernameCheckbox.setEnabled(value);
1510   }
1511 
handleDesktopSize()1512   private void handleDesktopSize()
1513   {
1514     desktopWidthInput.setEnabled(desktopSizeCheckbox.isSelected());
1515     desktopHeightInput.setEnabled(desktopSizeCheckbox.isSelected());
1516   }
1517 
handleRemoteResize()1518   private void handleRemoteResize()
1519   {
1520     scalingFactorInput.setEnabled(!remoteResizeButton.isSelected());
1521   }
1522 
handleTunnel()1523   private void handleTunnel()
1524   {
1525     viaCheckbox.setEnabled(tunnelCheckbox.isSelected());
1526     extSSHCheckbox.setEnabled(tunnelCheckbox.isSelected());
1527     if (tunnelCheckbox.isSelected()) {
1528       JComponent[] components = { viaUserInput, viaHostInput, viaPortInput };
1529       for (JComponent c : components)
1530         c.setEnabled(viaCheckbox.isSelected());
1531       sshClientInput.setEnabled(extSSHCheckbox.isSelected());
1532       sshClientChooser.setEnabled(extSSHCheckbox.isSelected());
1533       sshArgsDefaultButton.setEnabled(extSSHCheckbox.isSelected());
1534       sshArgsCustomButton.setEnabled(extSSHCheckbox.isSelected());
1535       sshArgsInput.setEnabled(extSSHCheckbox.isSelected());
1536       sshConfigInput.setEnabled(!extSSHCheckbox.isSelected());
1537       sshConfigChooser.setEnabled(!extSSHCheckbox.isSelected());
1538       sshKeyFileInput.setEnabled(!extSSHCheckbox.isSelected());
1539       sshKeyFileChooser.setEnabled(!extSSHCheckbox.isSelected());
1540     } else {
1541       JComponent[] components = {
1542         viaUserInput, viaHostInput, viaPortInput, sshClientInput,
1543         sshClientChooser, sshArgsDefaultButton, sshArgsCustomButton,
1544         sshArgsInput, sshConfigInput, sshConfigChooser, sshKeyFileInput,
1545         sshKeyFileChooser, };
1546       for (JComponent c : components)
1547         c.setEnabled(false);
1548     }
1549   }
1550 
handleVia()1551   private void handleVia()
1552   {
1553     if (tunnelCheckbox.isSelected()) {
1554       viaUserInput.setEnabled(viaCheckbox.isSelected());
1555       viaHostInput.setEnabled(viaCheckbox.isSelected());
1556       viaPortInput.setEnabled(viaCheckbox.isSelected());
1557     }
1558   }
1559 
handleExtSSH()1560   private void handleExtSSH()
1561   {
1562     if (tunnelCheckbox.isSelected()) {
1563       sshClientInput.setEnabled(extSSHCheckbox.isSelected());
1564       sshClientChooser.setEnabled(extSSHCheckbox.isSelected());
1565       sshArgsDefaultButton.setEnabled(extSSHCheckbox.isSelected());
1566       sshArgsCustomButton.setEnabled(extSSHCheckbox.isSelected());
1567       sshConfigInput.setEnabled(!extSSHCheckbox.isSelected());
1568       sshConfigChooser.setEnabled(!extSSHCheckbox.isSelected());
1569       sshKeyFileInput.setEnabled(!extSSHCheckbox.isSelected());
1570       sshKeyFileChooser.setEnabled(!extSSHCheckbox.isSelected());
1571       if (sshArgsCustomButton.isSelected())
1572         sshArgsInput.setEnabled(extSSHCheckbox.isSelected());
1573       else
1574         sshArgsInput.setEnabled(false);
1575     }
1576   }
1577 
handleRfbState()1578   private void handleRfbState()
1579   {
1580     CConn cc = VncViewer.cc;
1581     if (cc != null && cc.state() == CConnection.stateEnum.RFBSTATE_NORMAL) {
1582       JComponent[] components = {
1583           encNoneCheckbox, encTLSCheckbox, encX509Checkbox, authNoneCheckbox,
1584           authVncCheckbox, authVncCheckbox, authIdentCheckbox, authPlainCheckbox,
1585           sendLocalUsernameCheckbox, caInput, caChooser, crlInput, crlChooser,
1586           sharedCheckbox, tunnelCheckbox, viaCheckbox, viaUserInput, viaHostInput,
1587           viaPortInput, extSSHCheckbox, sshClientInput, sshClientChooser,
1588           sshArgsDefaultButton, sshArgsCustomButton, sshArgsInput, sshConfigInput,
1589           sshKeyFileInput, sshConfigChooser, sshKeyFileChooser,
1590         };
1591       for (JComponent c : components)
1592         c.setEnabled(false);
1593     }
1594   }
1595 
1596   static LogWriter vlog = new LogWriter("OptionsDialog");
1597 }
1598