0
|
1 // Copyright (C) 2010, 2011, 2012, 2013 GlavSoft LLC.
|
|
2 // All rights reserved.
|
|
3 //
|
|
4 //-------------------------------------------------------------------------
|
|
5 // This file is part of the TightVNC software. Please visit our Web site:
|
|
6 //
|
|
7 // http://www.tightvnc.com/
|
|
8 //
|
|
9 // This program is free software; you can redistribute it and/or modify
|
|
10 // it under the terms of the GNU General Public License as published by
|
|
11 // the Free Software Foundation; either version 2 of the License, or
|
|
12 // (at your option) any later version.
|
|
13 //
|
|
14 // This program is distributed in the hope that it will be useful,
|
|
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17 // GNU General Public License for more details.
|
|
18 //
|
|
19 // You should have received a copy of the GNU General Public License along
|
|
20 // with this program; if not, write to the Free Software Foundation, Inc.,
|
|
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
22 //-------------------------------------------------------------------------
|
|
23 //
|
|
24
|
|
25 package com.glavsoft.viewer.swing;
|
|
26
|
16
|
27 import alice.datasegment.DataSegment;
|
|
28
|
0
|
29 import com.glavsoft.core.SettingsChangedEvent;
|
|
30 import com.glavsoft.rfb.IChangeSettingsListener;
|
|
31 import com.glavsoft.rfb.client.KeyEventMessage;
|
|
32 import com.glavsoft.rfb.protocol.Protocol;
|
|
33 import com.glavsoft.rfb.protocol.ProtocolContext;
|
|
34 import com.glavsoft.rfb.protocol.ProtocolSettings;
|
|
35 import com.glavsoft.utils.Keymap;
|
|
36 import com.glavsoft.viewer.ConnectionPresenter;
|
|
37 import com.glavsoft.viewer.UiSettings;
|
|
38 import com.glavsoft.viewer.Viewer;
|
|
39 import com.glavsoft.viewer.swing.gui.OptionsDialog;
|
|
40
|
|
41 import javax.swing.*;
|
|
42 import javax.swing.border.BevelBorder;
|
|
43 import javax.swing.border.Border;
|
|
44 import javax.swing.border.EmptyBorder;
|
16
|
45
|
0
|
46 import java.awt.*;
|
|
47 import java.awt.event.*;
|
|
48 import java.util.LinkedList;
|
|
49 import java.util.List;
|
|
50 import java.util.concurrent.Executors;
|
|
51 import java.util.concurrent.ScheduledExecutorService;
|
|
52 import java.util.concurrent.ScheduledFuture;
|
|
53 import java.util.concurrent.TimeUnit;
|
|
54 import java.util.logging.Logger;
|
|
55
|
|
56 public class SwingViewerWindow implements IChangeSettingsListener {
|
|
57 public static final int FS_SCROLLING_ACTIVE_BORDER = 20;
|
|
58 private JToggleButton zoomFitButton;
|
|
59 private JToggleButton zoomFullScreenButton;
|
|
60 private JButton zoomInButton;
|
|
61 private JButton zoomOutButton;
|
|
62 private JButton zoomAsIsButton;
|
|
63 private JPanel outerPanel;
|
|
64 private JScrollPane scroller;
|
|
65 private JFrame frame;
|
|
66 private boolean forceResizable = true;
|
|
67 private ButtonsBar buttonsBar;
|
|
68 private Surface surface;
|
|
69 private boolean isSeparateFrame;
|
|
70 private final boolean isApplet;
|
|
71 private Viewer viewer;
|
|
72 private String connectionString;
|
|
73 private ConnectionPresenter presenter;
|
|
74 private Rectangle oldContainerBounds;
|
|
75 private volatile boolean isFullScreen;
|
|
76 private Border oldScrollerBorder;
|
|
77 private JLayeredPane lpane;
|
|
78 private EmptyButtonsBarMouseAdapter buttonsBarMouseAdapter;
|
|
79 private String remoteDesktopName;
|
|
80 private ProtocolSettings rfbSettings;
|
|
81 private UiSettings uiSettings;
|
|
82 private Protocol workingProtocol;
|
|
83
|
|
84 private boolean isZoomToFitSelected;
|
|
85 private List<JComponent> kbdButtons;
|
|
86
|
|
87 public SwingViewerWindow(Protocol workingProtocol, ProtocolSettings rfbSettings, UiSettings uiSettings, Surface surface,
|
|
88 boolean isSeparateFrame, boolean isApplet, Viewer viewer, String connectionString,
|
|
89 ConnectionPresenter presenter) {
|
|
90 this.workingProtocol = workingProtocol;
|
|
91 this.rfbSettings = rfbSettings;
|
|
92 this.uiSettings = uiSettings;
|
|
93 this.surface = surface;
|
|
94 this.isSeparateFrame = isSeparateFrame;
|
|
95 this.isApplet = isApplet;
|
|
96 this.viewer = viewer;
|
|
97 this.connectionString = connectionString;
|
|
98 this.presenter = presenter;
|
|
99 createContainer(surface, isApplet, viewer);
|
|
100
|
|
101 if (uiSettings.showControls) {
|
|
102 createButtonsPanel(workingProtocol, isSeparateFrame? frame: viewer);
|
|
103 if (isSeparateFrame) registerResizeListener(frame);
|
|
104 updateZoomButtonsState();
|
|
105 }
|
|
106 if (uiSettings.isFullScreen()) {
|
|
107 switchOnFullscreenMode();
|
|
108 }
|
|
109 setSurfaceToHandleKbdFocus();
|
|
110 if (isSeparateFrame) {
|
|
111 new Thread(new Runnable() {
|
|
112 @Override
|
|
113 public void run() {
|
|
114 try {
|
|
115 Thread.sleep(1000);
|
|
116 } catch (InterruptedException e) {
|
|
117 // nop
|
|
118 }
|
|
119 SwingUtilities.invokeLater(new Runnable() {
|
|
120 @Override
|
|
121 public void run() {
|
|
122 frame.toFront();
|
|
123 }
|
|
124 });
|
|
125 }
|
|
126 }).start();
|
|
127 }
|
|
128 }
|
|
129
|
|
130 private void createContainer(final Surface surface, boolean isApplet, JApplet appletWindow) {
|
|
131 outerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)) {
|
|
132 @Override
|
|
133 public Dimension getSize() {
|
|
134 return surface.getPreferredSize();
|
|
135 }
|
|
136 @Override
|
|
137 public Dimension getPreferredSize() {
|
|
138 return surface.getPreferredSize();
|
|
139 }
|
|
140 };
|
|
141 outerPanel.setBackground(Color.DARK_GRAY);
|
|
142 lpane = new JLayeredPane() {
|
|
143 @Override
|
|
144 public Dimension getSize() {
|
|
145 return surface.getPreferredSize();
|
|
146 }
|
|
147 @Override
|
|
148 public Dimension getPreferredSize() {
|
|
149 return surface.getPreferredSize();
|
|
150 }
|
|
151 };
|
|
152 lpane.setPreferredSize(surface.getPreferredSize());
|
|
153 lpane.add(surface, JLayeredPane.DEFAULT_LAYER, 0);
|
|
154 outerPanel.add(lpane);
|
|
155
|
|
156 scroller = new JScrollPane(outerPanel);
|
|
157 if (isSeparateFrame) {
|
|
158 frame = new JFrame();
|
|
159 if ( ! isApplet) {
|
|
160 frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
|
161 }
|
|
162 frame.setModalExclusionType(Dialog.ModalExclusionType.APPLICATION_EXCLUDE);
|
|
163 Utils.setApplicationIconsForWindow(frame);
|
|
164 frame.setLayout(new BorderLayout(0, 0));
|
|
165 frame.add(scroller, BorderLayout.CENTER);
|
|
166
|
|
167 // frame.pack();
|
|
168 outerPanel.setSize(surface.getPreferredSize());
|
|
169 internalPack(null);
|
|
170 frame.setVisible(true);
|
|
171 frame.validate();
|
|
172 } else {
|
|
173 appletWindow.setLayout(new BorderLayout(0, 0));
|
|
174 appletWindow.add(scroller, BorderLayout.CENTER);
|
|
175 appletWindow.validate();
|
|
176 }
|
|
177 }
|
|
178
|
|
179 public void pack() {
|
|
180 final Dimension outerPanelOldSize = outerPanel.getSize();
|
|
181 outerPanel.setSize(surface.getPreferredSize());
|
|
182 if (isSeparateFrame && ! isZoomToFitSelected()) {
|
|
183 internalPack(outerPanelOldSize);
|
|
184 }
|
|
185 if (buttonsBar != null) {
|
|
186 updateZoomButtonsState();
|
|
187 }
|
|
188 updateWindowTitle();
|
|
189 }
|
|
190
|
|
191 public boolean isZoomToFitSelected() {
|
|
192 return isZoomToFitSelected;
|
|
193 }
|
|
194
|
|
195 public void setZoomToFitSelected(boolean zoomToFitSelected) {
|
|
196 isZoomToFitSelected = zoomToFitSelected;
|
|
197 }
|
|
198
|
|
199 public void setRemoteDesktopName(String name) {
|
|
200 remoteDesktopName = name;
|
|
201 updateWindowTitle();
|
|
202 }
|
|
203
|
|
204 private void updateWindowTitle() {
|
|
205 if (isSeparateFrame) {
|
|
206 frame.setTitle(remoteDesktopName + " [zoom: " + uiSettings.getScalePercentFormatted() + "%]");
|
|
207 }
|
|
208 }
|
|
209
|
|
210 private void internalPack(Dimension outerPanelOldSize) {
|
|
211 final Rectangle workareaRectangle = getWorkareaRectangle();
|
|
212 if (workareaRectangle.equals(frame.getBounds())) {
|
|
213 forceResizable = true;
|
|
214 }
|
|
215 final boolean isHScrollBar = scroller.getHorizontalScrollBar().isShowing() && ! forceResizable;
|
|
216 final boolean isVScrollBar = scroller.getVerticalScrollBar().isShowing() && ! forceResizable;
|
|
217
|
|
218 boolean isWidthChangeable = true;
|
|
219 boolean isHeightChangeable = true;
|
|
220 if (outerPanelOldSize != null && surface.oldSize != null) {
|
|
221 isWidthChangeable = forceResizable ||
|
|
222 (outerPanelOldSize.width == surface.oldSize.width && ! isHScrollBar);
|
|
223 isHeightChangeable = forceResizable ||
|
|
224 (outerPanelOldSize.height == surface.oldSize.height && ! isVScrollBar);
|
|
225 }
|
|
226 forceResizable = false;
|
|
227 frame.validate();
|
|
228
|
|
229 final Insets containerInsets = frame.getInsets();
|
|
230 Dimension preferredSize = frame.getPreferredSize();
|
|
231 Rectangle preferredRectangle = new Rectangle(frame.getLocation(), preferredSize);
|
|
232
|
|
233 if (null == outerPanelOldSize && workareaRectangle.contains(preferredRectangle)) {
|
|
234 frame.pack();
|
|
235 } else {
|
|
236 Dimension minDimension = new Dimension(
|
|
237 containerInsets.left + containerInsets.right, containerInsets.top + containerInsets.bottom);
|
|
238 if (buttonsBar != null && buttonsBar.isVisible) {
|
|
239 minDimension.width += buttonsBar.getWidth();
|
|
240 minDimension.height += buttonsBar.getHeight();
|
|
241 }
|
|
242 Dimension dim = new Dimension(preferredSize);
|
|
243 Point location = frame.getLocation();
|
|
244 if ( ! isWidthChangeable) {
|
|
245 dim.width = frame.getWidth();
|
|
246 } else {
|
|
247 if (isVScrollBar) dim.width += scroller.getVerticalScrollBar().getWidth();
|
|
248 if (dim.width < minDimension.width) dim.width = minDimension.width;
|
|
249
|
|
250 int dx = location.x - workareaRectangle.x;
|
|
251 if (dx < 0) {
|
|
252 dx = 0;
|
|
253 location.x = workareaRectangle.x;
|
|
254 }
|
|
255 int w = workareaRectangle.width - dx;
|
|
256 if (w < dim.width) {
|
|
257 int dw = dim.width - w;
|
|
258 if (dw < dx) {
|
|
259 location.x -= dw;
|
|
260 } else {
|
|
261 dim.width = workareaRectangle.width;
|
|
262 location.x = workareaRectangle.x;
|
|
263 }
|
|
264 }
|
|
265 }
|
|
266 if ( ! isHeightChangeable) {
|
|
267 dim.height = frame.getHeight();
|
|
268 } else {
|
|
269
|
|
270 if (isHScrollBar) dim.height += scroller.getHorizontalScrollBar().getHeight();
|
|
271 if (dim.height < minDimension.height) dim.height = minDimension.height;
|
|
272
|
|
273 int dy = location.y - workareaRectangle.y;
|
|
274 if (dy < 0) {
|
|
275 dy = 0;
|
|
276 location.y = workareaRectangle.y;
|
|
277 }
|
|
278 int h = workareaRectangle.height - dy;
|
|
279 if (h < dim.height) {
|
|
280 int dh = dim.height - h;
|
|
281 if (dh < dy) {
|
|
282 location.y -= dh;
|
|
283 } else {
|
|
284 dim.height = workareaRectangle.height;
|
|
285 location.y = workareaRectangle.y;
|
|
286 }
|
|
287 }
|
|
288 }
|
|
289 if ( ! location.equals(frame.getLocation())) {
|
|
290 frame.setLocation(location);
|
|
291 }
|
|
292 if ( ! isFullScreen ) {
|
|
293 frame.setSize(dim);
|
|
294 }
|
|
295 }
|
|
296 scroller.revalidate();
|
|
297 }
|
|
298
|
|
299 private Rectangle getWorkareaRectangle() {
|
|
300 final GraphicsConfiguration graphicsConfiguration = frame.getGraphicsConfiguration();
|
|
301 final Rectangle screenBounds = graphicsConfiguration.getBounds();
|
|
302 final Insets screenInsets = Toolkit.getDefaultToolkit().getScreenInsets(graphicsConfiguration);
|
|
303
|
|
304 screenBounds.x += screenInsets.left;
|
|
305 screenBounds.y += screenInsets.top;
|
|
306 screenBounds.width -= screenInsets.left + screenInsets.right;
|
|
307 screenBounds.height -= screenInsets.top + screenInsets.bottom;
|
|
308 return screenBounds;
|
|
309 }
|
|
310
|
|
311 void addZoomButtons() {
|
|
312 buttonsBar.createStrut();
|
|
313 zoomOutButton = buttonsBar.createButton("zoom-out", "Zoom Out", new ActionListener() {
|
|
314 @Override
|
|
315 public void actionPerformed(ActionEvent e) {
|
|
316 zoomFitButton.setSelected(false);
|
|
317 uiSettings.zoomOut();
|
|
318 }
|
|
319 });
|
|
320 zoomInButton = buttonsBar.createButton("zoom-in", "Zoom In", new ActionListener() {
|
|
321 @Override
|
|
322 public void actionPerformed(ActionEvent e) {
|
|
323 zoomFitButton.setSelected(false);
|
|
324 uiSettings.zoomIn();
|
|
325 }
|
|
326 });
|
|
327 zoomAsIsButton = buttonsBar.createButton("zoom-100", "Zoom 100%", new ActionListener() {
|
|
328 @Override
|
|
329 public void actionPerformed(ActionEvent e) {
|
|
330 zoomFitButton.setSelected(false);
|
|
331 forceResizable = false;
|
|
332 uiSettings.zoomAsIs();
|
|
333 }
|
|
334 });
|
|
335
|
|
336 zoomFitButton = buttonsBar.createToggleButton("zoom-fit", "Zoom to Fit Window",
|
|
337 new ItemListener() {
|
|
338 @Override
|
|
339 public void itemStateChanged(ItemEvent e) {
|
|
340 if (e.getStateChange() == ItemEvent.SELECTED) {
|
|
341 setZoomToFitSelected(true);
|
|
342 forceResizable = true;
|
|
343 zoomToFit();
|
|
344 updateZoomButtonsState();
|
|
345 } else {
|
|
346 setZoomToFitSelected(false);
|
|
347 }
|
|
348 setSurfaceToHandleKbdFocus();
|
|
349 }
|
|
350 });
|
|
351
|
|
352 zoomFullScreenButton = buttonsBar.createToggleButton("zoom-fullscreen", "Full Screen",
|
|
353 new ItemListener() {
|
|
354 @Override
|
|
355 public void itemStateChanged(ItemEvent e) {
|
|
356 updateZoomButtonsState();
|
|
357 if (e.getStateChange() == ItemEvent.SELECTED) {
|
|
358 uiSettings.setFullScreen(switchOnFullscreenMode());
|
|
359 } else {
|
|
360 switchOffFullscreenMode();
|
|
361 uiSettings.setFullScreen(false);
|
|
362 }
|
|
363 setSurfaceToHandleKbdFocus();
|
|
364 }
|
|
365 });
|
|
366 if ( ! isSeparateFrame) {
|
|
367 zoomFullScreenButton.setEnabled(false);
|
|
368 zoomFitButton.setEnabled(false);
|
|
369 }
|
|
370 }
|
|
371
|
|
372 protected void setSurfaceToHandleKbdFocus() {
|
|
373 if (surface != null && ! surface.requestFocusInWindow()) {
|
|
374 surface.requestFocus();
|
|
375 }
|
|
376 }
|
|
377
|
|
378 boolean switchOnFullscreenMode() {
|
|
379 zoomFullScreenButton.setSelected(true);
|
|
380 oldContainerBounds = frame.getBounds();
|
|
381 setButtonsBarVisible(false);
|
|
382 forceResizable = true;
|
|
383 frame.dispose();
|
|
384 frame.setUndecorated(true);
|
|
385 frame.setResizable(false);
|
|
386 frame.setVisible(true);
|
|
387 try {
|
|
388 frame.getGraphicsConfiguration().getDevice().setFullScreenWindow(frame);
|
|
389 isFullScreen = true;
|
|
390 scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
|
|
391 scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
|
392 oldScrollerBorder = scroller.getBorder();
|
|
393 scroller.setBorder(new EmptyBorder(0, 0, 0, 0));
|
|
394 new FullscreenBorderDetectionThread(frame).start();
|
|
395 } catch (Exception ex) {
|
|
396 Logger.getLogger(this.getClass().getName()).info("Cannot switch into FullScreen mode: " + ex.getMessage());
|
|
397 return false;
|
|
398 }
|
|
399 return true;
|
|
400 }
|
|
401
|
|
402 private void switchOffFullscreenMode() {
|
|
403 if (isFullScreen) {
|
|
404 zoomFullScreenButton.setSelected(false);
|
|
405 isFullScreen = false;
|
|
406 setButtonsBarVisible(true);
|
|
407 try {
|
|
408 frame.dispose();
|
|
409 frame.setUndecorated(false);
|
|
410 frame.setResizable(true);
|
|
411 frame.getGraphicsConfiguration().getDevice().setFullScreenWindow(null);
|
|
412 } catch (Exception e) {
|
|
413 // nop
|
|
414 }
|
|
415 scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
|
|
416 scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
|
417 scroller.setBorder(oldScrollerBorder);
|
|
418 this.frame.setBounds(oldContainerBounds);
|
|
419 frame.setVisible(true);
|
|
420 pack();
|
|
421 }
|
|
422 }
|
|
423
|
|
424 private void zoomToFit() {
|
|
425 Dimension scrollerSize = scroller.getSize();
|
|
426 Insets scrollerInsets = scroller.getInsets();
|
|
427 uiSettings.zoomToFit(scrollerSize.width - scrollerInsets.left - scrollerInsets.right,
|
|
428 scrollerSize.height - scrollerInsets.top - scrollerInsets.bottom +
|
|
429 (isFullScreen ? buttonsBar.getHeight() : 0),
|
|
430 workingProtocol.getFbWidth(), workingProtocol.getFbHeight());
|
|
431 }
|
|
432
|
|
433 void registerResizeListener(Container container) {
|
|
434 container.addComponentListener(new ComponentAdapter() {
|
|
435 @Override
|
|
436 public void componentResized(ComponentEvent e) {
|
|
437 if (isZoomToFitSelected()) {
|
|
438 zoomToFit();
|
|
439 updateZoomButtonsState();
|
|
440 updateWindowTitle();
|
|
441 setSurfaceToHandleKbdFocus();
|
|
442 }
|
|
443 }
|
|
444 });
|
|
445 }
|
|
446
|
|
447 void updateZoomButtonsState() {
|
|
448 zoomOutButton.setEnabled(uiSettings.getScalePercent() > UiSettings.MIN_SCALE_PERCENT);
|
|
449 zoomInButton.setEnabled(uiSettings.getScalePercent() < UiSettings.MAX_SCALE_PERCENT);
|
|
450 zoomAsIsButton.setEnabled(uiSettings.getScalePercent() != 100);
|
|
451 }
|
|
452
|
|
453 public ButtonsBar createButtonsBar() {
|
|
454 buttonsBar = new ButtonsBar();
|
|
455 return buttonsBar;
|
|
456 }
|
|
457
|
|
458 public void setButtonsBarVisible(boolean isVisible) {
|
|
459 setButtonsBarVisible(isVisible, frame);
|
|
460 }
|
|
461
|
|
462 private void setButtonsBarVisible(boolean isVisible, Container container) {
|
|
463 buttonsBar.setVisible(isVisible);
|
|
464 if (isVisible) {
|
|
465 buttonsBar.borderOff();
|
|
466 container.add(buttonsBar.bar, BorderLayout.NORTH);
|
|
467 container.validate();
|
|
468 } else {
|
|
469 container.remove(buttonsBar.bar);
|
|
470 buttonsBar.borderOn();
|
|
471 }
|
|
472 }
|
|
473
|
|
474 public void setButtonsBarVisibleFS(boolean isVisible) {
|
|
475 if (isVisible) {
|
|
476 if ( ! buttonsBar.isVisible) {
|
|
477 lpane.add(buttonsBar.bar, JLayeredPane.POPUP_LAYER, 0);
|
|
478 final int bbWidth = buttonsBar.bar.getPreferredSize().width;
|
|
479 buttonsBar.bar.setBounds(
|
|
480 scroller.getViewport().getViewPosition().x + (scroller.getWidth() - bbWidth)/2, 0,
|
|
481 bbWidth, buttonsBar.bar.getPreferredSize().height);
|
|
482
|
|
483 // prevent mouse events to through down to Surface
|
|
484 if (null == buttonsBarMouseAdapter) buttonsBarMouseAdapter = new EmptyButtonsBarMouseAdapter();
|
|
485 buttonsBar.bar.addMouseListener(buttonsBarMouseAdapter);
|
|
486 }
|
|
487 } else {
|
|
488 buttonsBar.bar.removeMouseListener(buttonsBarMouseAdapter);
|
|
489 lpane.remove(buttonsBar.bar);
|
|
490 lpane.repaint(buttonsBar.bar.getBounds());
|
|
491 }
|
|
492 buttonsBar.setVisible(isVisible);
|
|
493 }
|
|
494
|
|
495 public Surface getSurface() {
|
|
496 return surface;
|
|
497 }
|
|
498
|
|
499 void close() {
|
|
500 if (isSeparateFrame && frame != null) {
|
|
501 frame.setVisible(false);
|
|
502 frame.dispose();
|
|
503 }
|
|
504 }
|
|
505
|
|
506 public static class ButtonsBar {
|
|
507 private static final Insets BUTTONS_MARGIN = new Insets(2, 2, 2, 2);
|
|
508 private JPanel bar;
|
|
509 private boolean isVisible;
|
|
510
|
|
511 public ButtonsBar() {
|
|
512 bar = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 1));
|
|
513 }
|
|
514
|
|
515 public JButton createButton(String iconId, String tooltipText, ActionListener actionListener) {
|
|
516 JButton button = new JButton(Utils.getButtonIcon(iconId));
|
|
517 button.setToolTipText(tooltipText);
|
|
518 button.setMargin(BUTTONS_MARGIN);
|
|
519 bar.add(button);
|
|
520 button.addActionListener(actionListener);
|
|
521 return button;
|
|
522 }
|
|
523
|
|
524 public void createStrut() {
|
|
525 bar.add(Box.createHorizontalStrut(10));
|
|
526 }
|
|
527
|
|
528 public JToggleButton createToggleButton(String iconId, String tooltipText, ItemListener itemListener) {
|
|
529 JToggleButton button = new JToggleButton(Utils.getButtonIcon(iconId));
|
|
530 button.setToolTipText(tooltipText);
|
|
531 button.setMargin(BUTTONS_MARGIN);
|
|
532 bar.add(button);
|
|
533 button.addItemListener(itemListener);
|
|
534 return button;
|
|
535 }
|
|
536
|
|
537 public void setVisible(boolean isVisible) {
|
|
538 this.isVisible = isVisible;
|
|
539 if (isVisible) bar.revalidate();
|
|
540 }
|
|
541
|
|
542 public int getWidth() {
|
|
543 return bar.getMinimumSize().width;
|
|
544 }
|
|
545 public int getHeight() {
|
|
546 return bar.getMinimumSize().height;
|
|
547 }
|
|
548
|
|
549 public void borderOn() {
|
|
550 bar.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
|
|
551 }
|
|
552
|
|
553 public void borderOff() {
|
|
554 bar.setBorder(BorderFactory.createEmptyBorder());
|
|
555 }
|
|
556 }
|
|
557
|
|
558 private static class EmptyButtonsBarMouseAdapter extends MouseAdapter {
|
|
559 // empty
|
|
560 }
|
|
561
|
|
562 private class FullscreenBorderDetectionThread extends Thread {
|
|
563 public static final int SHOW_HIDE_BUTTONS_BAR_DELAY_IN_MILLS = 700;
|
|
564 private final JFrame frame;
|
|
565 private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
|
|
566 private ScheduledFuture<?> futureForShow;
|
|
567 private ScheduledFuture<?> futureForHide;
|
|
568 private Point mousePoint, oldMousePoint;
|
|
569 private Point viewPosition;
|
|
570
|
|
571 public FullscreenBorderDetectionThread(JFrame frame) {
|
|
572 super("FS border detector");
|
|
573 this.frame = frame;
|
|
574 }
|
|
575
|
|
576 public void run() {
|
|
577 setPriority(Thread.MIN_PRIORITY);
|
|
578 while(isFullScreen) {
|
|
579 mousePoint = MouseInfo.getPointerInfo().getLocation();
|
|
580 if (null == oldMousePoint) oldMousePoint = mousePoint;
|
|
581 SwingUtilities.convertPointFromScreen(mousePoint, frame);
|
|
582 viewPosition = scroller.getViewport().getViewPosition();
|
|
583 processButtonsBarVisibility();
|
|
584
|
|
585 boolean needScrolling = processVScroll() || processHScroll();
|
|
586 oldMousePoint = mousePoint;
|
|
587 if (needScrolling) {
|
|
588 cancelShowExecutor();
|
|
589 setButtonsBarVisibleFS(false);
|
|
590 makeScrolling(viewPosition);
|
|
591 }
|
|
592 try {
|
|
593 Thread.sleep(100);
|
|
594 } catch (Exception e) {
|
|
595 // nop
|
|
596 }
|
|
597 }
|
|
598 }
|
|
599
|
|
600 private boolean processHScroll() {
|
|
601 if (mousePoint.x < FS_SCROLLING_ACTIVE_BORDER) {
|
|
602 if (viewPosition.x > 0) {
|
|
603 int delta = FS_SCROLLING_ACTIVE_BORDER - mousePoint.x;
|
|
604 if (mousePoint.y != oldMousePoint.y) delta *= 2; // speedify scrolling on mouse moving
|
|
605 viewPosition.x -= delta;
|
|
606 if (viewPosition.x < 0) viewPosition.x = 0;
|
|
607 return true;
|
|
608 }
|
|
609 } else if (mousePoint.x > (frame.getWidth() - FS_SCROLLING_ACTIVE_BORDER)) {
|
|
610 final Rectangle viewRect = scroller.getViewport().getViewRect();
|
|
611 final int right = viewRect.width + viewRect.x;
|
|
612 if (right < outerPanel.getSize().width) {
|
|
613 int delta = FS_SCROLLING_ACTIVE_BORDER - (frame.getWidth() - mousePoint.x);
|
|
614 if (mousePoint.y != oldMousePoint.y) delta *= 2; // speedify scrolling on mouse moving
|
|
615 viewPosition.x += delta;
|
|
616 if (viewPosition.x + viewRect.width > outerPanel.getSize().width) viewPosition.x =
|
|
617 outerPanel.getSize().width - viewRect.width;
|
|
618 return true;
|
|
619 }
|
|
620 }
|
|
621 return false;
|
|
622 }
|
|
623
|
|
624 private boolean processVScroll() {
|
|
625 if (mousePoint.y < FS_SCROLLING_ACTIVE_BORDER) {
|
|
626 if (viewPosition.y > 0) {
|
|
627 int delta = FS_SCROLLING_ACTIVE_BORDER - mousePoint.y;
|
|
628 if (mousePoint.x != oldMousePoint.x) delta *= 2; // speedify scrolling on mouse moving
|
|
629 viewPosition.y -= delta;
|
|
630 if (viewPosition.y < 0) viewPosition.y = 0;
|
|
631 return true;
|
|
632 }
|
|
633 } else if (mousePoint.y > (frame.getHeight() - FS_SCROLLING_ACTIVE_BORDER)) {
|
|
634 final Rectangle viewRect = scroller.getViewport().getViewRect();
|
|
635 final int bottom = viewRect.height + viewRect.y;
|
|
636 if (bottom < outerPanel.getSize().height) {
|
|
637 int delta = FS_SCROLLING_ACTIVE_BORDER - (frame.getHeight() - mousePoint.y);
|
|
638 if (mousePoint.x != oldMousePoint.x) delta *= 2; // speedify scrolling on mouse moving
|
|
639 viewPosition.y += delta;
|
|
640 if (viewPosition.y + viewRect.height > outerPanel.getSize().height) viewPosition.y =
|
|
641 outerPanel.getSize().height - viewRect.height;
|
|
642 return true;
|
|
643 }
|
|
644 }
|
|
645 return false;
|
|
646 }
|
|
647
|
|
648 private void processButtonsBarVisibility() {
|
|
649 if (mousePoint.y < 1) {
|
|
650 cancelHideExecutor();
|
|
651 // show buttons bar after delay
|
|
652 if (! buttonsBar.isVisible && (null == futureForShow || futureForShow.isDone())) {
|
|
653 futureForShow = scheduler.schedule(new Runnable() {
|
|
654 @Override
|
|
655 public void run() {
|
|
656 showButtonsBar();
|
|
657 }
|
|
658 }, SHOW_HIDE_BUTTONS_BAR_DELAY_IN_MILLS, TimeUnit.MILLISECONDS);
|
|
659 }
|
|
660 } else {
|
|
661 cancelShowExecutor();
|
|
662 }
|
|
663 if (buttonsBar.isVisible && mousePoint.y <= buttonsBar.getHeight()) {
|
|
664 cancelHideExecutor();
|
|
665 }
|
|
666 if (buttonsBar.isVisible && mousePoint.y > buttonsBar.getHeight()) {
|
|
667 // hide buttons bar after delay
|
|
668 if (null == futureForHide || futureForHide.isDone()) {
|
|
669 futureForHide = scheduler.schedule(new Runnable() {
|
|
670 @Override
|
|
671 public void run() {
|
|
672 SwingUtilities.invokeLater(new Runnable() {
|
|
673 @Override
|
|
674 public void run() {
|
|
675 setButtonsBarVisibleFS(false);
|
|
676 SwingViewerWindow.this.frame.validate();
|
|
677 }
|
|
678 });
|
|
679 }
|
|
680 }, SHOW_HIDE_BUTTONS_BAR_DELAY_IN_MILLS, TimeUnit.MILLISECONDS);
|
|
681 }
|
|
682 }
|
|
683 }
|
|
684
|
|
685 private void cancelHideExecutor() {
|
|
686 cancelExecutor(futureForHide);
|
|
687 }
|
|
688 private void cancelShowExecutor() {
|
|
689 cancelExecutor(futureForShow);
|
|
690 }
|
|
691
|
|
692 private void cancelExecutor(ScheduledFuture<?> future) {
|
|
693 if (future != null && ! future.isDone()) {
|
|
694 future.cancel(true);
|
|
695 }
|
|
696 }
|
|
697
|
|
698 private void makeScrolling(final Point viewPosition) {
|
|
699 SwingUtilities.invokeLater(new Runnable() {
|
|
700 @Override
|
|
701 public void run() {
|
|
702 scroller.getViewport().setViewPosition(viewPosition);
|
|
703 final Point mousePosition = surface.getMousePosition();
|
|
704 if (mousePosition != null) {
|
|
705 final MouseEvent mouseEvent = new MouseEvent(frame, 0, 0, 0,
|
|
706 mousePosition.x, mousePosition.y, 0, false);
|
|
707 for (MouseMotionListener mml : surface.getMouseMotionListeners()) {
|
|
708 mml.mouseMoved(mouseEvent);
|
|
709 }
|
|
710 }
|
|
711 }
|
|
712 });
|
|
713 }
|
|
714
|
|
715 private void showButtonsBar() {
|
|
716 SwingUtilities.invokeLater(new Runnable() {
|
|
717 @Override
|
|
718 public void run() {
|
|
719 setButtonsBarVisibleFS(true);
|
|
720 }
|
|
721 });
|
|
722 }
|
|
723 }
|
|
724
|
|
725 protected void createButtonsPanel(final ProtocolContext context, Container container) {
|
|
726 final SwingViewerWindow.ButtonsBar buttonsBar = createButtonsBar();
|
|
727
|
|
728 buttonsBar.createButton("options", "Set Options", new ActionListener() {
|
|
729 @Override
|
|
730 public void actionPerformed(ActionEvent e) {
|
|
731 showOptionsDialog();
|
|
732 setSurfaceToHandleKbdFocus();
|
|
733 }
|
|
734 });
|
|
735
|
|
736 buttonsBar.createButton("info", "Show connection info", new ActionListener() {
|
|
737 @Override
|
|
738 public void actionPerformed(ActionEvent e) {
|
|
739 showConnectionInfoMessage();
|
|
740 setSurfaceToHandleKbdFocus();
|
|
741 }
|
|
742 });
|
|
743
|
|
744 buttonsBar.createStrut();
|
|
745
|
|
746 buttonsBar.createButton("refresh", "Refresh screen", new ActionListener() {
|
|
747 @Override
|
|
748 public void actionPerformed(ActionEvent e) {
|
|
749 context.sendRefreshMessage();
|
|
750 setSurfaceToHandleKbdFocus();
|
|
751 }
|
|
752 });
|
|
753
|
|
754 addZoomButtons();
|
|
755
|
|
756 kbdButtons = new LinkedList<JComponent>();
|
|
757
|
|
758 buttonsBar.createStrut();
|
|
759
|
|
760 JButton ctrlAltDelButton = buttonsBar.createButton("ctrl-alt-del", "Send 'Ctrl-Alt-Del'", new ActionListener() {
|
|
761 @Override
|
|
762 public void actionPerformed(ActionEvent e) {
|
|
763 sendCtrlAltDel(context);
|
|
764 setSurfaceToHandleKbdFocus();
|
|
765 }
|
|
766 });
|
|
767 kbdButtons.add(ctrlAltDelButton);
|
|
768
|
|
769 JButton winButton = buttonsBar.createButton("win", "Send 'Win' key as 'Ctrl-Esc'", new ActionListener() {
|
|
770 @Override
|
|
771 public void actionPerformed(ActionEvent e) {
|
|
772 sendWinKey(context);
|
|
773 setSurfaceToHandleKbdFocus();
|
|
774 }
|
|
775 });
|
|
776 kbdButtons.add(winButton);
|
|
777
|
|
778 JToggleButton ctrlButton = buttonsBar.createToggleButton("ctrl", "Ctrl Lock",
|
|
779 new ItemListener() {
|
|
780 @Override
|
|
781 public void itemStateChanged(ItemEvent e) {
|
|
782 if (e.getStateChange() == ItemEvent.SELECTED) {
|
|
783 context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true));
|
|
784 } else {
|
|
785 context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false));
|
|
786 }
|
|
787 setSurfaceToHandleKbdFocus();
|
|
788 }
|
|
789 });
|
|
790 kbdButtons.add(ctrlButton);
|
|
791
|
|
792 JToggleButton altButton = buttonsBar.createToggleButton("alt", "Alt Lock",
|
|
793 new ItemListener() {
|
|
794 @Override
|
|
795 public void itemStateChanged(ItemEvent e) {
|
|
796 if (e.getStateChange() == ItemEvent.SELECTED) {
|
|
797 context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, true));
|
|
798 } else {
|
|
799 context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, false));
|
|
800 }
|
|
801 setSurfaceToHandleKbdFocus();
|
|
802 }
|
|
803 });
|
|
804 kbdButtons.add(altButton);
|
16
|
805
|
|
806 JButton screenButton = buttonsBar.createButton("share", "Share my screen", new ActionListener() {
|
|
807 @Override
|
|
808 public void actionPerformed(ActionEvent e) {
|
17
|
809 DataSegment.getLocal().put("Request", "Request", null);
|
16
|
810 }
|
|
811 });
|
|
812 kbdButtons.add(screenButton);
|
0
|
813
|
|
814 ModifierButtonEventListener modifierButtonListener = new ModifierButtonEventListener();
|
|
815 modifierButtonListener.addButton(KeyEvent.VK_CONTROL, ctrlButton);
|
|
816 modifierButtonListener.addButton(KeyEvent.VK_ALT, altButton);
|
|
817 surface.addModifierListener(modifierButtonListener);
|
|
818
|
|
819 // JButton fileTransferButton = new JButton(Utils.getButtonIcon("file-transfer"));
|
|
820 // fileTransferButton.setMargin(buttonsMargin);
|
|
821 // buttonBar.add(fileTransferButton);
|
|
822
|
|
823 buttonsBar.createStrut();
|
|
824
|
|
825 buttonsBar.createButton("close", isApplet ? "Disconnect" : "Close", new ActionListener() {
|
|
826 @Override
|
|
827 public void actionPerformed(ActionEvent e) {
|
|
828 if (frame != null) {
|
|
829 frame.setVisible(false);
|
|
830 frame.dispose();
|
|
831 }
|
|
832 presenter.setNeedReconnection(false);
|
|
833 presenter.cancelConnection();
|
|
834 viewer.closeApp();
|
|
835 }
|
|
836 }).setAlignmentX(JComponent.RIGHT_ALIGNMENT);
|
|
837
|
|
838 setButtonsBarVisible(true, container);
|
|
839 }
|
|
840
|
|
841 private void sendCtrlAltDel(ProtocolContext context) {
|
|
842 context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true));
|
|
843 context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, true));
|
|
844 context.sendMessage(new KeyEventMessage(Keymap.K_DELETE, true));
|
|
845 context.sendMessage(new KeyEventMessage(Keymap.K_DELETE, false));
|
|
846 context.sendMessage(new KeyEventMessage(Keymap.K_ALT_LEFT, false));
|
|
847 context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false));
|
|
848 }
|
|
849
|
|
850 private void sendWinKey(ProtocolContext context) {
|
|
851 context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, true));
|
|
852 context.sendMessage(new KeyEventMessage(Keymap.K_ESCAPE, true));
|
|
853 context.sendMessage(new KeyEventMessage(Keymap.K_ESCAPE, false));
|
|
854 context.sendMessage(new KeyEventMessage(Keymap.K_CTRL_LEFT, false));
|
|
855 }
|
|
856
|
|
857 @Override
|
|
858 public void settingsChanged(SettingsChangedEvent e) {
|
|
859 if (ProtocolSettings.isRfbSettingsChangedFired(e)) {
|
|
860 ProtocolSettings settings = (ProtocolSettings) e.getSource();
|
|
861 setEnabledKbdButtons( ! settings.isViewOnly());
|
|
862 }
|
|
863 }
|
|
864
|
|
865 private void setEnabledKbdButtons(boolean enabled) {
|
|
866 if (kbdButtons != null) {
|
|
867 for (JComponent b : kbdButtons) {
|
|
868 b.setEnabled(enabled);
|
|
869 }
|
|
870 }
|
|
871 }
|
|
872
|
|
873 private void showOptionsDialog() {
|
|
874 OptionsDialog optionsDialog = new OptionsDialog(frame);
|
|
875 optionsDialog.initControlsFromSettings(rfbSettings, uiSettings, false);
|
|
876 optionsDialog.setVisible(true);
|
|
877 presenter.saveHistory();
|
|
878 }
|
|
879
|
|
880 private void showConnectionInfoMessage() {
|
|
881 StringBuilder message = new StringBuilder();
|
|
882 message.append("TightVNC Viewer v.").append(Viewer.ver()).append("\n\n");
|
|
883 message.append("Connected to: ").append(remoteDesktopName).append("\n");
|
|
884 message.append("Host: ").append(connectionString).append("\n\n");
|
|
885
|
|
886 message.append("Desktop geometry: ")
|
|
887 .append(String.valueOf(surface.getWidth()))
|
|
888 .append(" \u00D7 ") // multiplication sign
|
|
889 .append(String.valueOf(surface.getHeight())).append("\n");
|
|
890 message.append("Color format: ")
|
|
891 .append(String.valueOf(Math.round(Math.pow(2, workingProtocol.getPixelFormat().depth))))
|
|
892 .append(" colors (")
|
|
893 .append(String.valueOf(workingProtocol.getPixelFormat().depth))
|
|
894 .append(" bits)\n");
|
|
895 message.append("Current protocol version: ")
|
|
896 .append(workingProtocol.getProtocolVersion());
|
|
897 if (workingProtocol.isTight()) {
|
|
898 message.append("tight");
|
|
899 }
|
|
900 message.append("\n");
|
|
901
|
|
902 JOptionPane infoPane = new JOptionPane(message.toString(), JOptionPane.INFORMATION_MESSAGE);
|
|
903 final JDialog infoDialog = infoPane.createDialog(frame, "VNC connection info");
|
|
904 infoDialog.setModalityType(Dialog.ModalityType.MODELESS);
|
|
905 infoDialog.setVisible(true);
|
|
906 }
|
|
907 } |