Project

General

Profile

Download (8.61 KB) Statistics
| Branch: | Tag: | Revision:
1
/*******************************************************************************
2
 * Copyright (c) 2000, 2010 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.draw2d;
12

    
13
import java.util.ArrayList;
14
import java.util.HashMap;
15
import java.util.Iterator;
16
import java.util.List;
17
import java.util.Map;
18

    
19
import org.eclipse.swt.SWTException;
20
import org.eclipse.swt.graphics.GC;
21
import org.eclipse.swt.widgets.Display;
22

    
23
import org.eclipse.draw2d.geometry.Rectangle;
24
import org.eclipse.draw2d.rap.swt.SWT;
25

    
26
/**
27
 * An UpdateManager that asynchronously updates the affected figures.
28
 */
29
public class DeferredUpdateManager extends UpdateManager {
30

    
31
	/**
32
	 * Calls {@link DeferredUpdateManager#performUpdate()}.
33
	 */
34
	protected class UpdateRequest implements Runnable {
35

    
36
		public UpdateRequest() {
37
			super();
38
		}
39

    
40
		/**
41
		 * Calls {@link DeferredUpdateManager#performUpdate()}.
42
		 */
43
		public void run() {
44
			performUpdate();
45
		}
46
	}
47

    
48
	private Rectangle damage;
49
	private Map dirtyRegions = new HashMap();
50

    
51
	private GraphicsSource graphicsSource;
52
	private List invalidFigures = new ArrayList();
53
	private IFigure root;
54
	private boolean updateQueued;
55

    
56
	private boolean updating;
57
	private boolean validating;
58
	private RunnableChain afterUpdate;
59

    
60
	private static class RunnableChain {
61
		RunnableChain next;
62
		Runnable run;
63

    
64
		RunnableChain(Runnable run, RunnableChain next) {
65
			this.run = run;
66
			this.next = next;
67
		}
68

    
69
		void run() {
70
			if (next != null)
71
				next.run();
72
			run.run();
73
		}
74
	}
75

    
76
	/**
77
	 * Empty constructor.
78
	 */
79
	public DeferredUpdateManager() {
80
	}
81

    
82
	/**
83
	 * Constructs a new DererredUpdateManager with the given GraphicsSource.
84
	 * 
85
	 * @param gs
86
	 *            the graphics source
87
	 */
88
	public DeferredUpdateManager(GraphicsSource gs) {
89
		setGraphicsSource(gs);
90
	}
91

    
92
	/**
93
	 * Adds a dirty region (defined by the rectangle <i>x, y, w, h</i>) to the
94
	 * update queue. If the figure isn't visible or either the width or height
95
	 * are 0, the method returns without queueing the dirty region.
96
	 * 
97
	 * @param figure
98
	 *            the figure that contains the dirty region
99
	 * @param x
100
	 *            the x coordinate of the dirty region
101
	 * @param y
102
	 *            the y coordinate of the dirty region
103
	 * @param w
104
	 *            the width of the dirty region
105
	 * @param h
106
	 *            the height of the dirty region
107
	 */
108
	public synchronized void addDirtyRegion(IFigure figure, int x, int y,
109
			int w, int h) {
110
		if (w == 0 || h == 0 || !figure.isShowing())
111
			return;
112

    
113
		Rectangle rect = (Rectangle) dirtyRegions.get(figure);
114
		if (rect == null) {
115
			rect = new Rectangle(x, y, w, h);
116
			dirtyRegions.put(figure, rect);
117
		} else
118
			rect.union(x, y, w, h);
119

    
120
		queueWork();
121
	}
122

    
123
	/**
124
	 * Adds the given figure to the update queue. Invalid figures will be
125
	 * validated before the damaged regions are repainted.
126
	 * 
127
	 * @param f
128
	 *            the invalid figure
129
	 */
130
	public synchronized void addInvalidFigure(IFigure f) {
131
		if (invalidFigures.contains(f))
132
			return;
133
		queueWork();
134
		invalidFigures.add(f);
135
	}
136

    
137
	/**
138
	 * Returns a Graphics object for the given region.
139
	 * 
140
	 * @param region
141
	 *            the region to be repainted
142
	 * @return the Graphics object
143
	 */
144
	protected Graphics getGraphics(Rectangle region) {
145
		if (graphicsSource == null)
146
			return null;
147
		return graphicsSource.getGraphics(region);
148
	}
149

    
150
	void paint(GC gc) {
151
		if (!validating) {
152
			SWTGraphics graphics = new SWTGraphics(gc);
153
			if (!updating) {
154
				/**
155
				 * If a paint occurs not as part of an update, we should notify
156
				 * that the region is being painted. Otherwise, notification
157
				 * already occurs in repairDamage().
158
				 */
159
				Rectangle rect = graphics.getClip(new Rectangle());
160
				HashMap map = new HashMap();
161
				map.put(root, rect);
162
				firePainting(rect, map);
163
			}
164
			performValidation();
165
			root.paint(graphics);
166
			graphics.dispose();
167
		} else {
168
			/*
169
			 * If figures are being validated then we can simply add a dirty
170
			 * region here and update will repaint this region with other dirty
171
			 * regions when it gets to painting. We can't paint if we're not
172
			 * sure that all figures are valid.
173
			 */
174
			addDirtyRegion(root, new Rectangle(gc.getClipping()));
175
		}
176
	}
177

    
178
	/**
179
	 * Performs the update. Validates the invalid figures and then repaints the
180
	 * dirty regions.
181
	 * 
182
	 * @see #validateFigures()
183
	 * @see #repairDamage()
184
	 */
185
	public synchronized void performUpdate() {
186
		if (isDisposed() || updating)
187
			return;
188
		updating = true;
189
		try {
190
			performValidation();
191
			updateQueued = false;
192
			repairDamage();
193
			if (afterUpdate != null) {
194
				RunnableChain chain = afterUpdate;
195
				afterUpdate = null;
196
				chain.run(); // chain may queue additional Runnable.
197
				if (afterUpdate != null)
198
					queueWork();
199
			}
200
		} finally {
201
			updating = false;
202
		}
203
	}
204

    
205
	/**
206
	 * @see UpdateManager#performValidation()
207
	 */
208
	public void performValidation() {
209
		if (invalidFigures.isEmpty() || validating)
210
			return;
211
		try {
212
			IFigure fig;
213
			validating = true;
214
			fireValidating();
215
			for (int i = 0; i < invalidFigures.size(); i++) {
216
				fig = (IFigure) invalidFigures.get(i);
217
				invalidFigures.set(i, null);
218
				fig.validate();
219
			}
220
		} finally {
221
			invalidFigures.clear();
222
			validating = false;
223
		}
224
	}
225

    
226
	/**
227
	 * Adds the given exposed region to the update queue and then performs the
228
	 * update.
229
	 * 
230
	 * @param exposed
231
	 *            the exposed region
232
	 */
233
	public synchronized void performUpdate(Rectangle exposed) {
234
		addDirtyRegion(root, exposed);
235
		performUpdate();
236
	}
237

    
238
	/**
239
	 * Posts an {@link UpdateRequest} using {@link Display#asyncExec(Runnable)}.
240
	 * If work has already been queued, a new request is not needed.
241
	 */
242
	protected void queueWork() {
243
		if (!updateQueued) {
244
			sendUpdateRequest();
245
			updateQueued = true;
246
		}
247
	}
248

    
249
	/**
250
	 * Fires the <code>UpdateRequest</code> to the current display
251
	 * asynchronously.
252
	 * 
253
	 * @since 3.2
254
	 */
255
	protected void sendUpdateRequest() {
256
		Display display = Display.getCurrent();
257
		if (display == null) {
258
			throw new SWTException(SWT.ERROR_THREAD_INVALID_ACCESS);
259
		}
260
		display.asyncExec(new UpdateRequest());
261
	}
262

    
263
	/**
264
	 * Releases the graphics object, which causes the GraphicsSource to flush.
265
	 * 
266
	 * @param graphics
267
	 *            the graphics object
268
	 */
269
	protected void releaseGraphics(Graphics graphics) {
270
		graphics.dispose();
271
		graphicsSource.flushGraphics(damage);
272
	}
273

    
274
	/**
275
	 * Repaints the dirty regions on the update queue and calls
276
	 * {@link UpdateManager#firePainting(Rectangle, Map)}, unless there are no
277
	 * dirty regions.
278
	 */
279
	protected void repairDamage() {
280
		Iterator keys = dirtyRegions.keySet().iterator();
281
		Rectangle contribution;
282
		IFigure figure;
283
		IFigure walker;
284

    
285
		while (keys.hasNext()) {
286
			figure = (IFigure) keys.next();
287
			walker = figure.getParent();
288
			contribution = (Rectangle) dirtyRegions.get(figure);
289
			// A figure can't paint beyond its own bounds
290
			contribution.intersect(figure.getBounds());
291
			while (!contribution.isEmpty() && walker != null) {
292
				walker.translateToParent(contribution);
293
				contribution.intersect(walker.getBounds());
294
				walker = walker.getParent();
295
			}
296
			if (damage == null)
297
				damage = new Rectangle(contribution);
298
			else
299
				damage.union(contribution);
300
		}
301

    
302
		if (!dirtyRegions.isEmpty()) {
303
			Map oldRegions = dirtyRegions;
304
			dirtyRegions = new HashMap();
305
			firePainting(damage, oldRegions);
306
		}
307

    
308
		if (damage != null && !damage.isEmpty()) {
309
			// ystem.out.println(damage);
310
			Graphics graphics = getGraphics(damage);
311
			if (graphics != null) {
312
				root.paint(graphics);
313
				releaseGraphics(graphics);
314
			}
315
		}
316
		damage = null;
317
	}
318

    
319
	/**
320
	 * Adds the given runnable and queues an update if an update is not under
321
	 * progress.
322
	 * 
323
	 * @param runnable
324
	 *            the runnable
325
	 */
326
	public synchronized void runWithUpdate(Runnable runnable) {
327
		afterUpdate = new RunnableChain(runnable, afterUpdate);
328
		if (!updating)
329
			queueWork();
330
	}
331

    
332
	/**
333
	 * Sets the graphics source.
334
	 * 
335
	 * @param gs
336
	 *            the graphics source
337
	 */
338
	public void setGraphicsSource(GraphicsSource gs) {
339
		graphicsSource = gs;
340
	}
341

    
342
	/**
343
	 * Sets the root figure.
344
	 * 
345
	 * @param figure
346
	 *            the root figure
347
	 */
348
	public void setRoot(IFigure figure) {
349
		root = figure;
350
	}
351

    
352
	/**
353
	 * Validates all invalid figures on the update queue and calls
354
	 * {@link UpdateManager#fireValidating()} unless there are no invalid
355
	 * figures.
356
	 */
357
	protected void validateFigures() {
358
		performValidation();
359
	}
360

    
361
}
(52-52/171)