From d49ca935d6380ddb992a8b678cbb6ad130fbd437 Mon Sep 17 00:00:00 2001 From: syeshchenko <stanislav.yeshchenko@gmail.com> Date: Wed, 17 Sep 2014 13:08:14 -0400 Subject: [PATCH] added resize and move cursors to make moving and resizing more accessible for users --- .../client/frame/CaptureRegionFrame.java | 133 ++++-- .../client/frame/WindowlessFrame.java | 412 ++++++++++++------ .../src/main/resources/images/move-cursor.png | Bin 0 -> 17249 bytes .../main/resources/images/resize-cursor.png | Bin 0 -> 102 bytes 4 files changed, 387 insertions(+), 158 deletions(-) create mode 100755 deskshare/applet/src/main/resources/images/move-cursor.png create mode 100755 deskshare/applet/src/main/resources/images/resize-cursor.png diff --git a/deskshare/applet/src/main/java/org/bigbluebutton/deskshare/client/frame/CaptureRegionFrame.java b/deskshare/applet/src/main/java/org/bigbluebutton/deskshare/client/frame/CaptureRegionFrame.java index 398278c13b..53313b6eea 100755 --- a/deskshare/applet/src/main/java/org/bigbluebutton/deskshare/client/frame/CaptureRegionFrame.java +++ b/deskshare/applet/src/main/java/org/bigbluebutton/deskshare/client/frame/CaptureRegionFrame.java @@ -1,29 +1,39 @@ /** -* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ -* -* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). -* -* This program is free software; you can redistribute it and/or modify it under the -* terms of the GNU Lesser General Public License as published by the Free Software -* Foundation; either version 3.0 of the License, or (at your option) any later -* version. -* -* BigBlueButton 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 Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License along -* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. -* -*/ + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 3.0 of the License, or (at your option) any later + * version. + * + * BigBlueButton 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + */ package org.bigbluebutton.deskshare.client.frame; import java.awt.Button; import java.awt.Color; +import java.awt.Dimension; import java.awt.FlowLayout; +import java.awt.Graphics; +import java.awt.GridBagLayout; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.io.IOException; + +import javax.imageio.ImageIO; +import javax.swing.BorderFactory; +import javax.swing.ImageIcon; +import javax.swing.JLabel; import javax.swing.JPanel; public class CaptureRegionFrame { @@ -31,38 +41,89 @@ public class CaptureRegionFrame { private CaptureRegionListener client; private boolean capturing = false; private WindowlessFrame frame; - + private static final int RESIZE_BAR_SIZE = 40; + private static final int MOVE_BAR_SIZE = 60; + public CaptureRegionFrame(CaptureRegionListener client, int borderWidth) { frame = new WindowlessFrame(borderWidth); this.client = client; frame.setCaptureRegionListener(client); - } - + public void setHeight(int h) { frame.setHeight(h); } - + public void setWidth(int w) { frame.setWidth(w); } - + public void setLocation(int x, int y) { frame.setLocation(x, y); } - + public void setVisible(boolean visible) { frame.setVisible(visible); } - + public void start(boolean autoStart) { frame.setToolbar(createToolbar()); + frame.setResizeBar(createResizeBar()); + frame.setMoveBar(createMoveBar()); setVisible(true); if (autoStart) { startCapture(); } } - + + private JPanel createResizeBar(){ + final JPanel resizePanel = new JPanel(); + resizePanel.setPreferredSize(new Dimension(RESIZE_BAR_SIZE,RESIZE_BAR_SIZE)); + resizePanel.setBorder(BorderFactory.createLineBorder(Color.RED)); + resizePanel.setLayout(new GridBagLayout()); + BufferedImage resizeCursorImage = null; + + try { + // Image was taken from http://4.bp.blogspot.com/_fhb-4UuRH50/R1ZLryoIvJI/AAAAAAAAA6U/G3S-XYabULk/s1600/se-resize.gif + resizeCursorImage = ImageIO.read(getClass().getResourceAsStream("/images/resize-cursor.png")); + } catch (IOException e) { + e.printStackTrace(); + } + + JLabel resizePicLabel = new JLabel(new ImageIcon(resizeCursorImage)); + resizePanel.add(resizePicLabel); + return resizePanel; + } + + private JPanel createMoveBar() { + final CirclePanel movePanel = new CirclePanel(); + movePanel.setPreferredSize(new Dimension(MOVE_BAR_SIZE,MOVE_BAR_SIZE)); + movePanel.setLayout(new GridBagLayout()); + movePanel.setOpaque(false); + BufferedImage moveCursorImage = null; + + try { + // Image was taken from http://www.iconarchive.com/show/oxygen-icons-by-oxygen-icons.org/Actions-transform-move-icon.html + moveCursorImage = ImageIO.read(getClass().getResourceAsStream("/images/move-cursor.png")); + } catch (IOException e) { + e.printStackTrace(); + } + + JLabel movePicLabel = new JLabel(new ImageIcon(moveCursorImage)); + movePanel.add(movePicLabel); + return movePanel; + } + + // Wrap move panel in a circle + public class CirclePanel extends JPanel { + static final long serialVersionUID = 1L; + + @Override + protected void paintComponent(Graphics g) { + g.drawOval(0, 0, g.getClipBounds().width, g.getClipBounds().height); + } + } + private JPanel createToolbar() { final JPanel panel = new JPanel(); panel.setBackground(Color.RED); @@ -72,28 +133,28 @@ public class CaptureRegionFrame { btnStartStop.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { -// if (capturing) { -// capturing = false; -// btnStartStop.setLabel("Start Capture"); -// stopCapture(); -// } else { -// capturing = true; -// btnStartStop.setLabel("Stop Capture"); - startCapture(); -// } + // if (capturing) { + // capturing = false; + // btnStartStop.setLabel("Start Capture"); + // stopCapture(); + // } else { + // capturing = true; + // btnStartStop.setLabel("Stop Capture"); + startCapture(); + // } } }); panel.add(btnStartStop); return panel; } - + private void startCapture() { frame.changeBorderToBlue(); frame.removeResizeListeners(); Rectangle rect = frame.getFramedRectangle(); client.onStartCapture(rect.x, rect.y, frame.getWidth(), frame.getHeight()); } - + private void stopCapture() { frame.changeBorderToRed(); client.onStopCapture(); diff --git a/deskshare/applet/src/main/java/org/bigbluebutton/deskshare/client/frame/WindowlessFrame.java b/deskshare/applet/src/main/java/org/bigbluebutton/deskshare/client/frame/WindowlessFrame.java index f16c3f6465..548b5804f0 100755 --- a/deskshare/applet/src/main/java/org/bigbluebutton/deskshare/client/frame/WindowlessFrame.java +++ b/deskshare/applet/src/main/java/org/bigbluebutton/deskshare/client/frame/WindowlessFrame.java @@ -1,21 +1,21 @@ /** -* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ -* -* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). -* -* This program is free software; you can redistribute it and/or modify it under the -* terms of the GNU Lesser General Public License as published by the Free Software -* Foundation; either version 3.0 of the License, or (at your option) any later -* version. -* -* BigBlueButton 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 Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License along -* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. -* -*/ + * BigBlueButton open source conferencing system - http://www.bigbluebutton.org/ + * + * Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below). + * + * This program is free software; you can redistribute it and/or modify it under the + * terms of the GNU Lesser General Public License as published by the Free Software + * Foundation; either version 3.0 of the License, or (at your option) any later + * version. + * + * BigBlueButton 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with BigBlueButton; if not, see <http://www.gnu.org/licenses/>. + * + */ package org.bigbluebutton.deskshare.client.frame; import java.awt.BasicStroke; @@ -49,15 +49,15 @@ class WindowlessFrame implements Serializable { private CaptureRegionListener captureRegionListener; private MouseAdapter resizingAdapter; private MouseAdapter movingAdapter; - + private static interface PropertyChanger { void changeOn(Component component); } - + private static interface LocationAndSizeUpdateable { void updateLocationAndSize(); } - + private static interface OffsetLocator { int getLeftOffset(); int getTopOffset(); @@ -92,7 +92,7 @@ class WindowlessFrame implements Serializable { component.repaint(); } }; - + // properties that change during use private Point mTopLeft = new Point(); @@ -101,11 +101,11 @@ class WindowlessFrame implements Serializable { // properties initialized during construction private BasicStroke mBorderStroke = new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, new float[] { 12, 12 }, 0); private final BasicStroke borderSolidStroke = new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0); - + private GradientPaint mGradient = new GradientPaint(0.0f, 0.0f, Color.red, 1.0f, 1.0f, Color.white, true); private final GradientPaint blueGradient = new GradientPaint(0.0f, 0.0f, Color.blue, 1.0f, 1.0f, Color.blue, true); private final GradientPaint redGradient = new GradientPaint(0.0f, 0.0f, Color.red, 1.0f, 1.0f, Color.white, true); - + private final int mBorderWidth; private final JFrame mWindowFrame; private final BarFrame mTopBorder; @@ -113,15 +113,19 @@ class WindowlessFrame implements Serializable { private final BarFrame mBottomBorder; private final BarFrame mLeftBorder; private ToolbarFrame mToolbarFrame; + private ResizeBarFrame mResizeBarFrame; + private MoveBarFrame mMoveBarFrame; + private static final int MIN_WINDOW_SIZE = 200; + private MultiScreen mScreen = new MultiScreen(); - + /***************************************************************************** ; Class MultiScreen ;---------------------------------------------------------------------------- ; DESCRIPTION ; This class is used to detect if the system has more than one screen. - ******************************************************************************/ + ******************************************************************************/ private class MultiScreen { private int minX=0 ; //minimum of x position private int totalWidth=0 ; // total screen resolution @@ -129,7 +133,7 @@ class WindowlessFrame implements Serializable { private GraphicsEnvironment ge ; private GraphicsDevice[] screenDevice ; private boolean ismultiscreen=false ; - + /***************************************************************************** ; MultiScreen ;---------------------------------------------------------------------------- @@ -150,41 +154,41 @@ class WindowlessFrame implements Serializable { ; __date__ : PTS: ; 2010.11.16 problem 644 and 647 ; - ******************************************************************************/ + ******************************************************************************/ private MultiScreen(){ int i ; - + ge = GraphicsEnvironment.getLocalGraphicsEnvironment() ; screenDevice = ge.getScreenDevices() ; - + if ( 1 < screenDevice.length ){ // this is the case for multiple devices. // set the flag to indicate multiple devices on the system. ismultiscreen=true ; for ( i=0; i<screenDevice.length; i++){ GraphicsConfiguration[] gc = screenDevice[i].getConfigurations() ; - + // determine the minimum x position for the main screen if ( gc[0].getBounds().x <= minX ){ minX = gc[0].getBounds().x; } - + // determine the total screen size if ( gc[0].getBounds().x >= 0){ totalWidth = totalWidth + gc[0].getBounds().width; } - + } }else{ // this is the case for one screen only. ismultiscreen = false ; } - + // set the main screen width curWidth = screenDevice[0].getConfigurations()[0].getBounds().width ; - + } // END FUNCTION MultiScreen - + /***************************************************************************** ; isMultiScreen ;---------------------------------------------------------------------------- @@ -204,19 +208,19 @@ class WindowlessFrame implements Serializable { ; __date__ : PTS: ; 2010.11.16 problem 644 and 647 ; - ******************************************************************************/ + ******************************************************************************/ public boolean isMultiScreen(){ - + return ismultiscreen ; } // END FUNCTION isMultiScreen - + } // END CLASS MultiScreen - + private class ToolbarFrame extends Window implements LocationAndSizeUpdateable { private static final long serialVersionUID = 1L; private final OffsetLocator mOffsetLocator; - + public ToolbarFrame(JFrame frame, OffsetLocator ol, JPanel content) { super(frame); super.setAlwaysOnTop(true); @@ -230,18 +234,111 @@ class WindowlessFrame implements Serializable { public void updateLocationAndSize() { setLocation(getLocation()); } - + @Override public Point getLocation() { return new Point(mTopLeft.x + mOffsetLocator.getLeftOffset(), mTopLeft.y + mOffsetLocator.getTopOffset()); } } - - private class WindowlessFrameMovingMouseListener extends MouseAdapter { - + + private class MoveBarFrame extends Window implements LocationAndSizeUpdateable { + private static final long serialVersionUID = 1L; + + private final OffsetLocator mOffsetLocator; + private MouseAdapter moveMouseAdapter = null; + + public MoveBarFrame(JFrame frame, OffsetLocator ol, JPanel content) { + super(frame); + super.setAlwaysOnTop(true); + frame.setAlwaysOnTop(true); + setBackground(new Color(0, 255, 0, 0)); + mOffsetLocator = ol; + add(content); + pack(); + + moveMouseAdapter = createMoveBarMovingMouseListener(); + + changeMovingBarFrame(new PropertyChanger() { + @Override + public void changeOn(Component component) { + component.addMouseListener(moveMouseAdapter); + component.addMouseMotionListener(moveMouseAdapter); + } + }); + } + + private void changeMovingBarFrame(PropertyChanger pc) { + pc.changeOn(this); + } + + private MouseAdapter createMoveBarMovingMouseListener() { + return new FrameMovingMouseListener(false); + } + + @Override + public void updateLocationAndSize() { + setLocation(getLocation()); + } + + @Override + public Point getLocation() { + return new Point(mTopLeft.x + mOffsetLocator.getLeftOffset(), mTopLeft.y + mOffsetLocator.getTopOffset()); + } + } + + private class ResizeBarFrame extends Window implements LocationAndSizeUpdateable { + + private static final long serialVersionUID = 1L; + private final OffsetLocator mOffsetLocator; + private MouseAdapter resizeMouseAdapter = null; + + public ResizeBarFrame(JFrame frame, OffsetLocator ol, JPanel content) { + super(frame); + super.setAlwaysOnTop(true); + frame.setAlwaysOnTop(true); + setBackground(new Color(0, 255, 0, 0)); + mOffsetLocator = ol; + add(content); + pack(); + + resizeMouseAdapter = createResizeBarResizingMouseListener(); + + changeResizeBarFrame(new PropertyChanger() { + @Override + public void changeOn(Component component) { + component.addMouseListener(resizeMouseAdapter); + component.addMouseMotionListener(resizeMouseAdapter); + } + }); + } + + private MouseAdapter createResizeBarResizingMouseListener() { + return new FrameResizingMouseListener(false); + } + + public void updateLocationAndSize() { + setLocation(getLocation()); + } + + @Override + public Point getLocation() { + return new Point(mTopLeft.x + mOffsetLocator.getLeftOffset(), mTopLeft.y + mOffsetLocator.getTopOffset()); + } + + private void changeResizeBarFrame(PropertyChanger pc) { + pc.changeOn(this); + } + } + + private class FrameMovingMouseListener extends MouseAdapter { + private AtomicBoolean mMoving = new AtomicBoolean(false); - private Point mActionOffset = null; + private Boolean isBorder = false; + + public FrameMovingMouseListener(Boolean isBorder) { + this.isBorder = isBorder; + } @Override public void mouseDragged(MouseEvent e) { @@ -249,7 +346,7 @@ class WindowlessFrame implements Serializable { int changeInY = e.getLocationOnScreen().y - mActionOffset.y - mTopLeft.y; Toolkit tk = Toolkit.getDefaultToolkit(); Dimension d = tk.getScreenSize(); - + // check if multiscreen if ( false == mScreen.isMultiScreen() ){ // case one screen only @@ -264,7 +361,7 @@ class WindowlessFrame implements Serializable { if (mTopLeft.x + mOverallSize.width > (d.width-6) && changeInX > 0) { mTopLeft.x = d.width - mOverallSize.width-5; changeInX = 0; - + } if (mTopLeft.y + mOverallSize.height > (d.height-6) && changeInY > 0) { mTopLeft.y = d.height - mOverallSize.height-5; @@ -280,7 +377,7 @@ class WindowlessFrame implements Serializable { mTopLeft.y = 0; changeInY = 0; } - + if (mTopLeft.x + mOverallSize.width > (mScreen.totalWidth-6) && changeInX > 0) { mTopLeft.x = mScreen.totalWidth - mOverallSize.width-5; changeInX = 0; @@ -294,7 +391,7 @@ class WindowlessFrame implements Serializable { WindowlessFrame.this.setLocation(changeInX + mTopLeft.x, changeInY + mTopLeft.y); } } - + @Override public void mousePressed(MouseEvent e) { final Point mouse = e.getLocationOnScreen(); @@ -302,39 +399,51 @@ class WindowlessFrame implements Serializable { mMoving.set(true); e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); } - + @Override public void mouseReleased(MouseEvent e) { mMoving.set(false); mActionOffset = null; e.getComponent().setCursor(Cursor.getDefaultCursor()); } - - } - - private class WindowlessFrameResizingMouseListener extends MouseAdapter { + @Override + public void mouseMoved(MouseEvent e) { + if (!isBorder) { + e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + } + } + } + + private class FrameResizingMouseListener extends MouseAdapter { + private static final int CORNER_SIZE = 150; private AtomicBoolean mResizing = new AtomicBoolean(false); - + private Point mActionOffset = null; private Dimension mOriginalSize = null; private Corner mCorner; + private Boolean isBorder = false; + + public FrameResizingMouseListener(Boolean isBorder) { + this.isBorder = isBorder; + } @Override public void mouseDragged(MouseEvent e) { - int changeInX = e.getLocationOnScreen().x - mActionOffset.x - mTopLeft.x; + int changeInX = e.getLocationOnScreen().x - mActionOffset.x - mTopLeft.x; final int changeInY = e.getLocationOnScreen().y - mActionOffset.y - mTopLeft.y; - + Toolkit tk = Toolkit.getDefaultToolkit(); Dimension d = tk.getScreenSize(); if (mResizing.get()) { int newH = mOriginalSize.height; int newW = mOriginalSize.width; - if (Corner.SOUTHEAST == mCorner) { - + if (isBorder && Corner.SOUTHEAST != mCorner) { + // doing nothing + } else { if (e.getLocationOnScreen().x < mTopLeft.x+5) { newW = 5; } else { @@ -345,7 +454,8 @@ class WindowlessFrame implements Serializable { } else { newH += changeInY; } - } /*else if (mCorner == Corner.NORTHEAST) { + } + /*else if (mCorner == Corner.NORTHEAST) { mTopLeft.y = mTopLeft.y + changeInY; newH = mOverallSize.height + -changeInY; newW += changeInX; @@ -381,26 +491,40 @@ class WindowlessFrame implements Serializable { if (newW + mTopLeft.x > mWidth-5 && mTopLeft.x >= 0){ newW = mWidth - mTopLeft.x-5; }else if (mTopLeft.x<0 && mTopLeft.x + newW > -5){ - newW = - mTopLeft.x-5; + newW = - mTopLeft.x-5; } } - + + // set minimum window size + if (newH < MIN_WINDOW_SIZE) { + newH = MIN_WINDOW_SIZE; + } + if (newW < MIN_WINDOW_SIZE) { + newW = MIN_WINDOW_SIZE; + } + WindowlessFrame.this.setSize(newH, newW); e.consume(); } } - + @Override public void mousePressed(MouseEvent e) { final Point mouse = e.getLocationOnScreen(); mActionOffset = new Point(mouse.x - mTopLeft.x, mouse.y - mTopLeft.y); mOriginalSize = new Dimension(mOverallSize); - mCorner = nearCorner(mouse); - if (mCorner != null) { + + if (isBorder) { + mCorner = nearCorner(mouse); + if (mCorner != null ) { + mResizing.set(true); + } + } + else { mResizing.set(true); } } - + @Override public void mouseReleased(MouseEvent e) { mResizing.set(false); @@ -419,14 +543,14 @@ class WindowlessFrame implements Serializable { } else if (isNearBottomLeftCorner(mouse)) { return Corner.SOUTHWEST; } - */ + */ return null; } private boolean isNearBottomRightCorner(Point mouse) { int xToBotLeft = Math.abs(mTopLeft.x + (int) mOverallSize.getWidth() - mouse.x); - int yToBotLeft = Math.abs(mTopLeft.y + (int) mOverallSize.getHeight() - mouse.y); - return xToBotLeft < CORNER_SIZE && yToBotLeft < CORNER_SIZE; + int yToBotLeft = Math.abs(mTopLeft.y + (int) mOverallSize.getHeight() - mouse.y);; + return (xToBotLeft < CORNER_SIZE && yToBotLeft < CORNER_SIZE); } /* private boolean isNearTopRightCorner(Point mouse) { @@ -446,12 +570,12 @@ class WindowlessFrame implements Serializable { int yToTopLeft = Math.abs(mTopLeft.y - mouse.y); return xToTopLeft < CORNER_SIZE && yToTopLeft < CORNER_SIZE; } - */ + */ @Override public void mouseMoved(MouseEvent e) { final Point mouse = e.getLocationOnScreen(); - + /* if (isNearTopLeftCorner(mouse)) { e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR)); @@ -460,20 +584,24 @@ class WindowlessFrame implements Serializable { } else if (isNearTopRightCorner(mouse)) { e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR)); } else - */ - if (isNearBottomRightCorner(mouse)) { - e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR)); + */ + if (isBorder) { + if (isNearBottomRightCorner(mouse)) { + e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR)); + } else { + e.getComponent().setCursor(Cursor.getDefaultCursor()); + } } else { - e.getComponent().setCursor(Cursor.getDefaultCursor()); + e.getComponent().setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR)); } } } - + private class BarFrame extends Window implements LocationAndSizeUpdateable { private static final long serialVersionUID = 1L; private final OffsetLocator mOffsetLocator; - + public BarFrame(Frame frame, OffsetLocator offsetLocator) { super(frame); mOffsetLocator = offsetLocator; @@ -500,85 +628,85 @@ class WindowlessFrame implements Serializable { setSize(getWidth(), getHeight()); setLocation(getLocation()); } - + @Override public Point getLocation() { return new Point(mTopLeft.x + mOffsetLocator.getLeftOffset(), mTopLeft.y + mOffsetLocator.getTopOffset()); } - + @Override public int getHeight() { return mBorderWidth; } - + @Override public int getWidth() { return mOverallSize.width; } } - + private class HorizontalBarFrame extends BarFrame { private static final long serialVersionUID = 1L; - + public HorizontalBarFrame(JFrame frame, OffsetLocator offsetLocator) { super(frame, offsetLocator); super.setAlwaysOnTop(true); } } - + private class VerticalBarFrame extends BarFrame { private static final long serialVersionUID = 1L; - + public VerticalBarFrame(JFrame frame, OffsetLocator offsetLocator) { super(frame, offsetLocator); super.setAlwaysOnTop(true); } - + @Override public int getWidth() { return mBorderWidth; } - + @Override public int getHeight() { return mOverallSize.height; } } - + public WindowlessFrame(int borderWidth) { mBorderWidth = borderWidth; mWindowFrame = new JFrame("Windowless Frame"); //mWindowFrame.setAlwaysOnTop(true); - + mTopBorder = new HorizontalBarFrame(mWindowFrame, new StaticOffsetLocator(0, 0)); mBottomBorder = new HorizontalBarFrame(mWindowFrame, new OffsetLocator() { - + @Override public int getTopOffset() { return mOverallSize.height; } - + @Override public int getLeftOffset() { return 0; } }); - + mRightBorder = new VerticalBarFrame(mWindowFrame, new OffsetLocator() { - + @Override public int getTopOffset() { return 0; } - + @Override public int getLeftOffset() { return mOverallSize.width; } }); mLeftBorder = new VerticalBarFrame(mWindowFrame, new StaticOffsetLocator(0, 0)); - + movingAdapter = createMovingMouseListener(); resizingAdapter = createResizingMouseListener(); changeBarFrames(new PropertyChanger() { @@ -591,13 +719,13 @@ class WindowlessFrame implements Serializable { } }, false); } - + public final MouseAdapter createMovingMouseListener() { - return new WindowlessFrameMovingMouseListener(); + return new FrameMovingMouseListener(true); } public final MouseAdapter createResizingMouseListener() { - return new WindowlessFrameResizingMouseListener(); + return new FrameResizingMouseListener(true); } public void setToolbar(final JPanel toolbar) { @@ -606,7 +734,7 @@ class WindowlessFrame implements Serializable { public int getTopOffset() { return (mOverallSize.height + mBorderWidth - toolbar.getHeight()) / 2; } - + @Override public int getLeftOffset() { return (mOverallSize.width + mBorderWidth - toolbar.getWidth()) / 2; @@ -615,6 +743,38 @@ class WindowlessFrame implements Serializable { mToolbarFrame = new ToolbarFrame(mWindowFrame, toolbarOffsetLocator, toolbar); } + public void setResizeBar(final JPanel resizeBar) { + + final OffsetLocator resizeBarOffsetLocator = new OffsetLocator() { + @Override + public int getTopOffset() { + return (mOverallSize.height + mBorderWidth - resizeBar.getHeight()); + } + + @Override + public int getLeftOffset() { + return (mOverallSize.width + mBorderWidth - resizeBar.getWidth()); + } + }; + + mResizeBarFrame = new ResizeBarFrame(mWindowFrame, resizeBarOffsetLocator, resizeBar); + } + + public void setMoveBar(final JPanel moveBar) { + final OffsetLocator moveBarOffsetLocator = new OffsetLocator() { + @Override + public int getTopOffset() { + return (mOverallSize.height + mBorderWidth - (moveBar.getHeight()/2) - moveBar.getHeight())/2 - 50; + } + + @Override + public int getLeftOffset() { + return (mOverallSize.width + mBorderWidth - (moveBar.getWidth()))/2; + } + }; + mMoveBarFrame = new MoveBarFrame(mWindowFrame, moveBarOffsetLocator, moveBar); + } + public final void setSize(int height, int width) { setHeight(height); setWidth(width); @@ -624,50 +784,50 @@ class WindowlessFrame implements Serializable { public final void setWidth(int width) { mOverallSize.width = width; } - + public final void setHeight(int height) { mOverallSize.height = height; } - + public final void setLocation(int x, int y) { mTopLeft.x = x; mTopLeft.y = y; repaint(); - + if (captureRegionListener != null) { Rectangle rect = getFramedRectangle(); captureRegionListener.onCaptureRegionMoved(rect.x, rect.y); } } - + public final int getX(){ return mTopLeft.x; } - + public final int getY(){ return mTopLeft.y; } - + public final int getWidth(){ return mOverallSize.width - mBorderWidth; } - + public final int getHeight(){ return mOverallSize.height - mBorderWidth; } - + public final void centerOnScreen() { - Toolkit kit = mLeftBorder.getToolkit(); - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - GraphicsDevice[] gs = ge.getScreenDevices(); - Insets in = kit.getScreenInsets(gs[0].getDefaultConfiguration()); - - Dimension d = kit.getScreenSize(); - int maxWidth = (d.width - in.left - in.right); - int maxHeight = (d.height - in.top - in.bottom); - setLocation((int) (maxWidth - mOverallSize.width) / 2, (int) (maxHeight - mOverallSize.height) / 2); + Toolkit kit = mLeftBorder.getToolkit(); + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice[] gs = ge.getScreenDevices(); + Insets in = kit.getScreenInsets(gs[0].getDefaultConfiguration()); + + Dimension d = kit.getScreenSize(); + int maxWidth = (d.width - in.left - in.right); + int maxHeight = (d.height - in.top - in.bottom); + setLocation((int) (maxWidth - mOverallSize.width) / 2, (int) (maxHeight - mOverallSize.height) / 2); } - + public final Rectangle getFramedRectangle() { return new Rectangle(mTopLeft.x + mBorderWidth, mTopLeft.y + mBorderWidth, mOverallSize.width - mBorderWidth, mOverallSize.height - mBorderWidth); } @@ -680,7 +840,7 @@ class WindowlessFrame implements Serializable { } }, true); } - + private void changeBarFrames(PropertyChanger pc, boolean repaint) { pc.changeOn(mTopBorder); pc.changeOn(mRightBorder); @@ -693,6 +853,8 @@ class WindowlessFrame implements Serializable { private void changeAll(PropertyChanger pc, boolean repaint) { if (mToolbarFrame != null) pc.changeOn(mToolbarFrame); + if (mResizeBarFrame != null) pc.changeOn(mResizeBarFrame); + if (mMoveBarFrame != null) pc.changeOn(mMoveBarFrame); changeBarFrames(pc, repaint); } @@ -705,12 +867,12 @@ class WindowlessFrame implements Serializable { mGradient = blueGradient; repaint(); } - + public void changeBorderToRed() { mGradient = redGradient; repaint(); } - + public static void main(String[] args) { final WindowlessFrame wf = new WindowlessFrame(5); wf.setHeight(300); @@ -718,11 +880,11 @@ class WindowlessFrame implements Serializable { wf.setLocation(100, 200); wf.setVisible(true); } - + public void setCaptureRegionListener(CaptureRegionListener listener){ this.captureRegionListener = listener; } - + public void removeResizeListeners() { mRightBorder.removeMouseListener(resizingAdapter); mRightBorder.removeMouseMotionListener(resizingAdapter); @@ -732,10 +894,16 @@ class WindowlessFrame implements Serializable { mTopBorder.removeMouseMotionListener(resizingAdapter); mBottomBorder.removeMouseListener(resizingAdapter); mBottomBorder.removeMouseMotionListener(resizingAdapter); + mResizeBarFrame.removeMouseListener(resizingAdapter); + mResizeBarFrame.removeMouseMotionListener(resizingAdapter); + mMoveBarFrame.removeMouseListener(movingAdapter); + mMoveBarFrame.removeMouseMotionListener(movingAdapter); repaint(); - + System.out.println("Removing listeners......................"); mToolbarFrame.setVisible(false); + mResizeBarFrame.setVisible(false); + mMoveBarFrame.setVisible(false); } public void addResizeListeners() { diff --git a/deskshare/applet/src/main/resources/images/move-cursor.png b/deskshare/applet/src/main/resources/images/move-cursor.png new file mode 100755 index 0000000000000000000000000000000000000000..4e64fc6e4ae7f6653495dd9bf22f5d09435343d0 GIT binary patch literal 17249 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 literal 0 HcmV?d00001 diff --git a/deskshare/applet/src/main/resources/images/resize-cursor.png b/deskshare/applet/src/main/resources/images/resize-cursor.png new file mode 100755 index 0000000000000000000000000000000000000000..50d5f933c6eaf6a6470a16935d5ece83e74182c2 GIT binary patch literal 102 zcmZ?wbhEHb)L_tHXkcLY|NlP&1B2pE7Djd;tpmhBaSa9rCfk<&m8akGFP^jIR`=$6 zdw%n`J@S|q*sw@#0-N^geXDj~deePlhu-7%v(h`0Op{iI`uv=BX4z-XUa#d>UM*%| Gum%7fUn*k& literal 0 HcmV?d00001 -- GitLab