Mercurial > projects > dwt2
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 } |