introduce listeners to extend layout and painting behaviour

Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java
index a9b4238..45e4217 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/widget/swt/BoxWidget.java
@@ -10,6 +10,8 @@
  *******************************************************************************/
 package org.eclipse.vex.core.internal.widget.swt;
 
+import java.util.concurrent.CopyOnWriteArrayList;
+
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.ControlEvent;
 import org.eclipse.swt.events.ControlListener;
@@ -52,6 +54,9 @@
 	private Runnable nextRenderer;
 	private final Object rendererMonitor = new Object();
 
+	private final CopyOnWriteArrayList<ILayoutListener> layoutListeners = new CopyOnWriteArrayList<ILayoutListener>();
+	private final CopyOnWriteArrayList<IPaintingListener> paintingListeners = new CopyOnWriteArrayList<IPaintingListener>();
+
 	public BoxWidget(final Composite parent, final int style) {
 		super(parent, style | SWT.NO_BACKGROUND);
 		connectDispose();
@@ -68,6 +73,10 @@
 		this.rootBox = rootBox;
 	}
 
+	public void invalidate() {
+		scheduleRenderer(new Painter(getDisplay(), getVerticalBar().getSelection(), getSize().x, getSize().y));
+	}
+
 	private void connectDispose() {
 		addDisposeListener(new DisposeListener() {
 			@Override
@@ -186,17 +195,15 @@
 	}
 
 	private void layoutRootBox(final Graphics graphics) {
-		System.out.print("Layout ");
-		final long start = System.currentTimeMillis();
+		fireLayoutStarting(graphics);
 		rootBox.layout(graphics);
-		System.out.println("took " + (System.currentTimeMillis() - start));
+		fireLayoutFinished(graphics);
 	}
 
 	private void paintRootBox(final Graphics graphics) {
-		System.out.print("Painting ");
-		final long start = System.currentTimeMillis();
+		firePaintingStarting(graphics);
 		rootBox.paint(graphics);
-		System.out.println("took " + (System.currentTimeMillis() - start));
+		firePaintingFinished(graphics);
 	}
 
 	private void updateVerticalBar() {
@@ -211,6 +218,62 @@
 		});
 	}
 
+	public void addLayoutListener(final ILayoutListener listener) {
+		layoutListeners.add(listener);
+	}
+
+	public void removeLayoutListener(final ILayoutListener listener) {
+		layoutListeners.remove(listener);
+	}
+
+	private void fireLayoutStarting(final Graphics graphics) {
+		for (final ILayoutListener listener : layoutListeners) {
+			try {
+				listener.layoutStarting(graphics);
+			} catch (final Throwable t) {
+				t.printStackTrace();
+			}
+		}
+	}
+
+	private void fireLayoutFinished(final Graphics graphics) {
+		for (final ILayoutListener listener : layoutListeners) {
+			try {
+				listener.layoutFinished(graphics);
+			} catch (final Throwable t) {
+				t.printStackTrace();
+			}
+		}
+	}
+
+	public void addPaintingListener(final IPaintingListener listener) {
+		paintingListeners.add(listener);
+	}
+
+	public void removePaintingListener(final IPaintingListener listener) {
+		paintingListeners.remove(listener);
+	}
+
+	private void firePaintingStarting(final Graphics graphics) {
+		for (final IPaintingListener listener : paintingListeners) {
+			try {
+				listener.paintingStarting(graphics);
+			} catch (final Throwable t) {
+				t.printStackTrace();
+			}
+		}
+	}
+
+	private void firePaintingFinished(final Graphics graphics) {
+		for (final IPaintingListener listener : paintingListeners) {
+			try {
+				listener.paintingFinished(graphics);
+			} catch (final Throwable t) {
+				t.printStackTrace();
+			}
+		}
+	}
+
 	private class Layouter implements Runnable {
 
 		private final int top;
@@ -265,4 +328,17 @@
 			rendererFinished();
 		}
 	}
+
+	public static interface ILayoutListener {
+		void layoutStarting(Graphics graphics);
+
+		void layoutFinished(Graphics graphics);
+	}
+
+	public static interface IPaintingListener {
+		void paintingStarting(Graphics graphics);
+
+		void paintingFinished(Graphics graphics);
+	}
+
 }
diff --git a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/boxview/BoxView.java b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/boxview/BoxView.java
index cf9521a..e875f18 100644
--- a/org.eclipse.vex.ui/src/org/eclipse/vex/ui/boxview/BoxView.java
+++ b/org.eclipse.vex.ui/src/org/eclipse/vex/ui/boxview/BoxView.java
@@ -34,6 +34,8 @@
 import org.eclipse.vex.core.internal.dom.Document;
 import org.eclipse.vex.core.internal.dom.Element;
 import org.eclipse.vex.core.internal.widget.swt.BoxWidget;
+import org.eclipse.vex.core.internal.widget.swt.BoxWidget.ILayoutListener;
+import org.eclipse.vex.core.internal.widget.swt.BoxWidget.IPaintingListener;
 import org.eclipse.vex.core.provisional.dom.BaseNodeVisitorWithResult;
 import org.eclipse.vex.core.provisional.dom.IDocument;
 import org.eclipse.vex.core.provisional.dom.IElement;
@@ -78,6 +80,38 @@
 			cleanStaleReferenceInShell();
 		}
 		boxWidget = new BoxWidget(parent, SWT.V_SCROLL);
+		boxWidget.addLayoutListener(new ILayoutListener() {
+			private long startTime;
+
+			@Override
+			public void layoutStarting(final Graphics graphics) {
+				System.out.print("Layout ");
+				startTime = System.currentTimeMillis();
+			}
+
+			@Override
+			public void layoutFinished(final Graphics graphics) {
+				final long duration = System.currentTimeMillis() - startTime;
+				System.out.println("took " + duration + "ms");
+			}
+
+		});
+		boxWidget.addPaintingListener(new IPaintingListener() {
+			private long startTime;
+
+			@Override
+			public void paintingStarting(final Graphics graphics) {
+				System.out.print("Painting ");
+				startTime = System.currentTimeMillis();
+			}
+
+			@Override
+			public void paintingFinished(final Graphics graphics) {
+				final long duration = System.currentTimeMillis() - startTime;
+				System.out.println("took " + duration + "ms");
+			}
+
+		});
 		boxWidget.setContent(createTestModel());
 		parent.layout();
 	}