blob: 7b8425065ae89f74670add772ca9ff2c8ba2182c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html Contributors:
* IBM Corporation - initial API and implementation
******************************************************************************/
package org.eclipse.swt.custom;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.*;
/**
* The SashForm lays out its children in a Row or Column arrangement (as
* specified by the orientation) and places a Sash between the children. One
* child may be maximized to occupy the entire size of the SashForm. The
* relative sizes of the children may be specfied using weights.
* <p>
* <dl>
* <dt><b>Styles:</b>
* <dd>HORIZONTAL, VERTICAL, SMOOTH
* </dl>
*/
public class SashForm extends Composite {
public int SASH_WIDTH = 3;
int sashStyle;
Sash[] sashes = new Sash[ 0 ];
// Remember background and foreground
// colors to determine whether to set
// sashes to the default color (null) or
// a specific color
Color background = null;
Color foreground = null;
Control[] controls = new Control[ 0 ];
Control maxControl = null;
SelectionListener sashListener;
static final int DRAG_MINIMUM = 20;
/**
* Constructs a new instance of this class given its parent and a style value
* describing its behavior and appearance.
* <p>
* The style value is either one of the style constants defined in class
* <code>SWT</code> which is applicable to instances of this class, or must
* be built by <em>bitwise OR</em>'ing together (that is, using the
* <code>int</code> "|" operator) two or more of those <code>SWT</code>
* style constants. The class description lists the style constants that are
* applicable to the class. Style bits are also inherited from superclasses.
* </p>
*
* @param parent a widget which will be the parent of the new instance (cannot
* be null)
* @param style the style of widget to construct
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
* </ul>
* @exception SWTException
* <ul>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the parent</li>
* </ul>
* @see SWT#HORIZONTAL
* @see SWT#VERTICAL
* @see #getStyle()
*/
public SashForm( final Composite parent, final int style ) {
super( parent, checkStyle( style ) );
super.setLayout( new SashFormLayout() );
sashStyle = ( ( style & SWT.VERTICAL ) != 0 )
? SWT.HORIZONTAL
: SWT.VERTICAL;
if( ( style & SWT.BORDER ) != 0 ) {
sashStyle |= SWT.BORDER;
}
// if ((style & SWT.SMOOTH) != 0) sashStyle |= SWT.SMOOTH;
sashListener = new SelectionAdapter() {
public void widgetSelected( final SelectionEvent event ) {
onDragSash( event );
}
};
}
static int checkStyle( int style ) {
int mask = SWT.BORDER /* | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT */;
return style & mask;
}
/**
* Returns SWT.HORIZONTAL if the controls in the SashForm are laid out side by
* side or SWT.VERTICAL if the controls in the SashForm are laid out top to
* bottom.
*
* @return SWT.HORIZONTAL or SWT.VERTICAL
*/
public int getOrientation() {
checkWidget();
return ( sashStyle & SWT.VERTICAL ) != 0 ? SWT.HORIZONTAL : SWT.VERTICAL;
}
public int getStyle() {
int style = super.getStyle();
style |= getOrientation() == SWT.VERTICAL ? SWT.VERTICAL : SWT.HORIZONTAL;
// if( ( sashStyle & SWT.SMOOTH ) != 0 )
// style |= SWT.SMOOTH;
return style;
}
/**
* Answer the control that currently is maximized in the SashForm. This value
* may be null.
*
* @return the control that currently is maximized or null
*/
public Control getMaximizedControl() {
checkWidget();
return this.maxControl;
}
/**
* Answer the relative weight of each child in the SashForm. The weight
* represents the percent of the total width (if SashForm has Horizontal
* orientation) or total height (if SashForm has Vertical orientation) each
* control occupies. The weights are returned in order of the creation of the
* widgets (weight[0] corresponds to the weight of the first child created).
*
* @return the relative weight of each child
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*/
public int[] getWeights() {
checkWidget();
Control[] cArray = getControls( false );
int[] ratios = new int[ cArray.length ];
for( int i = 0; i < cArray.length; i++ ) {
Object data = cArray[ i ].getLayoutData();
if( data != null && data instanceof SashFormData ) {
ratios[ i ] = ( int )( ( ( SashFormData )data ).weight * 1000 >> 16 );
} else {
ratios[ i ] = 200;
}
}
return ratios;
}
Control[] getControls( boolean onlyVisible ) {
Control[] children = getChildren();
Control[] result = new Control[ 0 ];
for( int i = 0; i < children.length; i++ ) {
if( children[ i ] instanceof Sash )
continue;
// if (onlyVisible && !children[i].getVisible()) continue;
Control[] newResult = new Control[ result.length + 1 ];
System.arraycopy( result, 0, newResult, 0, result.length );
newResult[ result.length ] = children[ i ];
result = newResult;
}
return result;
}
void onDragSash( final SelectionEvent event ) {
Sash sash = ( Sash )event.getSource();
int sashIndex = -1;
for( int i = 0; i < sashes.length; i++ ) {
if( sashes[ i ] == sash ) {
sashIndex = i;
break;
}
}
if( sashIndex == -1 )
return;
Control c1 = controls[ sashIndex ];
Control c2 = controls[ sashIndex + 1 ];
Rectangle b1 = c1.getBounds();
Rectangle b2 = c2.getBounds();
Rectangle sashBounds = sash.getBounds();
Rectangle area = getClientArea();
boolean correction = false;
if( getOrientation() == SWT.HORIZONTAL ) {
correction = b1.width < DRAG_MINIMUM || b2.width < DRAG_MINIMUM;
int totalWidth = b2.x + b2.width - b1.x;
int shift = event.x - sashBounds.x;
b1.width += shift;
b2.x += shift;
b2.width -= shift;
if( b1.width < DRAG_MINIMUM ) {
b1.width = DRAG_MINIMUM;
b2.x = b1.x + b1.width + sashBounds.width;
b2.width = totalWidth - b2.x;
event.x = b1.x + b1.width;
event.doit = false;
}
if( b2.width < DRAG_MINIMUM ) {
b1.width = totalWidth - DRAG_MINIMUM - sashBounds.width;
b2.x = b1.x + b1.width + sashBounds.width;
b2.width = DRAG_MINIMUM;
event.x = b1.x + b1.width;
event.doit = false;
}
Object data1 = c1.getLayoutData();
if( data1 == null || !( data1 instanceof SashFormData ) ) {
data1 = new SashFormData();
c1.setLayoutData( data1 );
}
Object data2 = c2.getLayoutData();
if( data2 == null || !( data2 instanceof SashFormData ) ) {
data2 = new SashFormData();
c2.setLayoutData( data2 );
}
( ( SashFormData )data1 ).weight = ( ( ( long )b1.width << 16 )
+ area.width - 1 )
/ area.width;
( ( SashFormData )data2 ).weight = ( ( ( long )b2.width << 16 )
+ area.width - 1 )
/ area.width;
} else {
correction = b1.height < DRAG_MINIMUM || b2.height < DRAG_MINIMUM;
int totalHeight = b2.y + b2.height - b1.y;
int shift = event.y - sashBounds.y;
b1.height += shift;
b2.y += shift;
b2.height -= shift;
if( b1.height < DRAG_MINIMUM ) {
b1.height = DRAG_MINIMUM;
b2.y = b1.y + b1.height + sashBounds.height;
b2.height = totalHeight - b2.y;
event.y = b1.y + b1.height;
event.doit = false;
}
if( b2.height < DRAG_MINIMUM ) {
b1.height = totalHeight - DRAG_MINIMUM - sashBounds.height;
b2.y = b1.y + b1.height + sashBounds.height;
b2.height = DRAG_MINIMUM;
event.y = b1.y + b1.height;
event.doit = false;
}
Object data1 = c1.getLayoutData();
if( data1 == null || !( data1 instanceof SashFormData ) ) {
data1 = new SashFormData();
c1.setLayoutData( data1 );
}
Object data2 = c2.getLayoutData();
if( data2 == null || !( data2 instanceof SashFormData ) ) {
data2 = new SashFormData();
c2.setLayoutData( data2 );
}
( ( SashFormData )data1 ).weight = ( ( ( long )b1.height << 16 )
+ area.height - 1 )
/ area.height;
( ( SashFormData )data2 ).weight = ( ( ( long )b2.height << 16 )
+ area.height - 1 )
/ area.height;
}
// if (correction || (event.doit && event.detail != SWT.DRAG)) {
if( correction || event.doit ) {
c1.setBounds( b1 );
sash.setBounds( event.x, event.y, event.width, event.height );
c2.setBounds( b2 );
}
}
/**
* If orientation is SWT.HORIZONTAL, lay the controls in the SashForm out side
* by side. If orientation is SWT.VERTICAL, lay the controls in the SashForm
* out top to bottom.
*
* @param orientation SWT.HORIZONTAL or SWT.VERTICAL
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* <li>ERROR_INVALID_ARGUMENT - if the value of orientation is
* not SWT.HORIZONTAL or SWT.VERTICAL
* </ul>
*/
public void setOrientation( int orientation ) {
checkWidget();
if( getOrientation() == orientation )
return;
if( orientation != SWT.HORIZONTAL && orientation != SWT.VERTICAL ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
sashStyle &= ~( SWT.HORIZONTAL | SWT.VERTICAL );
sashStyle |= orientation == SWT.VERTICAL
? SWT.HORIZONTAL
: SWT.VERTICAL;
for( int i = 0; i < sashes.length; i++ ) {
// sashes[i].dispose();
// sashes[i] = new Sash(this, sashStyle);
// sashes[i].setBackground(background);
// sashes[i].setForeground(foreground);
// sashes[i].addListener(SWT.Selection, sashListener);
sashes[ i ] = new Sash( this, sashStyle );
sashes[ i ].addSelectionListener( sashListener );
}
// layout(false);
layout();
}
// public void setBackground (Color color) {
// super.setBackground(color);
// background = color;
// for (int i = 0; i < sashes.length; i++) {
// sashes[i].setBackground(background);
// }
// }
// public void setForeground (Color color) {
// super.setForeground(color);
// foreground = color;
// for (int i = 0; i < sashes.length; i++) {
// sashes[i].setForeground(foreground);
// }
// }
/**
* Sets the layout which is associated with the receiver to be the argument
* which may be null.
* <p>
* Note: No Layout can be set on this Control because it already manages the
* size and position of its children.
* </p>
*
* @param layout the receiver's new layout or null
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*/
public void setLayout( Layout layout ) {
checkWidget();
return;
}
/**
* Specify the control that should take up the entire client area of the
* SashForm. If one control has been maximized, and this method is called with
* a different control, the previous control will be minimized and the new
* control will be maximized. If the value of control is null, the SashForm
* will minimize all controls and return to the default layout where all
* controls are laid out separated by sashes.
*
* @param control the control to be maximized or null
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* </ul>
*/
// public void setMaximizedControl(Control control){
// checkWidget();
// if (control == null) {
// if (maxControl != null) {
// this.maxControl = null;
// layout(false);
// for (int i= 0; i < sashes.length; i++){
// sashes[i].setVisible(true);
// }
// }
// return;
// }
//
// for (int i= 0; i < sashes.length; i++){
// sashes[i].setVisible(false);
// }
// maxControl = control;
// layout(false);
// }
/**
* Specify the relative weight of each child in the SashForm. This will
* determine what percent of the total width (if SashForm has Horizontal
* orientation) or total height (if SashForm has Vertical orientation) each
* control will occupy. The weights must be positive values and there must be
* an entry for each non-sash child of the SashForm.
*
* @param weights the relative weight of each child
* @exception SWTException
* <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the
* thread that created the receiver</li>
* <li>ERROR_INVALID_ARGUMENT - if the weights value is null or
* of incorrect length (must match the number of children)</li>
* </ul>
*/
public void setWeights( int[] weights ) {
checkWidget();
Control[] cArray = getControls( false );
if( weights == null || weights.length != cArray.length ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
int total = 0;
for( int i = 0; i < weights.length; i++ ) {
if( weights[ i ] < 0 ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
total += weights[ i ];
}
if( total == 0 ) {
SWT.error( SWT.ERROR_INVALID_ARGUMENT );
}
for( int i = 0; i < cArray.length; i++ ) {
Object data = cArray[ i ].getLayoutData();
if( data == null || !( data instanceof SashFormData ) ) {
data = new SashFormData();
cArray[ i ].setLayoutData( data );
}
( ( SashFormData )data ).weight = ( ( ( long )weights[ i ] << 16 )
+ total - 1 )
/ total;
}
// layout(false);
layout();
}
}