diff src/viewer_swing/java/com/glavsoft/viewer/Viewer.java @ 0:4689cc86d6cb

create TreeViewer2 Repository
author Yu Taninari <you@cr.ie.u-ryukyu.ac.jp>
date Tue, 03 Jul 2012 13:20:49 +0900
parents
children 1a30763734cf 17b702648079
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/viewer_swing/java/com/glavsoft/viewer/Viewer.java	Tue Jul 03 13:20:49 2012 +0900
@@ -0,0 +1,603 @@
+// Copyright (C) 2010, 2011 GlavSoft LLC.
+// All rights reserved.
+//
+//-------------------------------------------------------------------------
+// This file is part of the TightVNC software.  Please visit our Web site:
+//
+//                       http://www.tightvnc.com/
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+//-------------------------------------------------------------------------
+//
+
+package com.glavsoft.viewer;
+
+import com.glavsoft.core.SettingsChangedEvent;
+import com.glavsoft.exceptions.*;
+import com.glavsoft.rfb.IChangeSettingsListener;
+import com.glavsoft.rfb.IPasswordRetriever;
+import com.glavsoft.rfb.IRfbSessionListener;
+import com.glavsoft.rfb.client.KeyEventMessage;
+import com.glavsoft.rfb.protocol.Protocol;
+import com.glavsoft.rfb.protocol.ProtocolContext;
+import com.glavsoft.rfb.protocol.ProtocolSettings;
+import com.glavsoft.transport.Reader;
+import com.glavsoft.transport.Writer;
+import com.glavsoft.utils.Keymap;
+import com.glavsoft.utils.Strings;
+import com.glavsoft.viewer.cli.Parser;
+import com.glavsoft.viewer.swing.*;
+import com.glavsoft.viewer.swing.gui.OptionsDialog;
+import com.glavsoft.viewer.swing.gui.PasswordDialog;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.Dialog.ModalityType;
+import java.awt.event.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Socket;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.logging.Logger;
+
+@SuppressWarnings("serial")
+public class Viewer extends JApplet implements Runnable, IRfbSessionListener, WindowListener,
+		IChangeSettingsListener {
+	public static final int DEFAULT_PORT = 5900;
+
+	public static Logger logger = Logger.getLogger("com.glavsoft");
+	private static final Insets BUTTONS_MARGIN = new Insets(2, 2, 2, 2);
+	private boolean isZoomToFitSelected;
+	private boolean forceReconnection;
+	private String reconnectionReason;
+	private ContainerManager containerManager;
+
+	public Protocol getWorkingProtocol() {
+		return workingProtocol;
+	}
+
+	public boolean isZoomToFitSelected() {
+		return isZoomToFitSelected;
+	}
+
+	public Surface getSurface() {
+		return surface;
+	}
+
+	public UiSettings getUiSettings() {
+		return uiSettings;
+	}
+
+	public void setZoomToFitSelected(boolean zoomToFitSelected) {
+		isZoomToFitSelected = zoomToFitSelected;
+	}
+
+	/**
+	 * Ask user for password if needed
+	 */
+	public class PasswordChooser implements IPasswordRetriever {
+		private final String passwordPredefined;
+		private final ParametersHandler.ConnectionParams connectionParams;
+		PasswordDialog passwordDialog;
+		private final JFrame owner;
+		private final WindowListener onClose;
+
+		public PasswordChooser(String passwordPredefined, ParametersHandler.ConnectionParams connectionParams,
+				JFrame owner, WindowListener onClose) {
+			this.passwordPredefined = passwordPredefined;
+			this.connectionParams = connectionParams;
+			this.owner = owner;
+			this.onClose = onClose;
+		}
+
+		@Override
+		public String getPassword() {
+			return Strings.isTrimmedEmpty(passwordPredefined) ?
+					getPasswordFromGUI() :
+					passwordPredefined;
+		}
+
+		private String getPasswordFromGUI() {
+			if (null == passwordDialog) {
+				passwordDialog = new PasswordDialog(owner, onClose, isApplet);
+			}
+			passwordDialog.setServerHostName(connectionParams.hostName);
+			passwordDialog.setVisible(true);
+			return passwordDialog.getPassword();
+		}
+	}
+
+	public static void main(String[] args) {
+		Parser parser = new Parser();
+		ParametersHandler.completeParserOptions(parser);
+
+		parser.parse(args);
+		if (parser.isSet(ParametersHandler.ARG_HELP)) {
+			printUsage(parser.optionsUsage());
+			System.exit(0);
+		}
+		Viewer viewer = new Viewer(parser);
+		SwingUtilities.invokeLater(viewer);
+	}
+
+	public static void printUsage(String additional) {
+		System.out.println("Usage: java -jar (progfilename) [hostname [port_number]] [Options]\n" +
+				"    or\n"+
+				" java -jar (progfilename) [Options]\n" +
+				"    or\n java -jar (progfilename) -help\n    to view this help\n\n" +
+				"Where Options are:\n" + additional +
+				"\nOptions format: -optionName=optionValue. Ex. -host=localhost -port=5900 -viewonly=yes\n" +
+				"Both option name and option value are case insensitive.");
+	}
+
+
+	private final ParametersHandler.ConnectionParams connectionParams;
+	protected String passwordFromParams;
+	protected Socket workingSocket;
+	protected Protocol workingProtocol;
+	protected JFrame containerFrame;
+	boolean isSeparateFrame = true;
+	protected boolean isApplet = true;
+	protected boolean showControls = true;
+	protected Surface surface;
+	protected final ProtocolSettings settings;
+	protected final UiSettings uiSettings;
+    protected boolean tryAgain;
+	private boolean isAppletStopped = false;
+	private volatile boolean isStoppingProcess;
+	private List<JComponent> kbdButtons;
+
+	public Viewer() {
+		connectionParams = new ParametersHandler.ConnectionParams();
+		settings = ProtocolSettings.getDefaultSettings();
+		uiSettings = new UiSettings();
+	}
+
+	private Viewer(Parser parser) {
+		this();
+		ParametersHandler.completeSettingsFromCLI(parser, connectionParams, settings, uiSettings);
+		showControls = ParametersHandler.showControls;
+		passwordFromParams = parser.getValueFor(ParametersHandler.ARG_PASSWORD);
+		logger.info("TightVNC Viewer version " + ver());
+		isApplet = false;
+	}
+
+
+	@Override
+	public void rfbSessionStopped(final String reason) {
+		if (isStoppingProcess)
+			return;
+		cleanUpUISessionAndConnection();
+		SwingUtilities.invokeLater(new Runnable() {
+			@Override
+			public void run() {
+				forceReconnection = true;
+				reconnectionReason = reason;
+			}
+		});
+		// start new session
+		SwingUtilities.invokeLater(this);
+	}
+
+	private synchronized void cleanUpUISessionAndConnection() {
+		isStoppingProcess = true;
+		if (workingSocket != null && workingSocket.isConnected()) {
+			try {
+				workingSocket.close();
+			} catch (IOException e) { /*nop*/ }
+		}
+		if (containerFrame != null) {
+			containerFrame.dispose();
+			containerFrame = null;
+		}
+		isStoppingProcess = false;
+	}
+
+	@Override
+	public void windowClosing(WindowEvent e) {
+		if (e != null && e.getComponent() != null) {
+			e.getWindow().setVisible(false);
+		}
+		closeApp();
+	}
+
+	/**
+	 * Closes App(lication) or stops App(let).
+	 */
+	protected void closeApp() {
+		if (workingProtocol != null) {
+			workingProtocol.cleanUpSession();
+		}
+		cleanUpUISessionAndConnection();
+		tryAgain = false;
+		if (isApplet) {
+			logger.severe("Applet is stopped.");
+			isAppletStopped  = true;
+			repaint();
+		} else {
+			System.exit(0);
+		}
+	}
+
+	@Override
+	public void paint(Graphics g) {
+		if ( ! isAppletStopped) {
+			super.paint(g);
+		} else {
+			getContentPane().removeAll();
+			g.clearRect(0, 0, getWidth(), getHeight());
+			g.drawString("Disconnected", 10, 20);
+		}
+	}
+
+	@Override
+	public void destroy() {
+		closeApp();
+		super.destroy();
+	}
+
+	@Override
+	public void init() {
+		ParametersHandler.completeSettingsFromApplet(this, connectionParams, settings, uiSettings);
+		showControls = ParametersHandler.showControls;
+		isSeparateFrame = ParametersHandler.isSeparateFrame;
+		passwordFromParams = getParameter(ParametersHandler.ARG_PASSWORD);
+		isApplet = true;
+
+		repaint();
+		SwingUtilities.invokeLater(this);
+	}
+
+	@Override
+	public void start() {
+		setSurfaceToHandleKbdFocus();
+		super.start();
+	}
+
+	@Override
+	public void run() {
+		ConnectionManager connectionManager = new ConnectionManager(this, isApplet);
+
+		if (forceReconnection) {
+			connectionManager.showReconnectDialog("Connection lost", reconnectionReason);
+			forceReconnection = false;
+		}
+		tryAgain = true;
+		while (tryAgain) {
+			workingSocket = connectionManager.connectToHost(connectionParams, settings);
+			if (null == workingSocket) {
+				closeApp();
+				break;
+			}
+			logger.info("Connected");
+
+			try {
+				workingSocket.setTcpNoDelay(true); // disable Nagle algorithm
+				Reader reader = new Reader(workingSocket.getInputStream());
+				Writer writer = new Writer(workingSocket.getOutputStream());
+
+				workingProtocol = new Protocol(reader, writer,
+						new PasswordChooser(passwordFromParams, connectionParams, containerFrame, this),
+						settings);
+				workingProtocol.handshake();
+
+                ClipboardControllerImpl clipboardController =
+		                new ClipboardControllerImpl(workingProtocol, settings.getRemoteCharsetName());
+				clipboardController.setEnabled(settings.isAllowClipboardTransfer());
+				settings.addListener(clipboardController);
+
+				surface = new Surface(workingProtocol, this, uiSettings.getScaleFactor());
+				settings.addListener(this);
+				uiSettings.addListener(surface);
+				containerFrame = createContainer();
+				connectionManager.setContainerFrame(containerFrame);
+				updateFrameTitle();
+
+				workingProtocol.startNormalHandling(this, surface, clipboardController);
+				tryAgain = false;
+			} catch (UnsupportedProtocolVersionException e) {
+				connectionManager.showReconnectDialog("Unsupported Protocol Version", e.getMessage());
+				logger.severe(e.getMessage());
+			} catch (UnsupportedSecurityTypeException e) {
+				connectionManager.showReconnectDialog("Unsupported Security Type", e.getMessage());
+				logger.severe(e.getMessage());
+			} catch (AuthenticationFailedException e) {
+				passwordFromParams = null;
+				connectionManager.showReconnectDialog("Authentication Failed", e.getMessage());
+				logger.severe(e.getMessage());
+			} catch (TransportException e) {
+				connectionManager.showReconnectDialog("Connection Error", "Connection Error" + ": " + e.getMessage());
+				logger.severe(e.getMessage());
+			} catch (IOException e) {
+				connectionManager.showReconnectDialog("Connection Error", "Connection Error" + ": " + e.getMessage());
+				logger.severe(e.getMessage());
+			} catch (FatalException e) {
+				connectionManager.showReconnectDialog("Connection Error", "Connection Error" + ": " + e.getMessage());
+				logger.severe(e.getMessage());
+			}
+		}
+	}
+
+	protected JFrame createContainer() {
+		containerManager = new ContainerManager(this);
+		Container container = containerManager.createContainer(surface, isSeparateFrame, isApplet);
+
+		if (showControls) {
+			createButtonsPanel(container, workingProtocol, containerManager);
+			containerManager.registerResizeListener(container);
+			containerManager.updateZoomButtonsState();
+		}
+		setSurfaceToHandleKbdFocus();
+		return isSeparateFrame ? (JFrame)container : null;
+	}
+
+	public void packContainer() {
+		containerManager.pack();
+	}
+
+	protected void createButtonsPanel(final Container container, final ProtocolContext context, ContainerManager containerManager) {
+		JPanel buttonBar = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 1));
+
+		JButton optionsButton = new JButton(Utils.getButtonIcon("options"));
+		optionsButton.setToolTipText("Set Options");
+		optionsButton.setMargin(BUTTONS_MARGIN);
+		buttonBar.add(optionsButton);
+		optionsButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				showOptionsDialog();
+				setSurfaceToHandleKbdFocus();
+			}
+		});
+
+		JButton infoButton = new JButton(Utils.getButtonIcon("info"));
+		infoButton.setToolTipText("Show connection info");
+		infoButton.setMargin(BUTTONS_MARGIN);
+		buttonBar.add(infoButton);
+		infoButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				showConnectionInfoMessage(context.getRemoteDesktopName());
+				setSurfaceToHandleKbdFocus();
+			}
+		});
+
+		buttonBar.add(Box.createHorizontalStrut(10));
+		JButton refreshButton = new JButton(Utils.getButtonIcon("refresh"));
+		refreshButton.setToolTipText("Refresh screen");
+		refreshButton.setMargin(BUTTONS_MARGIN);
+		buttonBar.add(refreshButton);
+		refreshButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				context.sendRefreshMessage();
+				setSurfaceToHandleKbdFocus();
+			}
+		});
+
+		containerManager.addZoomButtons(buttonBar, BUTTONS_MARGIN);
+
+		kbdButtons = new LinkedList<JComponent>();
+		buttonBar.add(Box.createHorizontalStrut(10));
+		JButton ctrlAltDelButton = new JButton(Utils.getButtonIcon("ctrl-alt-del"));
+		ctrlAltDelButton.setToolTipText("Send 'Ctrl-Alt-Del'");
+		ctrlAltDelButton.setMargin(BUTTONS_MARGIN);
+		buttonBar.add(ctrlAltDelButton);
+		kbdButtons.add(ctrlAltDelButton);
+		ctrlAltDelButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				sendCtrlAltDel(context);
+				setSurfaceToHandleKbdFocus();
+			}
+		});
+
+		JButton winButton = new JButton(Utils.getButtonIcon("win"));
+		winButton.setToolTipText("Send 'Win' key as 'Ctrl-Esc'");
+		winButton.setMargin(BUTTONS_MARGIN);
+		buttonBar.add(winButton);
+		kbdButtons.add(winButton);
+		winButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				sendWinKey(context);
+				setSurfaceToHandleKbdFocus();
+			}
+		});
+
+		JToggleButton ctrlButton = new JToggleButton(Utils.getButtonIcon("ctrl"));
+		ctrlButton.setToolTipText("Ctrl Lock");
+		ctrlButton.setMargin(BUTTONS_MARGIN);
+		buttonBar.add(ctrlButton);
+		kbdButtons.add(ctrlButton);
+		ctrlButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				setSurfaceToHandleKbdFocus();
+			}
+		});
+		ctrlButton.addItemListener(new ItemListener() {
+			@Override
+			public void itemStateChanged(ItemEvent e) {
+				if (e.getStateChange() == ItemEvent.SELECTED) {
+					context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true));
+				} else {
+					context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false));
+				}
+			}
+		});
+
+		JToggleButton altButton = new JToggleButton(Utils.getButtonIcon("alt"));
+		kbdButtons.add(altButton);
+		altButton.setToolTipText("Alt Lock");
+		altButton.setMargin(BUTTONS_MARGIN);
+		buttonBar.add(altButton);
+		kbdButtons.add(altButton);
+		altButton.addItemListener(new ItemListener() {
+			@Override
+			public void itemStateChanged(ItemEvent e) {
+				if (e.getStateChange() == ItemEvent.SELECTED) {
+					context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, true));
+				} else {
+					context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, false));
+				}
+			}
+		});
+		altButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				setSurfaceToHandleKbdFocus();
+			}
+		});
+		ModifierButtonEventListener modifierButtonListener = new ModifierButtonEventListener();
+		modifierButtonListener.addButton(KeyEvent.VK_CONTROL, ctrlButton);
+		modifierButtonListener.addButton(KeyEvent.VK_ALT, altButton);
+		surface.addModifierListener(modifierButtonListener);
+
+//		JButton fileTransferButton = new JButton(Utils.getButtonIcon("file-transfer"));
+//		fileTransferButton.setMargin(buttonsMargin);
+//		buttonBar.add(fileTransferButton);
+
+		buttonBar.add(Box.createHorizontalStrut(10));
+
+		JButton closeButton = new JButton(Utils.getButtonIcon("close"));
+		closeButton.setToolTipText(isApplet ? "Disconnect" : "Close");
+		closeButton.setMargin(BUTTONS_MARGIN);
+		closeButton.setAlignmentX(RIGHT_ALIGNMENT);
+		buttonBar.add(closeButton);
+		closeButton.addActionListener(new ActionListener() {
+			@Override
+			public void actionPerformed(ActionEvent e) {
+				closeApp();
+			}
+		});
+
+		container.add(buttonBar, BorderLayout.NORTH);
+	}
+
+	protected void updateFrameTitle() {
+		if (containerFrame != null) {
+			containerFrame.setTitle(
+					workingProtocol.getRemoteDesktopName() + " [zoom: " + uiSettings.getScalePercentFormatted() + "%]");
+		}
+	}
+
+	protected void setSurfaceToHandleKbdFocus() {
+		if (surface != null && ! surface.requestFocusInWindow()) {
+			surface.requestFocus();
+		}
+	}
+
+	@Override
+	public void settingsChanged(SettingsChangedEvent e) {
+		ProtocolSettings settings = (ProtocolSettings) e.getSource();
+		setEnabledKbdButtons( ! settings.isViewOnly());
+	}
+
+	private void setEnabledKbdButtons(boolean enabled) {
+		if (kbdButtons != null) {
+			for (JComponent b : kbdButtons) {
+				b.setEnabled(enabled);
+			}
+		}
+	}
+
+	private void showOptionsDialog() {
+		OptionsDialog optionsDialog = new OptionsDialog(containerFrame);
+		optionsDialog.initControlsFromSettings(settings, false);
+		optionsDialog.setVisible(true);
+	}
+
+	private void showConnectionInfoMessage(final String title) {
+		StringBuilder message = new StringBuilder();
+		message.append("Connected to: ").append(title).append("\n");
+		message.append("Host: ").append(connectionParams.hostName)
+			.append(" Port: ").append(connectionParams.portNumber).append("\n\n");
+
+		message.append("Desktop geometry: ")
+			.append(String.valueOf(surface.getWidth()))
+			.append(" \u00D7 ") // multiplication sign
+			.append(String.valueOf(surface.getHeight())).append("\n");
+		message.append("Color format: ")
+			.append(String.valueOf(Math.round(Math.pow(2, workingProtocol.getPixelFormat().depth))))
+			.append(" colors (")
+			.append(String.valueOf(workingProtocol.getPixelFormat().depth))
+			.append(" bits)\n");
+		message.append("Current protocol version: ")
+			.append(settings.getProtocolVersion());
+		if (settings.isTight()) {
+			message.append("tight");
+		}
+		message.append("\n");
+
+		JOptionPane infoPane = new JOptionPane(message.toString(), JOptionPane.INFORMATION_MESSAGE);
+		final JDialog infoDialog = infoPane.createDialog(containerFrame, "VNC connection info");
+		infoDialog.setModalityType(ModalityType.MODELESS);
+		try {
+			infoDialog.setAlwaysOnTop(true);
+		} catch (SecurityException e) { /*nop*/ }
+		infoDialog.setVisible(true);
+	}
+
+	private void sendCtrlAltDel(ProtocolContext context) {
+		context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true));
+		context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, true));
+		context.sendMessage(new KeyEventMessage(Keymap.K_DELETE, true));
+		context.sendMessage(new KeyEventMessage(Keymap.K_DELETE, false));
+		context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, false));
+		context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false));
+	}
+
+	private void sendWinKey(ProtocolContext context) {
+		context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true));
+		context.sendMessage(new KeyEventMessage(Keymap.K_ESCAPE, true));
+		context.sendMessage(new KeyEventMessage(Keymap.K_ESCAPE, false));
+		context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false));
+	}
+
+	@Override
+	public void windowOpened(WindowEvent e) { /* nop */ }
+	@Override
+	public void windowClosed(WindowEvent e) { /* nop */ }
+	@Override
+	public void windowIconified(WindowEvent e) { /* nop */ }
+	@Override
+	public void windowDeiconified(WindowEvent e) { /* nop */ }
+	@Override
+	public void windowActivated(WindowEvent e) { /* nop */ }
+	@Override
+	public void windowDeactivated(WindowEvent e) { /* nop */ }
+
+	protected static String ver() {
+		final InputStream mfStream = Viewer.class.getClassLoader().getResourceAsStream(
+				"META-INF/MANIFEST.MF");
+		if (null == mfStream) {
+			System.out.println("No Manifest file found.");
+			return "-1";
+		}
+		try {
+			Manifest mf = new Manifest();
+			mf.read(mfStream);
+			Attributes atts = mf.getMainAttributes();
+			return atts.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
+		} catch (IOException e) {
+			return "-2";
+		}
+	}
+
+}