blob: 1e270c26863c4d70f3295382ab1e979194cae974 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2002, 2012 Innoopract Informationssysteme GmbH 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:
* Innoopract Informationssysteme GmbH - initial API and implementation
* EclipseSource - ongoing development
******************************************************************************/
package org.eclipse.swt.widgets;
import org.eclipse.rwt.lifecycle.ProcessActionRunner;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.internal.SerializableCompatibility;
import org.eclipse.swt.internal.widgets.ControlHolder;
import org.eclipse.swt.internal.widgets.ICompositeAdapter;
import org.eclipse.swt.internal.widgets.IControlHolderAdapter;
/**
* Instances of this class are controls which are capable
* of containing other controls.
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>NO_FOCUS, NO_RADIO_GROUP</dd>
* <dt><b>Events:</b></dt>
* <dd>(none)</dd>
* </dl>
* <p>
* This class may be subclassed by custom control implementors
* who are building controls that are constructed from aggregates
* of other controls.
* </p>
*/
public class Composite extends Scrollable {
private final ICompositeAdapter compositeAdapter;
private Layout layout;
int layoutCount;
private final ControlHolder controlHolder;
private Control[] tabList;
int backgroundMode;
Composite( Composite parent ) {
// prevent instantiation from outside this package
super( parent );
controlHolder = new ControlHolder();
compositeAdapter = new CompositeAdapter();
}
/**
* 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#NO_FOCUS
* @see SWT#NO_RADIO_GROUP
* @see Widget#getStyle
*/
public Composite( Composite parent, int style ) {
super( parent, style );
controlHolder = new ControlHolder();
compositeAdapter = new CompositeAdapter();
}
@Override
void initState() {
if( ( style & ( SWT.H_SCROLL | SWT.V_SCROLL ) ) == 0 ) {
state |= THEME_BACKGROUND;
}
}
/**
* Returns a (possibly empty) array containing the receiver's children.
* Children are returned in the order that they are drawn. The topmost
* control appears at the beginning of the array. Subsequent controls
* draw beneath this control and appear later in the array.
* <p>
* Note: This is not the actual structure used by the receiver
* to maintain its list of children, so modifying the array will
* not affect the receiver.
* </p>
*
* @return an array of children
*
* @see Control#moveAbove
* @see Control#moveBelow
*
* @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 Control[] getChildren() {
checkWidget();
return controlHolder.getControls();
}
@Override
@SuppressWarnings("unchecked")
public <T> T getAdapter( Class<T> adapter ) {
T result;
if( adapter == IControlHolderAdapter.class ) {
result = ( T )controlHolder;
} else if( adapter == ICompositeAdapter.class ) {
result = ( T )compositeAdapter;
} else {
result = super.getAdapter( adapter );
}
return result;
}
//////////////////
// Layout methods
/**
* Sets the layout which is associated with the receiver to be
* the argument which may be null.
*
* @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();
this.layout = layout;
}
/**
* Returns layout which is associated with the receiver, or
* null if one has not been set.
*
* @return the receiver's 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 Layout getLayout() {
checkWidget();
return layout;
}
/**
* If the argument is <code>true</code>, causes subsequent layout
* operations in the receiver or any of its children to be ignored.
* No layout of any kind can occur in the receiver or any of its
* children until the flag is set to false.
* Layout operations that occurred while the flag was
* <code>true</code> are remembered and when the flag is set to
* <code>false</code>, the layout operations are performed in an
* optimized manner. Nested calls to this method are stacked.
*
* @param defer the new defer state
*
* @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>
*
* @see #layout(boolean)
* @see #layout(Control[])
*
* @since 1.3
*/
public void setLayoutDeferred( boolean defer ) {
checkWidget();
if( !defer ) {
if( --layoutCount == 0 ) {
if( ( state & LAYOUT_CHILD ) != 0 || ( state & LAYOUT_NEEDED ) != 0 ) {
updateLayout( true, true );
}
}
} else {
layoutCount++;
}
}
/**
* Returns <code>true</code> if the receiver has deferred
* the performing of layout, and <code>false</code> otherwise.
*
* @return the receiver's deferred layout state
*
* @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>
*
* @see #setLayoutDeferred(boolean)
* @see #isLayoutDeferred()
*/
public boolean getLayoutDeferred() {
checkWidget();
return layoutCount > 0;
}
/**
* Returns <code>true</code> if the receiver or any ancestor
* up to and including the receiver's nearest ancestor shell
* has deferred the performing of layouts. Otherwise, <code>false</code>
* is returned.
*
* @return the receiver's deferred layout state
*
* @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>
*
* @see #setLayoutDeferred(boolean)
* @see #getLayoutDeferred()
*/
public boolean isLayoutDeferred() {
checkWidget();
return findDeferredControl() != null;
}
/**
* If the receiver has a layout, asks the layout to <em>lay out</em>
* (that is, set the size and location of) the receiver's children.
* If the receiver does not have a layout, do nothing.
* <p>
* This is equivalent to calling <code>layout(true)</code>.
* </p>
* <p>
* Note: Layout is different from painting. If a child is
* moved or resized such that an area in the parent is
* exposed, then the parent will paint. If no child is
* affected, the parent will not paint.
* </p>
*
* @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 layout() {
checkWidget();
layout( true );
}
/**
* If the receiver has a layout, asks the layout to <em>lay out</em>
* (that is, set the size and location of) the receiver's children.
* If the argument is <code>true</code> the layout must not rely
* on any information it has cached about the immediate children. If it
* is <code>false</code> the layout may (potentially) optimize the
* work it is doing by assuming that none of the receiver's
* children has changed state since the last layout.
* If the receiver does not have a layout, do nothing.
* <p>
* If a child is resized as a result of a call to layout, the
* resize event will invoke the layout of the child. The layout
* will cascade down through all child widgets in the receiver's widget
* tree until a child is encountered that does not resize. Note that
* a layout due to a resize will not flush any cached information
* (same as <code>layout(false)</code>).
* </p>
* <p>
* Note: Layout is different from painting. If a child is
* moved or resized such that an area in the parent is
* exposed, then the parent will paint. If no child is
* affected, the parent will not paint.
* </p>
*
* @param changed <code>true</code> if the layout must flush its caches, and <code>false</code> otherwise
*
* @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 layout( boolean changed ) {
checkWidget();
if( layout != null ) {
layout( changed, false );
}
}
/**
* If the receiver has a layout, asks the layout to <em>lay out</em>
* (that is, set the size and location of) the receiver's children.
* If the changed argument is <code>true</code> the layout must not rely
* on any information it has cached about its children. If it
* is <code>false</code> the layout may (potentially) optimize the
* work it is doing by assuming that none of the receiver's
* children has changed state since the last layout.
* If the all argument is <code>true</code> the layout will cascade down
* through all child widgets in the receiver's widget tree, regardless of
* whether the child has changed size. The changed argument is applied to
* all layouts. If the all argument is <code>false</code>, the layout will
* <em>not</em> cascade down through all child widgets in the receiver's widget
* tree. However, if a child is resized as a result of a call to layout, the
* resize event will invoke the layout of the child. Note that
* a layout due to a resize will not flush any cached information
* (same as <code>layout(false)</code>).
* </p>
* <p>
* Note: Layout is different from painting. If a child is
* moved or resized such that an area in the parent is
* exposed, then the parent will paint. If no child is
* affected, the parent will not paint.
* </p>
*
* @param changed <code>true</code> if the layout must flush its caches, and <code>false</code> otherwise
* @param all <code>true</code> if all children in the receiver's widget tree should be laid out, and <code>false</code> otherwise
*
* @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 layout( boolean changed, boolean all ) {
checkWidget();
if( layout != null || all ) {
markLayout( changed, all );
updateLayout( true, all );
}
}
/**
* Forces a lay out (that is, sets the size and location) of all widgets that
* are in the parent hierarchy of the changed control up to and including the
* receiver. The layouts in the hierarchy must not rely on any information
* cached about the changed control or any of its ancestors. The layout may
* (potentially) optimize the work it is doing by assuming that none of the
* peers of the changed control have changed state since the last layout.
* If an ancestor does not have a layout, skip it.
* <p>
* Note: Layout is different from painting. If a child is
* moved or resized such that an area in the parent is
* exposed, then the parent will paint. If no child is
* affected, the parent will not paint.
* </p>
*
* @param changed a control that has had a state change which requires a recalculation of its size
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the changed array is null any of its controls are null or have been disposed</li>
* <li>ERROR_INVALID_PARENT - if any control in changed is not in the widget tree of the receiver</li>
* </ul>
* @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 layout( Control[] changed ) {
checkWidget();
if( changed == null ) {
error( SWT.ERROR_INVALID_ARGUMENT );
}
layout( changed, SWT.NONE );
}
/**
* Forces a lay out (that is, sets the size and location) of all widgets that
* are in the parent hierarchy of the changed control up to and including the
* receiver.
* <p>
* The parameter <code>flags</code> may be a combination of:
* <dl>
* <dt><b>SWT.ALL</b></dt>
* <dd>all children in the receiver's widget tree should be laid out</dd>
* <dt><b>SWT.CHANGED</b></dt>
* <dd>the layout must flush its caches</dd>
* <dt><b>SWT.DEFER</b></dt>
* <dd>layout will be deferred</dd>
* </dl>
* </p>
* <p>
* When the <code>changed</code> array is specified, the flags <code>SWT.ALL</code>
* and <code>SWT.CHANGED</code> have no effect. In this case, the layouts in the
* hierarchy must not rely on any information cached about the changed control or
* any of its ancestors. The layout may (potentially) optimize the
* work it is doing by assuming that none of the peers of the changed
* control have changed state since the last layout.
* If an ancestor does not have a layout, skip it.
* </p>
* <p>
* When the <code>changed</code> array is not specified, the flag <code>SWT.ALL</code>
* indicates that the whole widget tree should be laid out. And the flag
* <code>SWT.CHANGED</code> indicates that the layouts should flush any cached
* information for all controls that are laid out.
* </p>
* <p>
* The <code>SWT.DEFER</code> flag always causes the layout to be deferred by
* calling <code>Composite.setLayoutDeferred(true)</code> and scheduling a call
* to <code>Composite.setLayoutDeferred(false)</code>, which will happen when
* appropriate (usually before the next event is handled). When this flag is set,
* the application should not call <code>Composite.setLayoutDeferred()</code>.
* </p>
* <p>
* Note: Layout is different from painting. If a child is
* moved or resized such that an area in the parent is
* exposed, then the parent will paint. If no child is
* affected, the parent will not paint.
* </p>
*
* @param changed a control that has had a state change which requires a recalculation of its size
* @param flags the flags specifying how the layout should happen
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if any of the controls in changed is null or has been disposed</li>
* <li>ERROR_INVALID_PARENT - if any control in changed is not in the widget tree of the receiver</li>
* </ul>
* @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>
*
* @since 1.3
*/
// [if]: This is a verbatim copy of SWT, thus no reformatting was done.
public void layout (Control [] changed, int flags) {
checkWidget ();
if (changed != null) {
for (int i=0; i<changed.length; i++) {
Control control = changed [i];
if (control == null) {
error (SWT.ERROR_INVALID_ARGUMENT);
}
if (control.isDisposed ()) {
error (SWT.ERROR_INVALID_ARGUMENT);
}
boolean ancestor = false;
Composite composite = control.parent;
while (composite != null) {
ancestor = composite == this;
if (ancestor) {
break;
}
composite = composite.parent;
}
if (!ancestor) {
error (SWT.ERROR_INVALID_PARENT);
}
}
int updateCount = 0;
Composite [] update = new Composite [16];
for (int i=0; i<changed.length; i++) {
Control child = changed [i];
Composite composite = child.parent;
while (child != this) {
if (composite.layout != null) {
composite.state |= LAYOUT_NEEDED;
if (!composite.layout.flushCache (child)) {
composite.state |= LAYOUT_CHANGED;
}
}
if (updateCount == update.length) {
Composite [] newUpdate = new Composite [update.length + 16];
System.arraycopy (update, 0, newUpdate, 0, update.length);
update = newUpdate;
}
child = update [updateCount++] = composite;
composite = child.parent;
}
}
if ((flags & SWT.DEFER) != 0) {
setLayoutDeferred (true);
display.addLayoutDeferred (this);
}
for (int i=updateCount-1; i>=0; i--) {
update [i].updateLayout (false);
}
} else {
if (layout == null && (flags & SWT.ALL) == 0) {
return;
}
markLayout ((flags & SWT.CHANGED) != 0, (flags & SWT.ALL) != 0);
if ((flags & SWT.DEFER) != 0) {
setLayoutDeferred (true);
display.addLayoutDeferred (this);
}
updateLayout ((flags & SWT.ALL) != 0);
}
}
@Override
void markLayout( boolean changed, boolean all ) {
if( layout != null ) {
state |= LAYOUT_NEEDED;
if( changed ) {
state |= LAYOUT_CHANGED;
}
}
if( all ) {
Control[] children = controlHolder.getControls();
for( int i = 0; i < children.length; i++ ) {
children[ i ].markLayout( changed, all );
}
}
}
void updateLayout( boolean all ) {
updateLayout( true, all );
}
@Override
void updateLayout( boolean resize, boolean all ) {
Composite parent = findDeferredControl();
if( parent != null ) {
parent.state |= LAYOUT_CHILD;
return;
}
if( ( state & LAYOUT_NEEDED ) != 0 ) {
boolean changed = ( state & LAYOUT_CHANGED ) != 0;
state &= ~( LAYOUT_NEEDED | LAYOUT_CHANGED );
// if (resize) setResizeChildren (false);
layout.layout( this, changed );
// if (resize) setResizeChildren (true);
}
if( all ) {
state &= ~LAYOUT_CHILD;
Control[] children = controlHolder.getControls();
for( int i = 0; i < children.length; i++ ) {
children[ i ].updateLayout( resize, all );
}
}
}
Composite findDeferredControl() {
return layoutCount > 0 ? this : parent.findDeferredControl();
}
@Override
public Point computeSize( int wHint, int hHint, boolean changed ) {
checkWidget();
Point size;
boolean hasChanged = changed;
if( layout != null ) {
if( wHint == SWT.DEFAULT || hHint == SWT.DEFAULT ) {
hasChanged |= ( state & LAYOUT_CHANGED ) != 0;
state &= ~LAYOUT_CHANGED;
size = layout.computeSize( this, wHint, hHint, hasChanged );
} else {
size = new Point( wHint, hHint );
}
} else {
size = minimumSize( wHint, hHint, hasChanged );
}
if( size.x == 0 ) {
size.x = DEFAULT_WIDTH;
}
if( size.y == 0 ) {
size.y = DEFAULT_HEIGHT;
}
if( wHint != SWT.DEFAULT ) {
size.x = wHint;
}
if( hHint != SWT.DEFAULT ) {
size.y = hHint;
}
Rectangle trim = computeTrim( 0, 0, size.x, size.y );
return new Point( trim.width, trim.height );
}
/**
* Clears any data that has been cached by a Layout for all widgets that
* are in the parent hierarchy of the changed control up to and including the
* receiver. If an ancestor does not have a layout, it is skipped.
*
* @param changed an array of controls that changed state and require a recalculation of size
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if the changed array is null any of its controls are null or have been disposed</li>
* <li>ERROR_INVALID_PARENT - if any control in changed is not in the widget tree of the receiver</li>
* </ul>
* @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>
*
* @since 1.0
*/
public void changed( Control[] changed ) {
checkWidget();
if( changed == null ) {
error( SWT.ERROR_INVALID_ARGUMENT );
}
for( int i = 0; i < changed.length; i++ ) {
Control control = changed[ i ];
if( control == null ) {
error( SWT.ERROR_INVALID_ARGUMENT );
}
if( control.isDisposed() ) {
error( SWT.ERROR_INVALID_ARGUMENT );
}
boolean ancestor = false;
Composite composite = control.parent;
while( composite != null ) {
ancestor = composite == this;
if( ancestor ) {
break;
}
composite = composite.parent;
}
if( !ancestor ) {
error( SWT.ERROR_INVALID_PARENT );
}
}
for( int i = 0; i < changed.length; i++ ) {
Control child = changed[ i ];
Composite composite = child.parent;
while( child != this ) {
if( composite.layout == null || !composite.layout.flushCache( child ) )
{
composite.state |= LAYOUT_CHANGED;
}
child = composite;
composite = child.parent;
}
}
}
/**
* Returns the receiver's background drawing mode. This
* will be one of the following constants defined in class
* <code>SWT</code>:
* <code>INHERIT_NONE</code>, <code>INHERIT_DEFAULT</code>,
* <code>INHERTIT_FORCE</code>.
*
* @return the background mode
*
* @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>
*
* @see SWT
*
* @since 1.1
*/
public int getBackgroundMode() {
checkWidget();
return backgroundMode;
}
/**
* Sets the background drawing mode to the argument which should be one of the
* following constants defined in class <code>SWT</code>:
* <code>INHERIT_NONE</code>, <code>INHERIT_DEFAULT</code>,
* <code>INHERIT_FORCE</code>.
*
* @param mode the new background mode
* @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>
* @see SWT
* @since 1.1
*/
public void setBackgroundMode( int mode ) {
checkWidget();
backgroundMode = mode;
Control[] children = controlHolder.getControls();
for( int i = 0; i < children.length; i++ ) {
children[ i ].updateBackgroundMode();
}
}
@Override
void updateBackgroundMode() {
super.updateBackgroundMode();
Control[] children = controlHolder.getControls();
for( int i = 0; i < children.length; i++ ) {
children[ i ].updateBackgroundMode();
}
}
// ///////////////////
// setFocus override
@Override
public boolean setFocus() {
checkWidget();
Control[] children = getChildren();
// for( int i = 0; i < children.length; i++ ) {
// Control child = children[ i ];
// if( child.setRadioFocus() )
// return true;
// }
Control focusedChild = null;
for( int i = 0; focusedChild == null && i < children.length; i++ ) {
Control child = children[ i ];
if( child.setFocus() ) {
focusedChild = child;
}
}
boolean result = true;
if( focusedChild == null ) {
result = super.setFocus();
}
return result;
}
////////////
// Tab Order
/**
* Sets the tabbing order for the specified controls to
* match the order that they occur in the argument list.
*
* @param tabList the ordered list of controls representing the tab order or null
*
* @exception IllegalArgumentException <ul>
* <li>ERROR_INVALID_ARGUMENT - if a widget in the tabList is null or has been disposed</li>
* <li>ERROR_INVALID_PARENT - if widget in the tabList is not in the same widget tree</li>
* </ul>
* @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 setTabList( Control[] tabList ) {
checkWidget();
Control[] newList = tabList;
if( tabList != null ) {
for( int i = 0; i < tabList.length; i++ ) {
Control control = tabList[ i ];
if( control == null ) {
error( SWT.ERROR_INVALID_ARGUMENT );
}
if( control.isDisposed() ) {
error( SWT.ERROR_INVALID_ARGUMENT );
}
if( control.parent != this ) {
error( SWT.ERROR_INVALID_PARENT );
}
}
newList = new Control[ tabList.length ];
System.arraycopy( tabList, 0, newList, 0, tabList.length );
}
this.tabList = newList;
}
/**
* Gets the (possibly empty) tabbing order for the control.
*
* @return tabList the ordered list of controls representing the tab order
*
* @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>
*
* @see #setTabList
*/
// returns only tabGroups
public Control[] getTabList() {
checkWidget();
Control[] result = _getTabList();
if( result == null ) {
int count = 0;
Control[] list = controlHolder.getControls();
for( int i = 0; i < list.length; i++ ) {
if( list[ i ].isTabGroup() ) {
count++;
}
}
result = new Control[ count ];
int index = 0;
for( int i = 0; i < list.length; i++ ) {
if( list[ i ].isTabGroup() ) {
result[ index++ ] = list[ i ];
}
}
}
return result;
}
// filters disposed controls out
Control[] _getTabList() {
if( tabList != null ) {
int count = 0;
for( int i = 0; i < tabList.length; i++ ) {
if( !tabList[ i ].isDisposed() ) {
count++;
}
}
if( count != tabList.length ) {
Control[] newList = new Control[ count ];
int index = 0;
for( int i = 0; i < tabList.length; i++ ) {
if( !tabList[ i ].isDisposed() ) {
newList[ index++ ] = tabList[ i ];
}
}
tabList = newList;
}
}
return tabList;
}
@Override
boolean isTabGroup() {
return true;
}
/////////////////////////////////////
// Helping method used by computeSize
Point minimumSize( int wHint, int hHint, boolean changed ) {
Control[] children = getChildren();
Rectangle clientArea = getClientArea();
int width = 0, height = 0;
for( int i = 0; i < children.length; i++ ) {
Rectangle rect = children[ i ].getBounds();
width = Math.max( width, rect.x - clientArea.x + rect.width );
height = Math.max( height, rect.y - clientArea.y + rect.height );
}
return new Point( width, height );
}
/////////////////////////////////////////////////
// Internal methods to maintain the child controls
@Override
void releaseChildren() {
super.releaseChildren();
Control[] children = controlHolder.getControls();
for( int i = 0; i < children.length; i++ ) {
children[ i ].dispose();
}
}
void removeControl( Control control ) {
if( controlHolder.contains( control ) ) {
controlHolder.remove( control );
}
}
////////////////
// Resize helper
@Override
void notifyResize( Point oldSize ) {
// TODO [rh] revise this: the SWT code (method sendResize) first calls
// 'super' (fires resize events) and *then* does the layouting
if( !oldSize.equals( getSize() ) || isLayoutNeeded() ) {
ProcessActionRunner.add( new Runnable() {
public void run() {
if( !isDisposed() && layout != null ) {
markLayout( false, false );
updateLayout( false, false );
}
}
} );
}
super.notifyResize( oldSize );
}
private boolean isLayoutNeeded() {
return ( state & LAYOUT_NEEDED ) != 0;
}
///////////////////
// Skinning support
@Override
void reskinChildren( int flags ) {
super.reskinChildren( flags );
Control[] children = controlHolder.getControls();
for( int i = 0; i < children.length; i++ ) {
Control child = children[ i ];
if( child != null ) {
child.reskin( flags );
}
}
}
////////////////
// Inner classes
private final class CompositeAdapter implements ICompositeAdapter, SerializableCompatibility {
public void markLayoutNeeded() {
markLayout( false, false );
}
}
}