/******************************************************************************* * Copyright (c) 2000, 2008 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.draw2d; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.widgets.Display; import org.eclipse.draw2d.geometry.Rectangle; import com.cari.voip.keyboard.soft.Activator; /** * An UpdateManager that asynchronously updates the affected figures. */ public class DeferredUpdateManager extends UpdateManager { /** * Calls {@link DeferredUpdateManager#performUpdate()}. */ protected class UpdateRequest implements Runnable { public UpdateRequest() { super(); } /** * Calls {@link DeferredUpdateManager#performUpdate()}. */ public void run() { performUpdate(); } } private Rectangle damage; private Map dirtyRegions = new HashMap(); private GraphicsSource graphicsSource; private List invalidFigures = new ArrayList(); private IFigure root; private boolean updateQueued; private boolean updating; private boolean validating; private RunnableChain afterUpdate; private static class RunnableChain { RunnableChain next; Runnable run; RunnableChain(Runnable run, RunnableChain next) { this.run = run; this.next = next; } void run() { if (next != null) next.run(); run.run(); } } /** * Empty constructor. */ public DeferredUpdateManager() { } /** * Constructs a new DererredUpdateManager with the given GraphicsSource. * @param gs the graphics source */ public DeferredUpdateManager(GraphicsSource gs) { setGraphicsSource(gs); } /** * Adds a dirty region (defined by the rectangle <i>x, y, w, h</i>) to the update queue. * If the figure isn't visible or either the width or height are 0, the method returns * without queueing the dirty region. * * @param figure the figure that contains the dirty region * @param x the x coordinate of the dirty region * @param y the y coordinate of the dirty region * @param w the width of the dirty region * @param h the height of the dirty region */ public synchronized void addDirtyRegion(IFigure figure, int x, int y, int w, int h) { if (w == 0 || h == 0 || !figure.isShowing()) return; Rectangle rect = (Rectangle)dirtyRegions.get(figure); if (rect == null) { rect = new Rectangle(x, y, w, h); dirtyRegions.put(figure, rect); } else rect.union(x, y, w, h); queueWork(); } /** * Adds the given figure to the update queue. Invalid figures will be validated before * the damaged regions are repainted. * * @param f the invalid figure */ public synchronized void addInvalidFigure(IFigure f) { if (invalidFigures.contains(f)) return; queueWork(); invalidFigures.add(f); } /** * Returns a Graphics object for the given region. * @param region the region to be repainted * @return the Graphics object */ protected Graphics getGraphics(Rectangle region) { if (graphicsSource == null) return null; return graphicsSource.getGraphics(region); } void paint(GC gc) { if (!validating) { SWTGraphics graphics = new SWTGraphics(gc); if (!updating) { /** * If a paint occurs not as part of an update, we should notify that the region * is being painted. Otherwise, notification already occurs in repairDamage(). */ Rectangle rect = graphics.getClip(new Rectangle()); HashMap map = new HashMap(); map.put(root, rect); firePainting(rect, map); } performValidation(); root.paint(graphics); graphics.dispose(); } else { /* * If figures are being validated then we can simply * add a dirty region here and update will repaint this region with other * dirty regions when it gets to painting. We can't paint if we're not sure * that all figures are valid. */ addDirtyRegion(root, new Rectangle(gc.getClipping())); } } /** * Performs the update. Validates the invalid figures and then repaints the dirty * regions. * @see #validateFigures() * @see #repairDamage() */ public synchronized void performUpdate() { if (isDisposed() || updating) return; updating = true; try { performValidation(); updateQueued = false; repairDamage(); if (afterUpdate != null) { RunnableChain chain = afterUpdate; afterUpdate = null; chain.run(); //chain may queue additional Runnable. if (afterUpdate != null) queueWork(); } } finally { updating = false; } } /** * @see UpdateManager#performValidation() */ public void performValidation() { if (invalidFigures.isEmpty() || validating) return; try { IFigure fig; validating = true; fireValidating(); for (int i = 0; i < invalidFigures.size(); i++) { fig = (IFigure) invalidFigures.get(i); invalidFigures.set(i, null); fig.validate(); } } finally { invalidFigures.clear(); validating = false; } } /** * Adds the given exposed region to the update queue and then performs the update. * * @param exposed the exposed region */ public synchronized void performUpdate(Rectangle exposed) { addDirtyRegion(root, exposed); performUpdate(); } /** * Posts an {@link UpdateRequest} using {@link Display#asyncExec(Runnable)}. If work has * already been queued, a new request is not needed. */ protected void queueWork() { if (!updateQueued) { sendUpdateRequest(); updateQueued = true; } } /** * Fires the <code>UpdateRequest</code> to the current display asynchronously. * @since 3.2 */ protected void sendUpdateRequest() { Display display = Display.getCurrent(); if (display == null) { throw new SWTException(SWT.ERROR_THREAD_INVALID_ACCESS); } display.asyncExec(new UpdateRequest()); } /** * Releases the graphics object, which causes the GraphicsSource to flush. * @param graphics the graphics object */ protected void releaseGraphics(Graphics graphics) { graphics.dispose(); graphicsSource.flushGraphics(damage); } /** * Repaints the dirty regions on the update queue and calls * {@link UpdateManager#firePainting(Rectangle, Map)}, unless there are no dirty regions. */ protected void repairDamage() { Iterator keys = dirtyRegions.keySet().iterator(); Rectangle contribution; IFigure figure; IFigure walker; while (keys.hasNext()) { figure = (IFigure)keys.next(); walker = figure.getParent(); contribution = (Rectangle)dirtyRegions.get(figure); //A figure can't paint beyond its own bounds contribution.intersect(figure.getBounds()); while (!contribution.isEmpty() && walker != null) { walker.translateToParent(contribution); contribution.intersect(walker.getBounds()); walker = walker.getParent(); } if (damage == null) damage = new Rectangle(contribution); else damage.union(contribution); } if (!dirtyRegions.isEmpty()) { Map oldRegions = dirtyRegions; dirtyRegions = new HashMap(); firePainting(damage, oldRegions); } if (damage != null && !damage.isEmpty()) { //ystem.out.println(damage); Graphics graphics = getGraphics(damage); if (graphics != null) { root.paint(graphics); releaseGraphics(graphics); } } damage = null; } /** * Adds the given runnable and queues an update if an update is not under progress. * @param runnable the runnable */ public synchronized void runWithUpdate(Runnable runnable) { afterUpdate = new RunnableChain(runnable, afterUpdate); if (!updating) queueWork(); } /** * Sets the graphics source. * @param gs the graphics source */ public void setGraphicsSource(GraphicsSource gs) { graphicsSource = gs; } /** * Sets the root figure. * @param figure the root figure */ public void setRoot(IFigure figure) { root = figure; } /** * Validates all invalid figures on the update queue and calls * {@link UpdateManager#fireValidating()} unless there are no invalid figures. */ protected void validateFigures() { performValidation(); } }