comparison org.eclipse.draw2d/src/org/eclipse/draw2d/parts/Thumbnail.d @ 12:bc29606a740c

Added dwt-addons in original directory structure of eclipse.org
author Frank Benoit <benoit@tionex.de>
date Sat, 14 Mar 2009 18:23:29 +0100
parents
children dbfb303e8fb0
comparison
equal deleted inserted replaced
11:43904fec5dca 12:bc29606a740c
1 /*******************************************************************************
2 * Copyright (c) 2000, 2005 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 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module org.eclipse.draw2d.parts.Thumbnail;
14
15 import java.lang.all;
16 import org.eclipse.swt.dwthelper.Runnable;
17
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.graphics.Color;
20 import org.eclipse.swt.graphics.GC;
21 import org.eclipse.swt.graphics.Image;
22 static import org.eclipse.draw2d.geometry.Point;
23 import org.eclipse.swt.widgets.Display;
24 import org.eclipse.draw2d.Figure;
25 import org.eclipse.draw2d.Graphics;
26 import org.eclipse.draw2d.IFigure;
27 import org.eclipse.draw2d.SWTGraphics;
28 import org.eclipse.draw2d.ScaledGraphics;
29 import org.eclipse.draw2d.UpdateListener;
30 import org.eclipse.draw2d.geometry.Dimension;
31 import org.eclipse.draw2d.geometry.Rectangle;
32
33 /**
34 * A Thumbnail is a Figure that displays an image of its source Figure at a
35 * smaller size. The Thumbnail will maintain the aspect ratio of the source
36 * Figure.
37 *
38 * @author Eric Bordeau
39 */
40 public class Thumbnail
41 : Figure
42 , UpdateListener
43 {
44 alias Figure.getPreferredSize getPreferredSize;
45
46
47 /**
48 * This updates the Thumbnail by breaking the thumbnail {@link Image} into
49 * several tiles and updating each tile individually.
50 */
51 class ThumbnailUpdater : Runnable {
52 static final int MAX_BUFFER_SIZE = 256;
53 private int currentHTile, currentVTile;
54 private int hTiles, vTiles;
55 private bool isActive_ = true;
56
57 private bool isRunning_ = false;
58 private GC thumbnailGC;
59 private ScaledGraphics thumbnailGraphics;
60 private Dimension tileSize;
61
62 /**
63 * Stops the updater and disposes of any resources.
64 */
65 public void deactivate() {
66 setActive(false);
67 stop();
68 if (thumbnailImage !is null) {
69 thumbnailImage.dispose();
70 thumbnailImage = null;
71 thumbnailImageSize = null;
72 }
73 }
74
75 /**
76 * Returns the current horizontal tile index.
77 * @return current horizontal tile index.
78 */
79 protected int getCurrentHTile() {
80 return currentHTile;
81 }
82
83 /**
84 * Returns the current vertical tile index.
85 * @return current vertical tile index.
86 */
87 protected int getCurrentVTile() {
88 return currentVTile;
89 }
90
91 /**
92 * Returns <code>true</code> if this ThumbnailUpdater is active. An inactive
93 * updater has disposed of its {@link Image}. The updater may be active and
94 * not currently running.
95 * @return <code>true</code> if this ThumbnailUpdater is active
96 */
97 public bool isActive() {
98 return isActive_;
99 }
100
101 /**
102 * Returns <code>true</code> if this is currently running and updating at
103 * least one tile on the thumbnail {@link Image}.
104 * @return <code>true</code> if this is currently running
105 */
106 public bool isRunning() {
107 return isRunning_;
108 }
109
110 /**
111 * Resets the number of vertical and horizontal tiles, as well as the tile
112 * size and current tile index.
113 */
114 public void resetTileValues() {
115 hTiles = cast(int)Math.ceil(cast(float)getSourceRectangle().width
116 / cast(float)MAX_BUFFER_SIZE);
117 vTiles = cast(int)Math.ceil(cast(float)getSourceRectangle().height
118 / cast(float)MAX_BUFFER_SIZE);
119
120 tileSize = new Dimension(cast(int)Math.ceil(cast(float)getSourceRectangle().width
121 / cast(float)hTiles),
122 cast(int)Math.ceil(cast(float)getSourceRectangle().height
123 / cast(float)vTiles));
124
125 currentHTile = 0;
126 currentVTile = 0;
127 }
128
129 /**
130 * Restarts the updater.
131 */
132 public void restart() {
133 stop();
134 start();
135 }
136
137 /**
138 * Updates the current tile on the Thumbnail. An area of the source Figure
139 * is painted to an {@link Image}. That Image is then drawn on the
140 * Thumbnail. Scaling of the source Image is done inside
141 * {@link GC#drawImage(Image, int, int, int, int, int, int, int, int)} since
142 * the source and target sizes are different. The current tile indexes are
143 * incremented and if more updating is necesary, this {@link Runnable} is
144 * called again in a {@link Display#timerExec(int, Runnable)}. If no more
145 * updating is required, {@link #stop()} is called.
146 */
147 public void run() {
148 if (!isActive() || !isRunning())
149 return;
150 int v = getCurrentVTile();
151 int sy1 = v * tileSize.height;
152 int sy2 = Math.min((v + 1) * tileSize.height, getSourceRectangle().height);
153
154 int h = getCurrentHTile();
155 int sx1 = h * tileSize.width;
156 int sx2 = Math.min((h + 1) * tileSize.width, getSourceRectangle().width);
157 org.eclipse.draw2d.geometry.Point.Point p = getSourceRectangle().getLocation();
158
159 Rectangle rect = new Rectangle(sx1 + p.x, sy1 + p.y, sx2 - sx1, sy2 - sy1);
160 thumbnailGraphics.pushState();
161 thumbnailGraphics.setClip(rect);
162 thumbnailGraphics.fillRectangle(rect);
163 sourceFigure.paint(thumbnailGraphics);
164 thumbnailGraphics.popState();
165
166 if (getCurrentHTile() < (hTiles - 1))
167 setCurrentHTile(getCurrentHTile() + 1);
168 else {
169 setCurrentHTile(0);
170 if (getCurrentVTile() < (vTiles - 1))
171 setCurrentVTile(getCurrentVTile() + 1);
172 else
173 setCurrentVTile(0);
174 }
175
176 if (getCurrentHTile() !is 0 || getCurrentVTile() !is 0)
177 Display.getCurrent().asyncExec(this);
178 else if (isDirty()) {
179 setDirty(false);
180 Display.getCurrent().asyncExec(this);
181 repaint();
182 } else {
183 stop();
184 repaint();
185 }
186 }
187
188 /**
189 * Sets the active flag.
190 * @param value The active value
191 */
192 public void setActive(bool value) {
193 isActive_ = value;
194 }
195
196 /**
197 * Sets the current horizontal tile index.
198 * @param count current horizontal tile index
199 */
200 protected void setCurrentHTile(int count) {
201 currentHTile = count;
202 }
203
204 /**
205 * Sets the current vertical tile index.
206 * @param count current vertical tile index
207 */
208 protected void setCurrentVTile(int count) {
209 currentVTile = count;
210 }
211
212 /**
213 * Starts this updater. This method initializes all the necessary resources
214 * and puts this {@link Runnable} on the asynch queue. If this updater is
215 * not active or is already running, this method just returns.
216 */
217 public void start() {
218 if (!isActive() || isRunning())
219 return;
220
221 isRunning_ = true;
222 setDirty(false);
223 resetTileValues();
224
225 if (!targetSize.opEquals(thumbnailImageSize)) {
226 resetThumbnailImage();
227 }
228
229 if (targetSize.isEmpty())
230 return;
231
232 thumbnailGC = new GC(thumbnailImage,
233 sourceFigure.isMirrored() ? SWT.RIGHT_TO_LEFT : SWT.NONE);
234 thumbnailGraphics = new ScaledGraphics(new SWTGraphics(thumbnailGC));
235 thumbnailGraphics.scale(getScaleX());
236 thumbnailGraphics.translate(getSourceRectangle().getLocation().negate());
237
238 Color color = sourceFigure.getForegroundColor();
239 if (color !is null)
240 thumbnailGraphics.setForegroundColor(color);
241 color = sourceFigure.getBackgroundColor();
242 if (color !is null)
243 thumbnailGraphics.setBackgroundColor(color);
244 thumbnailGraphics.setFont(sourceFigure.getFont());
245
246 setScales(targetSize.width / cast(float)getSourceRectangle().width,
247 targetSize.height / cast(float)getSourceRectangle().height);
248
249 Display.getCurrent().asyncExec(this);
250 }
251
252 /**
253 *
254 * @since 3.2
255 */
256 private void resetThumbnailImage() {
257 if (thumbnailImage !is null)
258 thumbnailImage.dispose();
259
260 if (!targetSize.isEmpty()) {
261 thumbnailImage = new Image(Display.getDefault(),
262 targetSize.width,
263 targetSize.height);
264 thumbnailImageSize = new Dimension(targetSize);
265 }
266 else {
267 thumbnailImage = null;
268 thumbnailImageSize = new Dimension(0, 0);
269 }
270 }
271
272 /**
273 * Stops this updater. Also disposes of resources (except the thumbnail
274 * image which is still needed for painting).
275 */
276 public void stop() {
277 isRunning_ = false;
278 if (thumbnailGC !is null) {
279 thumbnailGC.dispose();
280 thumbnailGC = null;
281 }
282 if (thumbnailGraphics !is null) {
283 thumbnailGraphics.dispose();
284 thumbnailGraphics = null;
285 }
286 // Don't dispose of the thumbnail image since it is needed to paint the
287 // figure when the source is not dirty (i.e. showing/hiding the dock).
288 }
289 }
290 private bool isDirty_;
291 private float scaleX;
292 private float scaleY;
293
294 private IFigure sourceFigure;
295 Dimension targetSize;
296 private Image thumbnailImage;
297 private Dimension thumbnailImageSize;
298 private ThumbnailUpdater updater;
299
300 /**
301 * Creates a new Thumbnail. The source Figure must be set separately if you
302 * use this constructor.
303 */
304 public this() {
305 super();
306 targetSize = new Dimension(0, 0);
307 updater = new ThumbnailUpdater();
308 }
309
310 /**
311 * Creates a new Thumbnail with the given IFigure as its source figure.
312 * @param fig The source figure
313 */
314 public this(IFigure fig) {
315 this();
316 setSource(fig);
317 }
318
319 private Dimension adjustToAspectRatio(Dimension size, bool adjustToMaxDimension) {
320 Dimension sourceSize = getSourceRectangle().getSize();
321 Dimension borderSize = new Dimension(getInsets().getWidth(), getInsets().getHeight());
322 size.expand(borderSize.getNegated());
323 int width, height;
324 if (adjustToMaxDimension) {
325 width = Math.max(size.width, cast(int)(size.height * sourceSize.width
326 / cast(float)sourceSize.height + 0.5));
327 height = Math.max(size.height, cast(int)(size.width * sourceSize.height
328 / cast(float)sourceSize.width + 0.5));
329 } else {
330 width = Math.min(size.width, cast(int)(size.height * sourceSize.width
331 / cast(float)sourceSize.height + 0.5));
332 height = Math.min(size.height, cast(int)(size.width * sourceSize.height
333 / cast(float)sourceSize.width + 0.5));
334 }
335 size.width = width;
336 size.height = height;
337 return size.expand(borderSize);
338 }
339
340 /**
341 * Deactivates this Thumbnail.
342 */
343 public void deactivate() {
344 sourceFigure.getUpdateManager().removeUpdateListener(this);
345 updater.deactivate();
346 }
347
348 /**
349 * Returns the preferred size of this Thumbnail. The preferred size will be
350 * calculated in a way that maintains the source Figure's aspect ratio.
351 *
352 * @param wHint The width hint
353 * @param hHint The height hint
354 * @return The preferred size
355 */
356 public Dimension getPreferredSize(int wHint, int hHint) {
357 if (prefSize is null)
358 return adjustToAspectRatio(getBounds().getSize(), false);
359
360 Dimension preferredSize = adjustToAspectRatio(prefSize.getCopy(), true);
361
362 if (maxSize is null)
363 return preferredSize;
364
365 Dimension maximumSize = adjustToAspectRatio(maxSize.getCopy(), true);
366 if (preferredSize.contains(maximumSize))
367 return maximumSize;
368 else
369 return preferredSize;
370 }
371
372 /**
373 * Returns the scale factor on the X-axis.
374 * @return X scale
375 */
376 protected float getScaleX() {
377 return scaleX;
378 }
379
380 /**
381 * Returns the scale factor on the Y-axis.
382 * @return Y scale
383 */
384 protected float getScaleY() {
385 return scaleY;
386 }
387
388 /**
389 * Returns the source figure being used to generate a thumbnail.
390 * @return the source figure
391 */
392 protected IFigure getSource() {
393 return sourceFigure;
394 }
395
396 /**
397 * Returns the rectangular region relative to the source figure which will be the basis of
398 * the thumbnail. The value may be returned by reference and should not be modified by
399 * the caller.
400 * @since 3.1
401 * @return the region of the source figure being used for the thumbnail
402 */
403 protected Rectangle getSourceRectangle() {
404 return sourceFigure.getBounds();
405 }
406
407 /**
408 * Returns the scaled Image of the source Figure. If the Image needs to be
409 * updated, the ThumbnailUpdater will notified.
410 *
411 * @return The thumbnail image
412 */
413 protected Image getThumbnailImage() {
414 Dimension oldSize = targetSize;
415 targetSize = getPreferredSize();
416 targetSize.expand((new Dimension(getInsets().getWidth(),
417 getInsets().getHeight())).negate());
418 setScales(targetSize.width / cast(float)getSourceRectangle().width,
419 targetSize.height / cast(float)getSourceRectangle().height);
420 if ((isDirty()) && !updater.isRunning())
421 updater.start();
422 else if (oldSize !is null && !targetSize.opEquals(oldSize)) {
423 revalidate();
424 updater.restart();
425 }
426
427 return thumbnailImage;
428 }
429
430 /**
431 * Returns <code>true</code> if the source figure has changed.
432 * @return <code>true</code> if the source figure has changed
433 */
434 protected bool isDirty() {
435 return isDirty_;
436 }
437
438 /**
439 * @see org.eclipse.draw2d.UpdateListener#notifyPainting(Rectangle, Map)
440 */
441 public void notifyPainting(Rectangle damage, Map dirtyRegions) {
442 Iterator dirtyFigures = dirtyRegions.keySet().iterator();
443 while (dirtyFigures.hasNext()) {
444 IFigure current = cast(IFigure)dirtyFigures.next();
445 while (current !is null) {
446 if (current is getSource()) {
447 setDirty(true);
448 repaint();
449 return;
450 }
451 current = current.getParent();
452 }
453 }
454 }
455
456 /**
457 * @see org.eclipse.draw2d.UpdateListener#notifyValidating()
458 */
459 public void notifyValidating() {
460 // setDirty(true);
461 // revalidate();
462 }
463
464 /**
465 * @see org.eclipse.draw2d.Figure#paintFigure(Graphics)
466 */
467 protected void paintFigure(Graphics graphics) {
468 Image thumbnail = getThumbnailImage();
469 if (thumbnail is null)
470 return;
471 graphics.drawImage(thumbnail, getClientArea().getLocation());
472 }
473
474 /**
475 * Sets the dirty flag.
476 * @param value The dirty value
477 */
478 public void setDirty(bool value) {
479 isDirty_ = value;
480 }
481
482 /**
483 * Sets the X and Y scales for the Thumbnail. These scales represent the ratio
484 * between the source figure and the Thumbnail.
485 * @param x The X scale
486 * @param y The Y scale
487 */
488 protected void setScales(float x, float y) {
489 scaleX = x;
490 scaleY = y;
491 }
492
493 /**
494 * Sets the source Figure. Also sets the scales and creates the necessary
495 * update manager.
496 * @param fig The source figure
497 */
498 public void setSource(IFigure fig) {
499 if (sourceFigure is fig)
500 return;
501 if (sourceFigure !is null)
502 sourceFigure.getUpdateManager().removeUpdateListener(this);
503 sourceFigure = fig;
504 if (sourceFigure !is null) {
505 setScales(cast(float)getSize().width / cast(float)getSourceRectangle().width,
506 cast(float)getSize().height / cast(float)getSourceRectangle().height);
507 sourceFigure.getUpdateManager().addUpdateListener(this);
508 repaint();
509 }
510 }
511
512 }