/*! ******************************************************************************
*
* Pentaho Data Integration
*
* Copyright (C) 2002-2013 by Pentaho : http://www.pentaho.com
*
*******************************************************************************
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.pentaho.di.ui.core.gui;
import org.eclipse.jface.util.Geometry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
/**
* This class stores information about a screen, window, etc.
*
* @author Matt
* @since 08-04-2004
*
*/
public class WindowProperty {
private String name;
private boolean maximized;
private Rectangle rectangle;
public WindowProperty( String name, boolean maximized, Rectangle rectangle ) {
this.name = name;
this.maximized = maximized;
this.rectangle = rectangle;
}
public WindowProperty( String name, boolean maximized, int x, int y, int width, int height ) {
this.name = name;
this.maximized = maximized;
this.rectangle = new Rectangle( x, y, width, height );
}
public WindowProperty( Shell shell ) {
name = shell.getText();
maximized = shell.getMaximized();
rectangle = shell.getBounds();
}
public void setShell( Shell shell ) {
setShell( shell, false );
}
public void setShell( Shell shell, boolean onlyPosition ) {
setShell( shell, onlyPosition, -1, -1 );
}
public void setShell( Shell shell, int minWidth, int minHeight ) {
setShell( shell, false, minWidth, minHeight );
}
/**
* Performs calculations to size and position a dialog If the size passed in is too large for the primary monitor
* client area, it is shrunk to fit. If the positioning leaves part of the dialog outside the client area, it is
* centered instead Note that currently, many of the defaults in org.pentaho.di.ui.core/default.properties have crazy
* values. This causes the failsafe code in here to fire a lot more than is really necessary.
*
* @param shell
* The dialog to position and size
* @param onlyPosition
* Unused argument. If the window is outside the viewable client are, it must be resized to prevent
* inaccessibility.
* @param minWidth
* @param minHeight
*/
public void setShell( Shell shell, boolean onlyPosition, int minWidth, int minHeight ) {
shell.setMaximized( maximized );
shell.setBounds( rectangle );
if ( minWidth > 0 || minHeight > 0 ) {
Rectangle bounds = shell.getBounds();
if ( bounds.width < minWidth ) {
bounds.width = minWidth;
}
if ( bounds.height < minHeight ) {
bounds.height = minHeight;
}
shell.setSize( bounds.width, bounds.height );
}
// Just to double check: what is the preferred size of this dialog?
// This computed is a minimum. If the minimum is smaller than the
// size of the current shell, we make it larger.
//
Point computedSize = shell.computeSize( SWT.DEFAULT, SWT.DEFAULT );
Rectangle shellSize = shell.getBounds();
if ( shellSize.width < computedSize.x ) {
shellSize.width = computedSize.x;
}
if ( shellSize.height < computedSize.y ) {
shellSize.height = computedSize.y;
}
shell.setBounds( shellSize );
Rectangle entireClientArea = shell.getDisplay().getClientArea();
Rectangle resizedRect = Geometry.copy( shellSize );
constrainRectangleToContainer( resizedRect, entireClientArea );
// If the persisted size/location doesn't perfectly fit
// into the entire client area, the persisted settings
// likely were not meant for this configuration of monitors.
// Relocate the shell into either the parent monitor or if
// there is no parent, the primary monitor then center it.
//
if ( !resizedRect.equals( shellSize ) || isClippedByUnalignedMonitors( resizedRect, shell.getDisplay() ) ) {
Monitor monitor = shell.getDisplay().getPrimaryMonitor();
if ( shell.getParent() != null ) {
monitor = shell.getParent().getMonitor();
}
Rectangle monitorClientArea = monitor.getClientArea();
constrainRectangleToContainer( resizedRect, monitorClientArea );
resizedRect.x = monitorClientArea.x + ( monitorClientArea.width - resizedRect.width ) / 2;
resizedRect.y = monitorClientArea.y + ( monitorClientArea.height - resizedRect.height ) / 2;
shell.setBounds( resizedRect );
}
}
/**
* @param constrainee
* @param container
*/
private void constrainRectangleToContainer( Rectangle constrainee, Rectangle container ) {
Point originalSize = Geometry.getSize( constrainee );
Point containerSize = Geometry.getSize( container );
Point oversize = Geometry.subtract( originalSize, containerSize );
if ( oversize.x > 0 ) {
constrainee.width = originalSize.x - oversize.x;
}
if ( oversize.y > 0 ) {
constrainee.height = originalSize.y - oversize.y;
}
// Detect if the dialog was positioned outside the container
Geometry.moveInside( constrainee, container );
}
/**
* This method is needed in the case where the display has multiple monitors, but they do not form a uniform
* rectangle. In this case, it is possible for Geometry.moveInside() to not detect that the window is partially or
* completely clipped. We check to make sure at least the upper left portion of the rectangle is visible to give the
* user the ability to reposition the dialog in this rare case.
*
* @param constrainee
* @param display
* @return
*/
private boolean isClippedByUnalignedMonitors( Rectangle constrainee, Display display ) {
boolean isClipped;
Monitor[] monitors = display.getMonitors();
if ( monitors.length > 0 ) {
// Loop searches for a monitor proving false
isClipped = true;
for ( Monitor monitor : monitors ) {
if ( monitor.getClientArea().contains( constrainee.x + 10, constrainee.y + 10 ) ) {
isClipped = false;
break;
}
}
} else {
isClipped = false;
}
return isClipped;
}
public String getName() {
return name;
}
public void setName( String name ) {
this.name = name;
}
public boolean isMaximized() {
return maximized;
}
public void setMaximized( boolean maximized ) {
this.maximized = maximized;
}
public Rectangle getRectangle() {
return rectangle;
}
public void setRectangle( Rectangle rectangle ) {
this.rectangle = rectangle;
}
public int getX() {
return rectangle.x;
}
public int getY() {
return rectangle.y;
}
public int getWidth() {
return rectangle.width;
}
public int getHeight() {
return rectangle.height;
}
public int hashCode() {
return name.hashCode();
}
public boolean equal( Object obj ) {
return ( (WindowProperty) obj ).getName().equalsIgnoreCase( name );
}
}