package org.jabref.gui.util; import java.awt.Dimension; import java.awt.Frame; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Point; import java.awt.Rectangle; import java.awt.Window; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import javax.swing.JFrame; import org.jabref.Globals; /** * Restores and saves the position of non-modal windows inside the JabRef preferences. * * Includes multi-monitor support. * If a windows is placed on another monitor than the main one, it tries to restore that position afterwards. * If the stored position in a multi-monitor setup is not available anymore, it places the window on an equivalent position on the main monitor. */ public class WindowLocation { private final String posXKey; private final String posYKey; private final String sizeXKey; private final String sizeYKey; private final Window window; public WindowLocation(Window window, String posXKey, String posYKey, String sizeXKey, String sizeYKey) { this.window = window; this.posXKey = posXKey; this.posYKey = posYKey; this.sizeXKey = sizeXKey; this.sizeYKey = sizeYKey; // set up a ComponentListener that saves the last size and position of the dialog window.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { storeCurrentWindowLocation(); } @Override public void componentMoved(ComponentEvent e) { storeCurrentWindowLocation(); } }); } public void displayWindowAtStoredLocation() { WindowPosition storedPosition = getStoredLocation(); // preference values are wrong/not in multi-monitor setup anymore if (!isDisplayable(storedPosition)) { // adapt position to be inside available boundaries storedPosition = adaptPosition(storedPosition); } setWindowLocation(storedPosition); } public void storeCurrentWindowLocation() { // maximizing is handled explicitely if (window instanceof Frame) { Frame frame = (Frame) window; if (frame.getExtendedState() == Frame.MAXIMIZED_BOTH) { return; } } Point location = window.getLocation(); Dimension dimensions = window.getSize(); Globals.prefs.putInt(posXKey, location.x); Globals.prefs.putInt(posYKey, location.y); Globals.prefs.putInt(sizeXKey, dimensions.width); Globals.prefs.putInt(sizeYKey, dimensions.height); } private WindowPosition getStoredLocation() { int sizeX = Globals.prefs.getInt(sizeXKey); int sizeY = Globals.prefs.getInt(sizeYKey); int posX = Globals.prefs.getInt(posXKey); int posY = Globals.prefs.getInt(posYKey); return new WindowPosition(posX, posY, sizeX, sizeY); } private void setWindowLocation(WindowPosition storedPosition) { window.setLocation(storedPosition.posX, storedPosition.posY); window.setSize(storedPosition.sizeX, storedPosition.sizeY); } private boolean isDisplayable(WindowPosition position) { JFrame frame = new JFrame(); frame.setBounds(position.posX, position.posY, position.sizeX, position.sizeY); return getVirtualBounds().contains(frame.getBounds()); } private Rectangle getVirtualBounds() { Rectangle bounds = new Rectangle(0, 0, 0, 0); GraphicsDevice[] devices = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices(); for (GraphicsDevice device : devices) { bounds.add(device.getDefaultConfiguration().getBounds()); } return bounds; } private WindowPosition adaptPosition(WindowPosition position) { if (isDisplayable(position)) { return position; } // current algorithm: // 1. try to move to main screen // 2. use default sizes on main monitor GraphicsDevice mainScreen = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); int mainScreenHeight = mainScreen.getDisplayMode().getHeight(); int mainScreenWidth = mainScreen.getDisplayMode().getWidth(); int newPosX = position.posX; int newPosY = position.posY; int newSizeX = position.sizeX; int newSizeY = position.sizeY; if ((position.posX + position.sizeX) > mainScreenWidth) { if (position.sizeX <= mainScreenWidth) { newPosX = mainScreenWidth - position.sizeX; } else { newPosX = Globals.prefs.getIntDefault(posXKey); newSizeX = Globals.prefs.getIntDefault(sizeXKey); } } if ((position.posY + position.sizeY) > mainScreenHeight) { if (position.sizeY <= mainScreenHeight) { newPosY = mainScreenHeight - position.sizeY; } else { newPosY = Globals.prefs.getIntDefault(posYKey); newSizeY = Globals.prefs.getIntDefault(sizeYKey); } } return new WindowPosition(newPosX, newPosY, newSizeX, newSizeY); } static class WindowPosition { public final int posX; public final int posY; public final int sizeX; public final int sizeY; public WindowPosition(int posX, int posY, int sizeX, int sizeY) { this.posX = posX; this.posY = posY; this.sizeX = sizeX; this.sizeY = sizeY; } } }