comparison dwtx/draw2d/FigureCanvas.d @ 98:95307ad235d9

Added Draw2d code, still work in progress
author Frank Benoit <benoit@tionex.de>
date Sun, 03 Aug 2008 00:52:14 +0200
parents
children
comparison
equal deleted inserted replaced
96:b492ba44e44d 98:95307ad235d9
1 /*******************************************************************************
2 * Copyright (c) 2000, 2008 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 dwtx.draw2d.FigureCanvas;
14
15 import dwt.dwthelper.utils;
16 import dwtx.dwtxhelper.Bean;
17 static import dwtx.dwtxhelper.Collection;
18
19 import dwt.DWT;
20 import dwt.events.SelectionAdapter;
21 import dwt.events.SelectionEvent;
22 import dwt.graphics.Font;
23 import dwt.widgets.Canvas;
24 import dwt.widgets.Composite;
25 import dwt.widgets.Control;
26 import dwtx.draw2d.geometry.Dimension;
27 import dwtx.draw2d.geometry.Point;
28 import dwtx.draw2d.geometry.Rectangle;
29 import dwtx.draw2d.Viewport;
30 import dwtx.draw2d.LightweightSystem;
31 import dwtx.draw2d.IFigure;
32 import dwtx.draw2d.Border;
33 import dwtx.draw2d.RangeModel;
34 static import dwt.graphics.Point;
35 static import dwt.graphics.Rectangle;
36 import dwtx.draw2d.UpdateListener;
37 import dwtx.draw2d.ScrollPaneSolver;
38
39 /**
40 * A Canvas that contains {@link Figure Figures}.
41 *
42 * <dl>
43 * <dt><b>Required Styles (when using certain constructors):</b></dt>
44 * <dd>V_SCROLL, H_SCROLL, NO_REDRAW_RESIZE</dd>
45 * <dt><b>Optional Styles:</b></dt>
46 * <dd>DOUBLE_BUFFERED, RIGHT_TO_LEFT, LEFT_TO_RIGHT, NO_BACKGROUND, BORDER</dd>
47 * </dl>
48 * <p>
49 * Note: Only one of the styles RIGHT_TO_LEFT, LEFT_TO_RIGHT may be specified.
50 * </p>
51 */
52 public class FigureCanvas
53 : Canvas
54 {
55
56 private static final int ACCEPTED_STYLES =
57 DWT.RIGHT_TO_LEFT | DWT.LEFT_TO_RIGHT
58 | DWT.V_SCROLL | DWT.H_SCROLL
59 | DWT.NO_BACKGROUND
60 | DWT.NO_REDRAW_RESIZE
61 | DWT.DOUBLE_BUFFERED
62 | DWT.BORDER;
63
64 /**
65 * The default styles are mixed in when certain constructors are used. This
66 * constant is a bitwise OR of the following DWT style constants:
67 * <UL>
68 * <LI>{@link DWT#NO_REDRAW_RESIZE}</LI>
69 * <LI>{@link DWT#NO_BACKGROUND}</LI>
70 * <LI>{@link DWT#V_SCROLL}</LI>
71 * <LI>{@link DWT#H_SCROLL}</LI>
72 * </UL>
73 */
74 static const int DEFAULT_STYLES =
75 DWT.NO_REDRAW_RESIZE
76 | DWT.NO_BACKGROUND
77 | DWT.V_SCROLL
78 | DWT.H_SCROLL;
79
80 private static const int REQUIRED_STYLES =
81 DWT.NO_REDRAW_RESIZE
82 | DWT.V_SCROLL
83 | DWT.H_SCROLL;
84
85 /** Never show scrollbar */
86 public static int NEVER = 0;
87 /** Automatically show scrollbar when needed */
88 public static int AUTOMATIC = 1;
89 /** Always show scrollbar */
90 public static int ALWAYS = 2;
91
92 private int vBarVisibility;
93 private int hBarVisibility;
94 private Viewport viewport;
95 private Font font;
96 private int hBarOffset;
97 private int vBarOffset;
98
99 private PropertyChangeListener horizontalChangeListener;
100 private PropertyChangeListener verticalChangeListener;
101 private const LightweightSystem lws;
102
103 /**
104 * Creates a new FigureCanvas with the given parent and the {@link #DEFAULT_STYLES}.
105 *
106 * @param parent the parent
107 */
108 public this(Composite parent) {
109 this(parent, DWT.DOUBLE_BUFFERED, new LightweightSystem());
110 }
111
112 /**
113 * Constructor which applies the default styles plus any optional styles indicated.
114 * @param parent the parent composite
115 * @param style see the class javadoc for optional styles
116 * @since 3.1
117 */
118 public this(Composite parent, int style) {
119 this(parent, style, new LightweightSystem());
120 }
121
122 /**
123 * Constructor which uses the given styles verbatim. Certain styles must be used
124 * with this class. Refer to the class javadoc for more details.
125 * @param style see the class javadoc for <b>required</b> and optional styles
126 * @param parent the parent composite
127 * @since 3.4
128 */
129 public this(int style, Composite parent) {
130 this(style, parent, new LightweightSystem());
131 }
132
133 /**
134 * Constructs a new FigureCanvas with the given parent and LightweightSystem, using
135 * the {@link #DEFAULT_STYLES}.
136 * @param parent the parent
137 * @param lws the LightweightSystem
138 */
139 public this(Composite parent, LightweightSystem lws) {
140 this(parent, DWT.DOUBLE_BUFFERED, lws);
141 }
142
143 /**
144 * Constructor taking a lightweight system and DWT style, which is used verbatim.
145 * Certain styles must be used with this class. Refer to the class javadoc for
146 * more details.
147 * @param style see the class javadoc for <b>required</b> and optional styles
148 * @param parent the parent composite
149 * @param lws the LightweightSystem
150 * @since 3.4
151 */
152 public this(int style, Composite parent, LightweightSystem lws) {
153 this.lws = lws;
154 vBarVisibility = AUTOMATIC;
155 hBarVisibility = AUTOMATIC;
156
157 horizontalChangeListener = new class() PropertyChangeListener {
158 public void propertyChange(PropertyChangeEvent event) {
159 RangeModel model = getViewport().getHorizontalRangeModel();
160 hBarOffset = Math.max(0, -model.getMinimum());
161 getHorizontalBar().setValues(
162 model.getValue() + hBarOffset,
163 model.getMinimum() + hBarOffset,
164 model.getMaximum() + hBarOffset,
165 model.getExtent(),
166 Math.max(1, model.getExtent() / 20),
167 Math.max(1, model.getExtent() * 3 / 4));
168 }
169 };
170
171 verticalChangeListener = new class() PropertyChangeListener {
172 public void propertyChange(PropertyChangeEvent event) {
173 RangeModel model = getViewport().getVerticalRangeModel();
174 vBarOffset = Math.max(0, -model.getMinimum());
175 getVerticalBar().setValues(
176 model.getValue() + vBarOffset,
177 model.getMinimum() + vBarOffset,
178 model.getMaximum() + vBarOffset,
179 model.getExtent(),
180 Math.max(1, model.getExtent() / 20),
181 Math.max(1, model.getExtent() * 3 / 4));
182 }
183 };
184 super(parent, checkStyle(style));
185 getHorizontalBar().setVisible(false);
186 getVerticalBar().setVisible(false);
187 lws.setControl(this);
188 hook();
189 }
190
191 /**
192 * Constructor
193 * @param parent the parent composite
194 * @param style look at class javadoc for valid styles
195 * @param lws the lightweight system
196 * @since 3.1
197 */
198 public this(Composite parent, int style, LightweightSystem lws) {
199 this(style | DEFAULT_STYLES, parent, lws);
200 }
201
202 private static int checkStyle(int style) {
203 if ((style & REQUIRED_STYLES) !is REQUIRED_STYLES)
204 throw new IllegalArgumentException("Required style missing on FigureCanvas"); //$NON-NLS-1$
205 if ((style & ~ACCEPTED_STYLES) !is 0)
206 throw new IllegalArgumentException("Invalid style being set on FigureCanvas"); //$NON-NLS-1$
207 return style;
208 }
209
210 /**
211 * @see dwt.widgets.Composite#computeSize(int, int, bool)
212 */
213 public dwt.graphics.Point.Point computeSize(int wHint, int hHint, bool changed) {
214 // TODO not accounting for scrollbars and trim
215 Dimension size = getLightweightSystem().getRootFigure().getPreferredSize(wHint, hHint);
216 size.union_(new Dimension(wHint, hHint));
217 return new dwt.graphics.Point.Point(size.width, size.height);
218 }
219
220 /**
221 * @return the contents of the {@link Viewport}.
222 */
223 public IFigure getContents() {
224 return getViewport().getContents();
225 }
226
227 /**
228 * @see dwt.widgets.Control#getFont()
229 */
230 public Font getFont() {
231 if (font is null)
232 font = super.getFont();
233 return font;
234 }
235
236 /**
237 * @return the horizontal scrollbar visibility.
238 */
239 public int getHorizontalScrollBarVisibility() {
240 return hBarVisibility;
241 }
242
243 /**
244 * @return the LightweightSystem
245 */
246 public LightweightSystem getLightweightSystem() {
247 return lws;
248 }
249
250 /**
251 * @return the vertical scrollbar visibility.
252 */
253 public int getVerticalScrollBarVisibility() {
254 return vBarVisibility;
255 }
256
257 /**
258 * Returns the Viewport. If it's <code>null</code>, a new one is created.
259 * @return the viewport
260 */
261 public Viewport getViewport() {
262 if (viewport is null)
263 setViewport(new Viewport(true));
264 return viewport;
265 }
266
267 /**
268 * Adds listeners for scrolling.
269 */
270 private void hook() {
271 getLightweightSystem().getUpdateManager().addUpdateListener(new class() UpdateListener {
272 public void notifyPainting(Rectangle damage, dwtx.dwtxhelper.Collection.Map dirtyRegions) { }
273 public void notifyValidating() {
274 if (!isDisposed())
275 layoutViewport();
276 }
277 });
278
279 getHorizontalBar().addSelectionListener(new class() SelectionAdapter {
280 public void widgetSelected(SelectionEvent event) {
281 scrollToX(getHorizontalBar().getSelection() - hBarOffset);
282 }
283 });
284
285 getVerticalBar().addSelectionListener(new class() SelectionAdapter {
286 public void widgetSelected(SelectionEvent event) {
287 scrollToY(getVerticalBar().getSelection() - vBarOffset);
288 }
289 });
290 }
291
292 private void hookViewport() {
293 getViewport()
294 .getHorizontalRangeModel()
295 .addPropertyChangeListener(horizontalChangeListener);
296 getViewport()
297 .getVerticalRangeModel()
298 .addPropertyChangeListener(verticalChangeListener);
299 }
300
301 private void unhookViewport() {
302 getViewport()
303 .getHorizontalRangeModel()
304 .removePropertyChangeListener(horizontalChangeListener);
305 getViewport()
306 .getVerticalRangeModel()
307 .removePropertyChangeListener(verticalChangeListener);
308 }
309
310 private void layoutViewport() {
311 ScrollPaneSolver.Result result;
312 result = ScrollPaneSolver.solve((new Rectangle(getBounds())).setLocation(0, 0),
313 getViewport(),
314 getHorizontalScrollBarVisibility(),
315 getVerticalScrollBarVisibility(),
316 computeTrim(0, 0, 0, 0).width,
317 computeTrim(0, 0, 0, 0).height);
318 getLightweightSystem().setIgnoreResize(true);
319 try {
320 if (getHorizontalBar().getVisible() !is result.showH)
321 getHorizontalBar().setVisible(result.showH);
322 if (getVerticalBar().getVisible() !is result.showV)
323 getVerticalBar().setVisible(result.showV);
324 Rectangle r = new Rectangle(getClientArea());
325 r.setLocation(0, 0);
326 getLightweightSystem().getRootFigure().setBounds(r);
327 } finally {
328 getLightweightSystem().setIgnoreResize(false);
329 }
330 }
331
332 /**
333 * Scrolls in an animated way to the new x and y location.
334 * @param x the x coordinate to scroll to
335 * @param y the y coordinate to scroll to
336 */
337 public void scrollSmoothTo(int x, int y) {
338 // Ensure newHOffset and newVOffset are within the appropriate ranges
339 x = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), x);
340 y = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), y);
341
342 int oldX = getViewport().getViewLocation().x;
343 int oldY = getViewport().getViewLocation().y;
344 int dx = x - oldX;
345 int dy = y - oldY;
346
347 if (dx is 0 && dy is 0)
348 return; //Nothing to do.
349
350 Dimension viewingArea = getViewport().getClientArea().getSize();
351
352 int minFrames = 3;
353 int maxFrames = 6;
354 if (dx is 0 || dy is 0) {
355 minFrames = 6;
356 maxFrames = 13;
357 }
358 int frames = (Math.abs(dx) + Math.abs(dy)) / 15;
359 frames = Math.max(frames, minFrames);
360 frames = Math.min(frames, maxFrames);
361
362 int stepX = Math.min((dx / frames), (viewingArea.width / 3));
363 int stepY = Math.min((dy / frames), (viewingArea.height / 3));
364
365 for (int i = 1; i < frames; i++) {
366 scrollTo(oldX + i * stepX, oldY + i * stepY);
367 getViewport().getUpdateManager().performUpdate();
368 }
369 scrollTo(x, y);
370 }
371
372 /**
373 * Scrolls the contents to the new x and y location. If this scroll operation only
374 * consists of a vertical or horizontal scroll, a call will be made to
375 * {@link #scrollToY(int)} or {@link #scrollToX(int)}, respectively, to increase
376 * performance.
377 *
378 * @param x the x coordinate to scroll to
379 * @param y the y coordinate to scroll to
380 */
381 public void scrollTo(int x, int y) {
382 x = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), x);
383 y = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), y);
384 if (x is getViewport().getViewLocation().x)
385 scrollToY(y);
386 else if (y is getViewport().getViewLocation().y)
387 scrollToX(x);
388 else
389 getViewport().setViewLocation(x, y);
390 }
391 /**
392 * Scrolls the contents horizontally so that they are offset by <code>hOffset</code>.
393 *
394 * @param hOffset the new horizontal offset
395 */
396 public void scrollToX(int hOffset) {
397 hOffset = verifyScrollBarOffset(getViewport().getHorizontalRangeModel(), hOffset);
398 int hOffsetOld = getViewport().getViewLocation().x;
399 if (hOffset is hOffsetOld)
400 return;
401 int dx = -hOffset + hOffsetOld;
402
403 Rectangle clientArea = getViewport().getBounds().getCropped(getViewport().getInsets());
404 Rectangle blit = clientArea.getResized(-Math.abs(dx), 0);
405 Rectangle expose = clientArea.getCopy();
406 Point dest = clientArea.getTopLeft();
407 expose.width = Math.abs(dx);
408 if (dx < 0) { //Moving left?
409 blit.translate(-dx, 0); //Move blit area to the right
410 expose.x = dest.x + blit.width;
411 } else //Moving right
412 dest.x += dx; //Move expose area to the right
413
414 // fix for bug 41111
415 Control[] children = getChildren();
416 bool[] manualMove = new bool[children.length];
417 for (int i = 0; i < children.length; i++) {
418 dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds();
419 manualMove[i] = blit.width <= 0 || bounds.x > blit.x + blit.width
420 || bounds.y > blit.y + blit.height || bounds.x + bounds.width < blit.x
421 || bounds.y + bounds.height < blit.y;
422 }
423 scroll(dest.x, dest.y, blit.x, blit.y, blit.width, blit.height, true);
424 for (int i = 0; i < children.length; i++) {
425 if (children[i].isDisposed ())
426 continue;
427 dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds();
428 if (manualMove[i])
429 children[i].setBounds(bounds.x + dx, bounds.y, bounds.width, bounds.height);
430 }
431
432 getViewport().setIgnoreScroll(true);
433 getViewport().setHorizontalLocation(hOffset);
434 getViewport().setIgnoreScroll(false);
435 redraw(expose.x, expose.y, expose.width, expose.height, true);
436 }
437
438 /**
439 * Scrolls the contents vertically so that they are offset by <code>vOffset</code>.
440 *
441 * @param vOffset the new vertical offset
442 */
443 public void scrollToY(int vOffset) {
444 vOffset = verifyScrollBarOffset(getViewport().getVerticalRangeModel(), vOffset);
445 int vOffsetOld = getViewport().getViewLocation().y;
446 if (vOffset is vOffsetOld)
447 return;
448 int dy = -vOffset + vOffsetOld;
449
450 Rectangle clientArea = getViewport().getBounds().getCropped(getViewport().getInsets());
451 Rectangle blit = clientArea.getResized(0, -Math.abs(dy));
452 Rectangle expose = clientArea.getCopy();
453 Point dest = clientArea.getTopLeft();
454 expose.height = Math.abs(dy);
455 if (dy < 0) { //Moving up?
456 blit.translate(0, -dy); //Move blit area down
457 expose.y = dest.y + blit.height; //Move expose area down
458 } else //Moving down
459 dest.y += dy;
460
461 // fix for bug 41111
462 Control[] children = getChildren();
463 bool[] manualMove = new bool[children.length];
464 for (int i = 0; i < children.length; i++) {
465 dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds();
466 manualMove[i] = blit.height <= 0 || bounds.x > blit.x + blit.width
467 || bounds.y > blit.y + blit.height || bounds.x + bounds.width < blit.x
468 || bounds.y + bounds.height < blit.y;
469 }
470 scroll(dest.x, dest.y, blit.x, blit.y, blit.width, blit.height, true);
471 for (int i = 0; i < children.length; i++) {
472 if (children[i].isDisposed ())
473 continue;
474 dwt.graphics.Rectangle.Rectangle bounds = children[i].getBounds();
475 if (manualMove[i])
476 children[i].setBounds(bounds.x, bounds.y + dy, bounds.width, bounds.height);
477 }
478
479 getViewport().setIgnoreScroll(true);
480 getViewport().setVerticalLocation(vOffset);
481 getViewport().setIgnoreScroll(false);
482 redraw(expose.x, expose.y, expose.width, expose.height, true);
483 }
484
485 /**
486 * Sets the given border on the LightweightSystem's root figure.
487 *
488 * @param border The new border
489 */
490 public void setBorder(Border border) {
491 getLightweightSystem().getRootFigure().setBorder(border);
492 }
493
494 /**
495 * Sets the contents of the {@link Viewport}.
496 *
497 * @param figure the new contents
498 */
499 public void setContents(IFigure figure) {
500 getViewport().setContents(figure);
501 }
502
503 /**
504 * @see dwt.widgets.Control#setFont(dwt.graphics.Font)
505 */
506 public void setFont(Font font) {
507 this.font = font;
508 super.setFont(font);
509 }
510
511 /**
512 * Sets the horizontal scrollbar visibility. Possible values are {@link #AUTOMATIC},
513 * {@link #ALWAYS}, and {@link #NEVER}.
514 *
515 * @param v the new visibility
516 */
517 public void setHorizontalScrollBarVisibility(int v) {
518 hBarVisibility = v;
519 }
520
521 /**
522 * Sets both the horizontal and vertical scrollbar visibility to the given value.
523 * Possible values are {@link #AUTOMATIC}, {@link #ALWAYS}, and {@link #NEVER}.
524 * @param both the new visibility
525 */
526 public void setScrollBarVisibility(int both) {
527 setHorizontalScrollBarVisibility(both);
528 setVerticalScrollBarVisibility(both);
529 }
530
531 /**
532 * Sets the vertical scrollbar visibility. Possible values are {@link #AUTOMATIC},
533 * {@link #ALWAYS}, and {@link #NEVER}.
534 *
535 * @param v the new visibility
536 */
537 public void setVerticalScrollBarVisibility(int v) {
538 vBarVisibility = v;
539 }
540
541 /**
542 * Sets the Viewport. The given Viewport must use "fake" scrolling. That is, it must be
543 * constructed using <code>new Viewport(true)</code>.
544 *
545 * @param vp the new viewport
546 */
547 public void setViewport(Viewport vp) {
548 if (viewport !is null)
549 unhookViewport();
550 viewport = vp;
551 lws.setContents(viewport);
552 hookViewport();
553 }
554
555 private int verifyScrollBarOffset(RangeModel model, int value) {
556 value = Math.max(model.getMinimum(), value);
557 return Math.min(model.getMaximum() - model.getExtent(), value);
558 }
559
560 }