/****************************************************************************
* Copyright (c) 2008, 2009 Jeremy Dowdall
* 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:
* Jeremy Dowdall <jeremyd@aspencloud.com> - initial API and implementation
*****************************************************************************/
package org.eclipse.nebula.cwt.v;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.nebula.cwt.svg.SvgDocument;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Widget;
/**
* A VControl is a class wich wraps an SWT Button to create a widget that acts
* as much like a native Button as possible while adding the following features:
* <ul>
* <li>The appearance of Label when the mouse is not over it and it does not
* have the focus or selection (if style is SWT.TOGGLE).</li>
* <li>Can fit seemlessly into a larger visual piece - simple set the image to
* that of its background and adjust the image's offset if necessary.</li>
* <li>Can draw polygons and ovals.</li>
* <li>Can center or otherwise align its visual display (text, image, polygon
* or oval).</li>
* </ul>
*/
public abstract class VControl {
public enum Type {
Button, Custom, Label, Native, Panel, Text, Spacer
}
/**
* true if the platform is Carbon, false otherwise
*/
public static final boolean carbon = "carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
/**
* true if the platform is GTK, false otherwise
*/
public static final boolean gtk = "gtk".equals(SWT.getPlatform()); //$NON-NLS-1$
/**
* true if the platform is Win32, false otherwise
*/
public static final boolean win32 = "win32".equals(SWT.getPlatform()); //$NON-NLS-1$
private static final int[] Points_OK = { 2, 6, 5, 9, 10, 3, 9, 2, 5, 7, 3, 5 };
private static final int[] Points_Cancel = { 0, 1, 3, 4, 0, 7, 1, 8, 4, 5, 7, 8, 8, 7, 5, 4, 8, 1, 7, 0, 4, 3, 1, 0 };
private static final int[] Points_Left = { 9, 0, 4, 5, 9, 10 };
private static final int[] Points_Right = { 2, 0, 7, 5, 2, 10 };
private static final int[] Points_Up = { 10, 8, 5, 3, 0, 8 };
private static final int[] Points_Down = { 10, 2, 5, 7, 0, 2 };
private static final int[] Points_Add = { 2, 4, 4, 4, 4, 2, 5, 2, 5, 4, 7, 4, 7, 5, 5, 5, 5, 7, 4, 7, 4, 5, 2, 5 };
private static final int[] Points_Subtract = { 2, 4, 7, 4, 7, 5, 2, 5 };
// public static final int STATE_INACTIVE = 1 << 0;
public static final int STATE_ACTIVE = 1 << 1;
public static final int STATE_SELECTED = 1 << 2;
// public static final int STATE_FOCUS = 1 << 3;
public static final int STATE_ENABLED = 1 << 4;
public static final int STATE_MOUSE_DOWN = 1 << 5;
protected final static boolean containsControl(Control control, Composite composite) {
if(composite != null && !composite.isDisposed()) {
Control[] children = composite.getChildren();
for(Control child : children) {
if(!child.isDisposed()) {
if(child == control) {
return true;
} else if(child instanceof Composite){
return containsControl(control, (Composite) child);
}
}
}
}
return false;
}
Composite composite;
VPanel parent;
private int style;
Menu menu;
Image image;
SvgDocument svg;
String text;
String tooltipText;
int[] points;
Color fill;
Color foreground;
Color background;
private Cursor activeCursor;
private Cursor inactiveCursor;
GridData layoutData;
private int state = STATE_ENABLED;
Rectangle bounds;
int marginTop = 5;
int marginBottom = 5;
int marginLeft = 5;
int marginRight = 5;
int xAlign;
int yAlign;
boolean disposed = false;
boolean square = false;
int visibility = 100;
boolean scaleImage = false;
boolean customToolTip = false;
IControlPainter painter;
Map<String, Object> dataMap;
Map<Integer, List<Listener>> listeners = new HashMap<Integer, List<Listener>>();
private Set<Integer> eventTypes = new HashSet<Integer>();
private Listener listener = new Listener() {
public void handleEvent(Event event) {
if(event.type == SWT.FocusIn) {
if(VControl.this == VTracker.getFocusControl()) {
return;
}
}
VControl.this.handleEvent(event);
}
};
private boolean activatable = true;
/**
* Javadoc out of date // TODO: update javadoc
* @param panel
* @param style
*/
public VControl(VPanel panel, int style) {
setParent(panel);
this.style = style;
bounds = new Rectangle(0, 0, 0, 0);
if((style & SWT.OK) != 0) {
setPolygon(Points_OK);
setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_GREEN));
} else if((style & SWT.CANCEL) != 0) {
setPolygon(Points_Cancel);
setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_RED));
} else if((style & SWT.ARROW) != 0) {
if((style & SWT.DOWN) != 0) {
setPolygon(Points_Down);
} else if((style & SWT.LEFT) != 0) {
setPolygon(Points_Left);
} else if((style & SWT.RIGHT) != 0) {
setPolygon(Points_Right);
} else if((style & SWT.UP) != 0) {
setPolygon(Points_Up);
}
} else if((style & SWT.UP) != 0) {
setPolygon(Points_Add);
} else if((style & SWT.DOWN) != 0) {
setPolygon(Points_Subtract);
}
if(foreground == null) {
setForeground(Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_FOREGROUND));
}
if(fill == null) {
setFill(getForeground());
}
}
void handleEvent(Event event) {
event.data = this;
filterEvent(event);
if(listeners.containsKey(event.type)) {
Listener[] la = listeners.get(event.type).toArray(new Listener[listeners.get(event.type).size()]);
for(Listener listener : la) {
listener.handleEvent(event);
}
}
}
void activate() {
if(activatable && hasState(STATE_ENABLED) && setState(STATE_ACTIVE, true)) {
setState(STATE_MOUSE_DOWN, VTracker.isMouseDown());
setCursor(activeCursor);
attachListeners(false);
if(redrawOnActivate()) {
redraw();
}
notifyListeners(SWT.Activate);
}
}
void addListener(int eventType) {
eventTypes.add(eventType);
if(hasState(STATE_ACTIVE)) {
composite.addListener(eventType, listener);
}
}
public void addListener(int eventType, Listener listener) {
if(!listeners.containsKey(eventType)) {
listeners.put(eventType, new ArrayList<Listener>());
}
listeners.get(eventType).add(listener);
if(hasState(STATE_ACTIVE)) {
composite.addListener(eventType, listener);
}
}
void attachListeners(boolean keyListeners) {
// if(keyListeners && this != VTracker.getFocusControl()) {
// System.out.println("this: " + this + ", focusControl: " + VTracker.getFocusControl());
// throw new UnsupportedOperationException();
// }
// if(!keyListeners || this == VTracker.getFocusControl()) {
Set<Integer> eventTypes = new HashSet<Integer>(this.eventTypes);
eventTypes.addAll(listeners.keySet());
for(Integer eventType : eventTypes) {
if(include(keyListeners, eventType)) {
composite.addListener(eventType, listener);
}
}
// }
}
void detachListeners(boolean keyListeners) {
Set<Integer> eventTypes = new HashSet<Integer>(this.eventTypes);
eventTypes.addAll(listeners.keySet());
for(Integer eventType : eventTypes) {
if(include(keyListeners, eventType)) {
composite.removeListener(eventType, listener);
}
}
}
public Point computeSize(int wHint, int hHint) {
return computeSize(wHint, hHint, true);
}
public Point computeSize(int wHint, int hHint, boolean changed) {
if(wHint != SWT.DEFAULT && wHint < 0) {
wHint = 0;
}
if(hHint != SWT.DEFAULT && hHint < 0) {
hHint = 0;
}
Point size = new Point(2, 2);
if(image != null) {
Rectangle r = image.getBounds();
size.x = r.width;
size.y = r.height;
} else if(points != null) {
if(points.length > 2) {
int minX = points[0];
int maxX = points[0];
int minY = points[1];
int maxY = points[1];
for(int i = 2; i < (points.length - 1); i++) {
minX = Math.min(minX, points[i]);
maxX = Math.max(maxX, points[i]);
minY = Math.min(minY, points[i + 1]);
maxY = Math.max(maxY, points[i + 1]);
}
size.x += maxX - minX;
size.y += maxY - minY;
} else {
size.x += points[0];
size.y += points[1];
}
}
if(text != null) {
GC gc = new GC(composite);
Point tSize = gc.textExtent(text);
gc.dispose();
size.x += tSize.x;
size.y += tSize.y;
}
size.x += (marginLeft + marginRight);
size.y += (marginTop + marginBottom);
if(square) {
size.x = size.y = Math.max(size.x, size.y);
}
return size;
}
public Menu createMenu() {
menu = new Menu(composite);
addListener(SWT.MouseDown, new Listener() {
public void handleEvent(Event event) {
if(SWT.MouseDown == event.type && event.button == 3) {
menu.setVisible(true);
}
}
});
return menu;
}
void deactivate() {
if(setState(STATE_ACTIVE, false)) {
setState(STATE_MOUSE_DOWN, false);
setCursor(inactiveCursor);
detachListeners(false);
if(redrawOnDeactivate()) {
redraw();
}
notifyListeners(SWT.Deactivate);
}
}
public void dispose() {
if(!disposed) {
disposed = true;
notifyListeners(SWT.Dispose, new Event());
if(this == VTracker.getActiveControl()) {
VTracker.instance().deactivate(this);
}
if(this == VTracker.getFocusControl()) {
VTracker.instance().setFocusControl(null);
}
if(!composite.isDisposed()) {
detachListeners(true);
detachListeners(false);
}
setParent(null);
if(painter != null) {
painter.dispose();
}
listeners.clear();
listeners = null;
text = null;
// tooltip = null;
tooltipText = null;
image = null;
points = null;
}
}
public Color getBackground() {
if(background != null) {
return background;
}
VPanel p = parent;
while(p != null) {
if(p.background != null)
return p.background;
p = p.parent;
}
return composite.getBackground();
}
public Rectangle getBounds() {
return new Rectangle(bounds.x, bounds.y, bounds.width, bounds.height);
}
public Rectangle getClientArea() {
return new Rectangle(bounds.x + marginLeft, bounds.y + marginTop, bounds.width - (marginLeft + marginRight),
bounds.height - (marginTop + marginBottom));
}
public Point getClientSize() {
return new Point(bounds.width - (marginLeft + marginRight), bounds.height - (marginTop + marginBottom));
}
public Composite getComposite() {
return composite;
}
public Control getControl() {
return composite;
}
public Object getData(Enum<?> name) {
return getData(name.name());
}
public <T> T getData(Enum<?> name, Class<T> clazz) {
return getData(name.name(), clazz);
}
public Object getData(String name) {
if(dataMap != null) {
return dataMap.get(name);
}
return null;
}
@SuppressWarnings("unchecked")
public <T> T getData(String name, Class<T> clazz) {
if(dataMap != null) {
return (T) dataMap.get(name);
}
return null;
}
public Display getDisplay() {
return composite.getDisplay();
}
public boolean getEnabled() {
return hasState(STATE_ENABLED);
}
public Color getForeground() {
return foreground;
}
public Image getImage() {
return image;
}
public GridData getLayoutData() {
return (layoutData != null) ? layoutData : new GridData();
}
protected Listener[] getListeners(int eventType) {
List<Listener> l = listeners.get(eventType);
return l.toArray(new Listener[l.size()]);
}
public Point getLocation() {
return new Point(bounds.x, bounds.y);
}
public Rectangle getMargins() {
return new Rectangle(marginLeft, marginRight, marginTop, marginBottom);
}
public Menu getMenu() {
return menu;
}
public VPanel getParent() {
return parent;
}
public Shell getShell() {
return composite.getShell();
}
public Point getSize() {
return new Point(bounds.width, bounds.height);
}
public int getState() {
return state;
}
public int getStyle() {
return style;
}
/**
* @return the text string displayed on this VControl
*/
public String getText() {
return text;
}
public String getToolTipText() {
return (tooltipText != null) ? tooltipText : "";
}
public abstract Type getType();
public int getVisibility() {
return visibility;
}
public boolean getVisible() {
return visibility > 0;
}
public Composite getWidget() {
return getParent().getWidget();
}
protected void filterEvent(Event event) {
// subclasses to implement if necessary
}
public boolean hasState(int state) {
return (this.state & state) != 0;
}
public boolean hasStyle(int style) {
return (this.style & style) != 0;
}
private boolean include(boolean key, int type) {
if(type == SWT.Selection) {
return false;
}
if(key && (type == SWT.KeyDown || type == SWT.KeyUp || type == SWT.Traverse)) {
return true;
}
if(!key && !(type == SWT.KeyDown || type == SWT.KeyUp || type == SWT.Traverse)) {
return true;
}
return false;
}
public boolean isActivatable() {
return activatable;
}
public boolean isDisposed() {
return disposed;
}
public boolean isEnabled() {
return getEnabled() && ((parent != null) ? parent.isEnabled() : composite.isEnabled());
}
public boolean isSameWidgetAs(VControl control) {
return control != null && getWidget() == control.getWidget();
}
public boolean isSameWidgetAs(Widget widget) {
Composite w = getWidget();
return w == widget || containsControl((Control) widget, w);
}
/**
* @return true if this VControl is to be sized as a square
*/
public boolean isSquare() {
return square;
}
public boolean isVisible() {
return getVisible() && composite.isVisible();
}
public void moveAbove(VControl control) {
parent.move(this, null);
}
public void moveBelow(VControl control) {
parent.move(null, this);
}
public void notifyListeners(int eventType) {
notifyListeners(eventType, null);
}
public void notifyListeners(int eventType, Event event) {
if(listeners.containsKey(eventType)) {
if(event == null) {
event = new Event();
}
event.data = this;
event.type = eventType;
if(this instanceof VNative && eventType == SWT.FocusOut) {
System.out.println("wtf");
}
for(Listener listener : getListeners(eventType)) {
listener.handleEvent(event);
}
}
}
public final void paintControl(Event e) {
if(painter != null && bounds.intersects(e.x, e.y, e.width, e.height) && isVisible()) {
int alpha = e.gc.getAlpha();
int fullX, fullY, fullW, fullH;
fullX = bounds.x - 1;
fullY = bounds.y - 1;
if(parent != null) {
fullW = Math.min(parent.bounds.x + parent.bounds.width - bounds.x, bounds.x + bounds.width) + 1;
fullH = Math.min(parent.bounds.y + parent.bounds.height - bounds.y, bounds.y + bounds.height) + 1;
} else {
fullW = bounds.width;
fullH = bounds.height;
}
int clientX, clientY, clientW, clientH;
clientX = fullX + marginLeft;
clientY = fullY + marginTop;
clientW = fullW - marginLeft - marginRight;
clientH = fullH - marginTop - marginBottom;
e.gc.setClipping(fullX, fullY, fullW, fullH);
setAlpha(e.gc);
painter.paintBackground(this, e);
if(clientW > 0 && clientH > 0) {
e.gc.setClipping(clientX, clientY, clientW, clientH);
setAlpha(e.gc);
painter.paintContent(this, e);
}
e.gc.setClipping(fullX, fullY, fullW, fullH);
setAlpha(e.gc);
painter.paintBorders(this, e);
if(!getEnabled()) {
setAlpha(e.gc, 25);
e.gc.setBackground(e.display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND));
e.gc.fillRectangle(fullX, fullY, fullW, fullH);
}
e.gc.setClipping((Rectangle) null);
e.gc.setAlpha(alpha);
if(listeners.containsKey(SWT.Paint)) {
for(Listener listener : listeners.get(SWT.Paint)) {
listener.handleEvent(e);
}
}
}
}
public void redraw() {
if(composite != null && !composite.isDisposed()) {
composite.redraw(bounds.x, bounds.y, bounds.width, bounds.height, false);
}
}
protected boolean redrawOnActivate() {
return true;
}
protected boolean redrawOnDeactivate() {
return true;
}
void removeListener(int eventType) {
eventTypes.remove(eventType);
if(hasState(STATE_ACTIVE)) {
composite.removeListener(eventType, listener);
}
}
public void removeListener(int eventType, Listener listener) {
if(listeners.containsKey(eventType)) {
listeners.get(eventType).remove(listener);
}
if(hasState(STATE_ACTIVE)) {
composite.removeListener(eventType, listener);
}
}
public void setActivatable(boolean activatable) {
this.activatable = activatable;
}
public void setActiveCursor(Cursor cursor) {
activeCursor = cursor;
}
/**
* @param x
* @param y
*/
public void setAlignment(int x, int y) {
xAlign = x;
yAlign = y;
}
public void setAlpha(GC gc) {
gc.setAlpha((int) (2.55 * (double) visibility));
}
public void setAlpha(GC gc, int alpha) {
gc.setAlpha((int) ((double) alpha * (double) visibility * (double) 0.01));
}
public void setBackground(Color color) {
background = color;
}
public void setBounds(int x, int y, int width, int height) {
boolean moved = (bounds.x != x || bounds.y != y);
boolean resized = (bounds.width != width || bounds.height != height);
bounds.x = x;
bounds.y = y;
bounds.width = width;
bounds.height = height;
Point p = getDisplay().getCursorLocation();
if(bounds.contains(toControl(p))) {
activate();
} else {
deactivate();
}
if(moved) {
notifyListeners(SWT.Move, new Event());
}
if(resized) {
notifyListeners(SWT.Resize, new Event());
}
}
public void setBounds(Rectangle bounds) {
setBounds(bounds.x, bounds.y, bounds.width, bounds.height);
}
public void setCursor(Cursor cursor) {
getComposite().setCursor(cursor);
}
public void setData(Enum<?> name, Object value) {
setData(name.name(), value);
}
public void setData(String name, Object value) {
if(value == null) {
if(dataMap != null) {
dataMap.remove(name);
if(dataMap.isEmpty()) {
dataMap = null;
}
}
} else {
if(dataMap == null) {
dataMap = new HashMap<String, Object>();
}
dataMap.put(name, value);
}
}
public void setEnabled(boolean enabled) {
if(setState(STATE_ENABLED, enabled)) {
if(this instanceof VNative) {
Control c = getControl();
if(c != null) {
c.setEnabled(enabled);
}
}
if(!enabled) {
deactivate();
}
redraw();
}
}
public void setFill(Color color) {
fill = color;
}
public boolean setFocus() {
return VTracker.instance().setFocusControl(this);
}
protected boolean setFocus(boolean focus) {
if(!hasStyle(SWT.NO_FOCUS)) {
if(focus) {
attachListeners(true);
notifyListeners(SWT.FocusIn);
} else {
notifyListeners(SWT.FocusOut);
detachListeners(true);
}
return true;
}
return false;
}
public void setFont(Font font) {
// TODO setFont
}
public void setForeground(Color color) {
foreground = color;
}
public void setImage(Image image) {
this.image = image;
redraw();
}
public void setImage(SvgDocument svg) {
this.svg = svg;
redraw();
}
public void setInactiveCursor(Cursor cursor) {
inactiveCursor = cursor;
}
public void setLayoutData(GridData data) {
layoutData = data;
}
public void setLocation(Point location) {
if(location != null) {
setLocation(location.x, location.y);
}
}
public void setLocation(int x, int y) {
setBounds(x, y, bounds.width, bounds.height);
}
/**
* @param marginWidth
* @param marginHeight
*/
public void setMargins(int marginWidth, int marginHeight) {
setMargins(marginWidth, marginWidth, marginHeight, marginHeight);
}
/**
* @param left
* @param right
* @param top
* @param bottom
*/
public void setMargins(int left, int right, int top, int bottom) {
if(left >= 0) {
marginLeft = left;
}
if(right >= 0) {
marginRight = right;
}
if(top >= 0) {
marginTop = top;
}
if(bottom >= 0) {
marginBottom = bottom;
}
}
public void setMargins(Rectangle margins) {
setMargins(margins.x, margins.y, margins.width, margins.height);
}
public void setOval(int rx, int ry) {
setPolygon(new int[] { rx, ry });
}
public void setOval(int rx, int ry, Color fillColor) {
setPolygon(new int[] { rx, ry }, fillColor);
}
public void setPainter(IControlPainter painter) {
this.painter = painter;
}
public void setParent(VPanel panel) {
if(this.parent != null) {
this.parent.removeChild(this);
}
this.parent = panel;
if(this.parent != null) {
this.composite = this.parent.composite;
this.parent.addChild(this);
}
}
public void setPolygon(int[] points) {
setPolygon(points, (fill != null ? fill : ((background != null) ? background : getForeground())));
}
public void setPolygon(int[] points, Color fillColor) {
if(points == null || points.length < 2 || points.length % 2 != 0) {
return;
}
if(points.length == 2 && (points[0] < 1 || points[1] < 1)) {
return;
}
this.points = points;
setFill(fillColor);
redraw();
}
public void setScaleImage(boolean scale) {
this.scaleImage = scale;
}
public void setSize(Point size) {
if(size != null) {
setBounds(bounds.x, bounds.y, size.x, size.y);
}
}
/**
* if parameter equal is true, the x and y sizes of this VControl will be
* forced equal, thus drawing a square button
*
* @param equal
*/
public void setSquare(boolean equal) {
square = equal;
}
protected boolean setState(int state, boolean set) {
if(set && !hasState(state)) {
this.state |= state;
return true;
} else if(!set && hasState(state)) {
this.state &= ~state;
return true;
}
return false;
}
public void setStyle(int style) {
this.style = style;
}
public boolean setStyle(int style, boolean set) {
if(set && !hasStyle(style)) {
this.style |= style;
return true;
} else if(!set && hasStyle(style)) {
this.style &= ~style;
return true;
}
return false;
}
/**
* @param text
*/
public void setText(String text) {
this.text = text;
redraw();
}
public void setToolTipText(String text) {
tooltipText = text;
}
void setVisibility(int visibility) {
if(visibility > 100) {
visibility = 100;
} else if(visibility < 0) {
visibility = 0;
}
this.visibility = visibility;
if(!isVisible()) {
if(this == VTracker.getFocusControl()) {
VTracker.instance().setFocusControl(null);
}
VTracker.instance().deactivate(this);
}
redraw();
}
public void setVisible(boolean visible) {
setVisibility(visible ? 100 : 0);
}
public void setVisible(final boolean visible, final int duration) {
setVisible(visible, duration, null);
}
public void setVisible(final boolean visible, final int duration, final Runnable callback) {
if(duration <= 0) {
setVisible(visible);
} else {
new Thread() {
@Override
public void run() {
do {
Display.getDefault().syncExec(new Runnable() {
public void run() {
if(!disposed) {
setVisibility(visibility + (visible ? 10 : -10));
composite.update();
}
};
});
if(!disposed) {
try {
Thread.sleep(5 * duration / 100);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
} while(!disposed && visibility > 0 && visibility < 100);
if(!disposed && visibility != 0 && visibility != 100) {
Display.getDefault().syncExec(new Runnable() {
public void run() {
if(!disposed) {
setVisible(visible);
composite.update();
}
};
});
}
if(callback != null) {
callback.run();
}
}
}.start();
}
}
public Point toControl(Point point) {
return getComposite().toControl(point);
}
public Point toControl(int x, int y) {
return getComposite().toControl(x, y);
}
public Point toDisplay(Point point) {
return getComposite().toDisplay(point);
}
public Point toDisplay(int x, int y) {
return getComposite().toDisplay(x, y);
}
@Override
public String toString() {
return super.toString() + " {" + text + "}";
}
public void update() {
if(composite != null && !composite.isDisposed()) {
composite.update();
}
}
}