comparison dwtx/draw2d/Figure.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 1082a0fc2bb8
comparison
equal deleted inserted replaced
96:b492ba44e44d 98:95307ad235d9
1 /*******************************************************************************
2 * Copyright (c) 2000, 2007 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.Figure;
14
15 import dwt.dwthelper.utils;
16 import dwtx.dwtxhelper.Bean;
17 import dwtx.dwtxhelper.Collection;
18
19 import dwt.graphics.Color;
20 import dwt.graphics.Cursor;
21 import dwt.graphics.Font;
22 import dwtx.draw2d.geometry.Dimension;
23 import dwtx.draw2d.geometry.Insets;
24 import dwtx.draw2d.geometry.Point;
25 import dwtx.draw2d.geometry.Rectangle;
26 import dwtx.draw2d.geometry.Translatable;
27 import dwtx.draw2d.IFigure;
28 import dwtx.draw2d.LayoutManager;
29 import dwtx.draw2d.EventListenerList;
30 import dwtx.draw2d.Border;
31 import dwtx.draw2d.AncestorHelper;
32 import dwtx.draw2d.CoordinateListener;
33 import dwtx.draw2d.FigureListener;
34 import dwtx.draw2d.FocusListener;
35 import dwtx.draw2d.KeyListener;
36 import dwtx.draw2d.MouseListener;
37 import dwtx.draw2d.MouseMotionListener;
38 import dwtx.draw2d.TreeSearch;
39 import dwtx.draw2d.LayoutListener;
40 import dwtx.draw2d.UpdateManager;
41 import dwtx.draw2d.Graphics;
42 import dwtx.draw2d.AncestorListener;
43 import dwtx.draw2d.MouseEvent;
44 import dwtx.draw2d.EventDispatcher;
45 import dwtx.draw2d.FocusEvent;
46 import dwtx.draw2d.KeyEvent;
47 import dwtx.draw2d.AncestorHelper;
48 import dwtx.draw2d.ExclusionSearch;
49 import dwtx.draw2d.AbstractBackground;
50 import dwtx.draw2d.Orientable;
51 import dwtx.draw2d.GraphicsSource;
52
53 /**
54 * The base implementation for graphical figures.
55 */
56 public class Figure
57 : IFigure
58 {
59
60 private static /+const+/ Rectangle PRIVATE_RECT;
61 private static /+const+/ Point PRIVATE_POINT;
62 private static const int
63 FLAG_VALID = 1,
64 FLAG_OPAQUE = (1 << 1),
65 FLAG_VISIBLE = (1 << 2),
66 FLAG_FOCUSABLE = (1 << 3),
67 FLAG_ENABLED = (1 << 4),
68 FLAG_FOCUS_TRAVERSABLE = (1 << 5);
69
70 static const int
71 FLAG_REALIZED = 1 << 31;
72
73 /**
74 * The largest flag defined in this class. If subclasses define flags, they should
75 * declare them as larger than this value and redefine MAX_FLAG to be their largest flag
76 * value.
77 * <P>
78 * This constant is evaluated at runtime and will not be inlined by the compiler.
79 */
80 protected static const int MAX_FLAG = FLAG_FOCUS_TRAVERSABLE;
81
82 /**
83 * The rectangular area that this Figure occupies.
84 */
85 protected Rectangle bounds;
86
87 private LayoutManager layoutManager;
88
89 /**
90 * The flags for this Figure.
91 */
92 protected int flags = FLAG_VISIBLE | FLAG_ENABLED;
93
94 private IFigure parent;
95 private Cursor cursor;
96
97 private PropertyChangeSupport propertyListeners;
98 private EventListenerList eventListeners;
99
100 private List children;
101
102 /**
103 * This Figure's preferred size.
104 */
105 protected Dimension prefSize;
106
107 /**
108 * This Figure's minimum size.
109 */
110 protected Dimension minSize;
111
112 /**
113 * This Figure's maximum size.
114 */
115 protected Dimension maxSize;
116
117 /**
118 * @deprecated access using {@link #getLocalFont()}
119 */
120 protected Font font;
121
122 /**
123 * @deprecated access using {@link #getLocalBackgroundColor()}.
124 */
125 protected Color bgColor;
126
127 /**
128 * @deprecated access using {@link #getLocalForegroundColor()}.
129 */
130 protected Color fgColor;
131
132 /**
133 * @deprecated access using {@link #getBorder()}
134 */
135 protected Border border;
136
137 /**
138 * @deprecated access using {@link #getToolTip()}
139 */
140 protected IFigure toolTip;
141
142 private AncestorHelper ancestorHelper;
143
144 private static void static_this(){
145 if( PRIVATE_RECT is null ){
146 synchronized( Figure.classinfo ){
147 if( PRIVATE_RECT is null ){
148 PRIVATE_RECT = new Rectangle();
149 PRIVATE_POINT = new Point();
150 }
151 }
152 }
153 }
154
155 private void instanceInit(){
156 static_this();
157 bounds = new Rectangle(0, 0, 0, 0);
158 eventListeners = new EventListenerList();
159 children = Collections.EMPTY_LIST;
160 }
161
162 this(){
163 instanceInit();
164 }
165 /**
166 * Calls {@link #add(IFigure, Object, int)} with -1 as the index.
167 * @see IFigure#add(IFigure, Object)
168 */
169 public final void add(IFigure figure, Object constraint) {
170 add(figure, constraint, -1);
171 }
172
173 /**
174 * @see IFigure#add(IFigure, Object, int)
175 */
176 public void add(IFigure figure, Object constraint, int index) {
177 if (children is Collections.EMPTY_LIST)
178 children = new ArrayList(2);
179 if (index < -1 || index > children.size())
180 throw new IndexOutOfBoundsException("Index does not exist"); //$NON-NLS-1$
181
182 //Check for Cycle in hierarchy
183 for (IFigure f = this; f !is null; f = f.getParent())
184 if (figure is f)
185 throw new IllegalArgumentException(
186 "Figure being added introduces cycle"); //$NON-NLS-1$
187
188 //Detach the child from previous parent
189 if (figure.getParent() !is null)
190 figure.getParent().remove(figure);
191
192 if (index is -1)
193 children.add(cast(Object) figure);
194 else
195 children.add(index, cast(Object)figure);
196 figure.setParent(this);
197
198 if (layoutManager !is null)
199 layoutManager.setConstraint(figure, constraint);
200
201 revalidate();
202
203 if (getFlag(FLAG_REALIZED))
204 figure.addNotify();
205 figure.repaint();
206 }
207
208 /**
209 * Calls {@link #add(IFigure, Object, int)} with <code>null</code> as the constraint and
210 * -1 as the index.
211 * @see IFigure#add(IFigure)
212 */
213 public final void add(IFigure figure) {
214 add(figure, null, -1);
215 }
216
217 /**
218 * Calls {@link #add(IFigure, Object, int)} with <code>null</code> as the constraint.
219 * @see IFigure#add(IFigure, int)
220 */
221 public final void add(IFigure figure, int index) {
222 add(figure, null, index);
223 }
224 /**
225 * @see IFigure#addAncestorListener(AncestorListener)
226 */
227 public void addAncestorListener(AncestorListener ancestorListener) {
228 if (ancestorHelper is null)
229 ancestorHelper = new AncestorHelper(this);
230 ancestorHelper.addAncestorListener(ancestorListener);
231 }
232
233 /**
234 * @see IFigure#addCoordinateListener(CoordinateListener)
235 */
236 public void addCoordinateListener(CoordinateListener listener) {
237 eventListeners.addListener(CoordinateListener.classinfo, cast(Object)listener);
238 }
239
240 /**
241 * @see IFigure#addFigureListener(FigureListener)
242 */
243 public void addFigureListener(FigureListener listener) {
244 eventListeners.addListener(FigureListener.classinfo, cast(Object)listener);
245 }
246
247 /**
248 * @see IFigure#addFocusListener(FocusListener)
249 */
250 public void addFocusListener(FocusListener listener) {
251 eventListeners.addListener(FocusListener.classinfo, cast(Object)listener);
252 }
253
254 /**
255 * @see IFigure#addKeyListener(KeyListener)
256 */
257 public void addKeyListener(KeyListener listener) {
258 eventListeners.addListener(KeyListener.classinfo, cast(Object)listener);
259 }
260
261 /**
262 * Appends the given layout listener to the list of layout listeners.
263 * @since 3.1
264 * @param listener the listener being added
265 */
266 public void addLayoutListener(LayoutListener listener) {
267 if (auto n = cast(LayoutNotifier)layoutManager ) {
268 LayoutNotifier notifier = n;
269 notifier.listeners.add(cast(Object)listener);
270 } else
271 layoutManager = new LayoutNotifier(layoutManager, listener);
272 }
273
274 /**
275 * Adds a listener of type <i>clazz</i> to this Figure's list of event listeners.
276 * @param clazz The listener type
277 * @param listener The listener
278 */
279 protected void addListener(ClassInfo clazz, Object listener) {
280 eventListeners.addListener(clazz, cast(Object)listener);
281 }
282
283 /**
284 * @see IFigure#addMouseListener(MouseListener)
285 */
286 public void addMouseListener(MouseListener listener) {
287 eventListeners.addListener(MouseListener.classinfo, cast(Object)listener);
288 }
289
290 /**
291 * @see IFigure#addMouseMotionListener(MouseMotionListener)
292 */
293 public void addMouseMotionListener(MouseMotionListener listener) {
294 eventListeners.addListener(MouseMotionListener.classinfo, cast(Object)listener);
295 }
296
297 /**
298 * Called after the receiver's parent has been set and it has been added to its parent.
299 *
300 * @since 2.0
301 */
302 public void addNotify() {
303 if (getFlag(FLAG_REALIZED))
304 throw new RuntimeException("addNotify() should not be called multiple times"); //$NON-NLS-1$
305 setFlag(FLAG_REALIZED, true);
306 for (int i = 0; i < children.size(); i++)
307 (cast(IFigure)children.get(i)).addNotify();
308 }
309
310 /**
311 * @see IFigure#addPropertyChangeListener(String,
312 * PropertyChangeListener)
313 */
314 public void addPropertyChangeListener(String property, PropertyChangeListener listener) {
315 if (propertyListeners is null)
316 propertyListeners = new PropertyChangeSupport(this);
317 propertyListeners.addPropertyChangeListener(property, listener);
318 }
319
320 /**
321 * @see IFigure#addPropertyChangeListener(PropertyChangeListener)
322 */
323 public void addPropertyChangeListener(PropertyChangeListener listener) {
324 if (propertyListeners is null)
325 propertyListeners = new PropertyChangeSupport(this);
326 propertyListeners.addPropertyChangeListener(listener);
327 }
328
329 /**
330 * This method is final. Override {@link #containsPoint(int, int)} if needed.
331 * @see IFigure#containsPoint(Point)
332 * @since 2.0
333 */
334 public final bool containsPoint(Point p) {
335 return containsPoint(p.x, p.y);
336 }
337
338 /**
339 * @see IFigure#containsPoint(int, int)
340 */
341 public bool containsPoint(int x, int y) {
342 return getBounds().contains(x, y);
343 }
344
345 /**
346 * @see IFigure#erase()
347 */
348 public void erase() {
349 if (getParent() is null || !isVisible())
350 return;
351
352 Rectangle r = new Rectangle(getBounds());
353 getParent().translateToParent(r);
354 getParent().repaint(r.x, r.y, r.width, r.height);
355 }
356
357 /**
358 * Returns a descendant of this Figure such that the Figure returned contains the point
359 * (x, y), and is accepted by the given TreeSearch. Returns <code>null</code> if none
360 * found.
361 * @param x The X coordinate
362 * @param y The Y coordinate
363 * @param search the TreeSearch
364 * @return The descendant Figure at (x,y)
365 */
366 protected IFigure findDescendantAtExcluding(int x, int y, TreeSearch search) {
367 PRIVATE_POINT.setLocation(x, y);
368 translateFromParent(PRIVATE_POINT);
369 if (!getClientArea(Rectangle.SINGLETON).contains(PRIVATE_POINT))
370 return null;
371
372 x = PRIVATE_POINT.x;
373 y = PRIVATE_POINT.y;
374 IFigure fig;
375 for (int i = children.size(); i > 0;) {
376 i--;
377 fig = cast(IFigure)children.get(i);
378 if (fig.isVisible()) {
379 fig = fig.findFigureAt(x, y, search);
380 if (fig !is null)
381 return fig;
382 }
383 }
384 //No descendants were found
385 return null;
386 }
387
388 /**
389 * @see IFigure#findFigureAt(Point)
390 */
391 public final IFigure findFigureAt(Point pt) {
392 return findFigureAtExcluding(pt.x, pt.y, Collections.EMPTY_LIST);
393 }
394
395 /**
396 * @see IFigure#findFigureAt(int, int)
397 */
398 public final IFigure findFigureAt(int x, int y) {
399 return findFigureAt(x, y, IdentitySearch.INSTANCE);
400 }
401
402 /**
403 * @see IFigure#findFigureAt(int, int, TreeSearch)
404 */
405 public IFigure findFigureAt(int x, int y, TreeSearch search) {
406 if (!containsPoint(x, y))
407 return null;
408 if (search.prune(this))
409 return null;
410 IFigure child = findDescendantAtExcluding(x, y, search);
411 if (child !is null)
412 return child;
413 if (search.accept(this))
414 return this;
415 return null;
416 }
417
418 /**
419 * @see IFigure#findFigureAtExcluding(int, int, Collection)
420 */
421 public final IFigure findFigureAtExcluding(int x, int y, Collection c) {
422 return findFigureAt(x, y, new ExclusionSearch(c));
423 }
424
425 /**
426 * Returns the deepest descendant for which {@link #isMouseEventTarget()} returns
427 * <code>true</code> or <code>null</code> if none found. The Parameters <i>x</i> and
428 * <i>y</i> are absolute locations. Any Graphics transformations applied by this Figure to
429 * its children during {@link #paintChildren(Graphics)} (thus causing the children to
430 * appear transformed to the user) should be applied inversely to the points <i>x</i> and
431 * <i>y</i> when called on the children.
432 *
433 * @param x The X coordinate
434 * @param y The Y coordinate
435 * @return The deepest descendant for which isMouseEventTarget() returns true
436 */
437 public IFigure findMouseEventTargetAt(int x, int y) {
438 if (!containsPoint(x, y))
439 return null;
440 IFigure f = findMouseEventTargetInDescendantsAt(x, y);
441 if (f !is null)
442 return f;
443 if (isMouseEventTarget())
444 return this;
445 return null;
446 }
447
448 /**
449 * Searches this Figure's children for the deepest descendant for which
450 * {@link #isMouseEventTarget()} returns <code>true</code> and returns that descendant or
451 * <code>null</code> if none found.
452 * @see #findMouseEventTargetAt(int, int)
453 * @param x The X coordinate
454 * @param y The Y coordinate
455 * @return The deepest descendant for which isMouseEventTarget() returns true
456 */
457 protected IFigure findMouseEventTargetInDescendantsAt(int x, int y) {
458 PRIVATE_POINT.setLocation(x, y);
459 translateFromParent(PRIVATE_POINT);
460
461 if (!getClientArea(Rectangle.SINGLETON).contains(PRIVATE_POINT))
462 return null;
463
464 IFigure fig;
465 for (int i = children.size(); i > 0;) {
466 i--;
467 fig = cast(IFigure)children.get(i);
468 if (fig.isVisible() && fig.isEnabled()) {
469 if (fig.containsPoint(PRIVATE_POINT.x, PRIVATE_POINT.y)) {
470 fig = fig.findMouseEventTargetAt(PRIVATE_POINT.x, PRIVATE_POINT.y);
471 return fig;
472 }
473 }
474 }
475 return null;
476 }
477
478 /**
479 * Notifies to all {@link CoordinateListener}s that this figure's local coordinate system
480 * has changed in a way which affects the absolute bounds of figures contained within.
481 *
482 * @since 3.1
483 */
484 protected void fireCoordinateSystemChanged() {
485 if (!eventListeners.containsListener(CoordinateListener.classinfo))
486 return;
487 Iterator figureListeners = eventListeners.getListeners(CoordinateListener.classinfo);
488 while (figureListeners.hasNext())
489 (cast(CoordinateListener)figureListeners.next()).
490 coordinateSystemChanged(this);
491 }
492
493 /**
494 * Notifies to all {@link FigureListener}s that this figure has moved. Moved means
495 * that the bounds have changed in some way, location and/or size.
496 * @since 3.1
497 */
498 protected void fireFigureMoved() {
499 if (!eventListeners.containsListener(FigureListener.classinfo))
500 return;
501 Iterator figureListeners = eventListeners.getListeners(FigureListener.classinfo);
502 while (figureListeners.hasNext())
503 (cast(FigureListener)figureListeners.next()).
504 figureMoved(this);
505 }
506
507 /**
508 * Fires both figuremoved and coordinate system changed. This method exists for
509 * compatibility. Some listeners which used to listen for figureMoved now listen for
510 * coordinates changed. So to be sure that those new listeners are notified, any client
511 * code which used called this method will also result in notification of coordinate
512 * changes.
513 * @since 2.0
514 * @deprecated call fireFigureMoved() or fireCoordinateSystemChanged() as appropriate
515 */
516 protected void fireMoved() {
517 fireFigureMoved();
518 fireCoordinateSystemChanged();
519 }
520
521 /**
522 * Notifies any {@link PropertyChangeListener PropertyChangeListeners} listening to this
523 * Figure that the bool property with id <i>property</i> has changed.
524 * @param property The id of the property that changed
525 * @param old The old value of the changed property
526 * @param current The current value of the changed property
527 * @since 2.0
528 */
529 protected void firePropertyChange(String property, bool old, bool current) {
530 if (propertyListeners is null)
531 return;
532 propertyListeners.firePropertyChange(property, old, current);
533 }
534
535 /**
536 * Notifies any {@link PropertyChangeListener PropertyChangeListeners} listening to this
537 * figure that the Object property with id <i>property</i> has changed.
538 * @param property The id of the property that changed
539 * @param old The old value of the changed property
540 * @param current The current value of the changed property
541 * @since 2.0
542 */
543 protected void firePropertyChange(String property, Object old, Object current) {
544 if (propertyListeners is null)
545 return;
546 propertyListeners.firePropertyChange(property, old, current);
547 }
548
549 /**
550 * Notifies any {@link PropertyChangeListener PropertyChangeListeners} listening to this
551 * figure that the integer property with id <code>property</code> has changed.
552 * @param property The id of the property that changed
553 * @param old The old value of the changed property
554 * @param current The current value of the changed property
555 * @since 2.0
556 */
557 protected void firePropertyChange(String property, int old, int current) {
558 if (propertyListeners is null)
559 return;
560 propertyListeners.firePropertyChange(property, old, current);
561 }
562
563 /**
564 * Returns this Figure's background color. If this Figure's background color is
565 * <code>null</code> and its parent is not <code>null</code>, the background color is
566 * inherited from the parent.
567 * @see IFigure#getBackgroundColor()
568 */
569 public Color getBackgroundColor() {
570 if (bgColor is null && getParent() !is null)
571 return getParent().getBackgroundColor();
572 return bgColor;
573 }
574
575 /**
576 * @see IFigure#getBorder()
577 */
578 public Border getBorder() {
579 return border;
580 }
581
582 /**
583 * Returns the smallest rectangle completely enclosing the figure. Implementors may return
584 * the Rectangle by reference. For this reason, callers of this method must not modify the
585 * returned Rectangle.
586 * @return The bounds of this Figure
587 */
588 public Rectangle getBounds() {
589 return bounds;
590 }
591
592 /**
593 * @see IFigure#getChildren()
594 */
595 public List getChildren() {
596 return children;
597 }
598
599 /**
600 * @see IFigure#getClientArea(Rectangle)
601 */
602 public Rectangle getClientArea(Rectangle rect) {
603 rect.setBounds(getBounds());
604 rect.crop(getInsets());
605 if (useLocalCoordinates())
606 rect.setLocation(0, 0);
607 return rect;
608 }
609
610 /**
611 * @see IFigure#getClientArea()
612 */
613 public final Rectangle getClientArea() {
614 return getClientArea(new Rectangle());
615 }
616
617 /**
618 * @see IFigure#getCursor()
619 */
620 public Cursor getCursor() {
621 if (cursor is null && getParent() !is null)
622 return getParent().getCursor();
623 return cursor;
624 }
625
626 /**
627 * Returns the value of the given flag.
628 * @param flag The flag to get
629 * @return The value of the given flag
630 */
631 protected bool getFlag(int flag) {
632 return (flags & flag) !is 0;
633 }
634
635 /**
636 * @see IFigure#getFont()
637 */
638 public Font getFont() {
639 if (font !is null)
640 return font;
641 if (getParent() !is null)
642 return getParent().getFont();
643 return null;
644 }
645
646 /**
647 * @see IFigure#getForegroundColor()
648 */
649 public Color getForegroundColor() {
650 if (fgColor is null && getParent() !is null)
651 return getParent().getForegroundColor();
652 return fgColor;
653 }
654
655 /**
656 * Returns the border's Insets if the border is set. Otherwise returns NO_INSETS, an
657 * instance of Insets with all 0s. Returns Insets by reference. DO NOT Modify returned
658 * value. Cannot return null.
659 * @return This Figure's Insets
660 */
661 public Insets getInsets() {
662 if (getBorder() !is null)
663 return getBorder().getInsets(this);
664 return IFigure_NO_INSETS;
665 }
666
667 /**
668 * @see IFigure#getLayoutManager()
669 */
670 public LayoutManager getLayoutManager() {
671 if (auto n = cast(LayoutNotifier)layoutManager )
672 return n.realLayout;
673 return layoutManager;
674 }
675
676 /**
677 * Returns an Iterator over the listeners of type <i>clazz</i> that are listening to
678 * this Figure. If there are no listeners of type <i>clazz</i>, an empty iterator is
679 * returned.
680 * @param clazz The type of listeners to get
681 * @return An Iterator over the requested listeners
682 * @since 2.0
683 */
684 protected Iterator getListeners(ClassInfo clazz) {
685 if (eventListeners is null)
686 return Collections.EMPTY_LIST.iterator();
687 return eventListeners.getListeners(clazz);
688 }
689
690 /**
691 * Returns <code>null</code> or the local background Color of this Figure. Does not
692 * inherit this Color from the parent.
693 * @return bgColor <code>null</code> or the local background Color
694 */
695 public Color getLocalBackgroundColor() {
696 return bgColor;
697 }
698
699 /**
700 * Returns <code>null</code> or the local font setting for this figure. Does not return
701 * values inherited from the parent figure.
702 * @return <code>null</code> or the local font
703 * @since 3.1
704 */
705 protected Font getLocalFont() {
706 return font;
707 }
708
709 /**
710 * Returns <code>null</code> or the local foreground Color of this Figure. Does not
711 * inherit this Color from the parent.
712 * @return fgColor <code>null</code> or the local foreground Color
713 */
714 public Color getLocalForegroundColor() {
715 return fgColor;
716 }
717
718 /**
719 * Returns the top-left corner of this Figure's bounds.
720 * @return The top-left corner of this Figure's bounds
721 * @since 2.0
722 */
723 public final Point getLocation() {
724 return getBounds().getLocation();
725 }
726
727 /**
728 * @see IFigure#getMaximumSize()
729 */
730 public Dimension getMaximumSize() {
731 if (maxSize !is null)
732 return maxSize;
733 return IFigure_MAX_DIMENSION;
734 }
735
736 /**
737 * @see IFigure#getMinimumSize()
738 */
739 public final Dimension getMinimumSize() {
740 return getMinimumSize(-1, -1);
741 }
742
743 /**
744 * @see IFigure#getMinimumSize(int, int)
745 */
746 public Dimension getMinimumSize(int wHint, int hHint) {
747 if (minSize !is null)
748 return minSize;
749 if (getLayoutManager() !is null) {
750 Dimension d = getLayoutManager().getMinimumSize(this, wHint, hHint);
751 if (d !is null)
752 return d;
753 }
754 return getPreferredSize(wHint, hHint);
755 }
756
757 /**
758 * @see IFigure#getParent()
759 */
760 public IFigure getParent() {
761 return parent;
762 }
763
764 /**
765 * @see IFigure#getPreferredSize()
766 */
767 public final Dimension getPreferredSize() {
768 return getPreferredSize(-1, -1);
769 }
770
771 /**
772 * @see IFigure#getPreferredSize(int, int)
773 */
774 public Dimension getPreferredSize(int wHint, int hHint) {
775 if (prefSize !is null)
776 return prefSize;
777 if (getLayoutManager() !is null) {
778 Dimension d = getLayoutManager().getPreferredSize(this, wHint, hHint);
779 if (d !is null)
780 return d;
781 }
782 return getSize();
783 }
784
785 /**
786 * @see IFigure#getSize()
787 */
788 public final Dimension getSize() {
789 return getBounds().getSize();
790 }
791
792 /**
793 * @see IFigure#getToolTip()
794 */
795 public IFigure getToolTip() {
796 return toolTip;
797 }
798
799 /**
800 * @see IFigure#getUpdateManager()
801 */
802 public UpdateManager getUpdateManager() {
803 if (getParent() !is null)
804 return getParent().getUpdateManager();
805 // Only happens when the figure has not been realized
806 return NO_MANAGER;
807 }
808
809 /**
810 * @see IFigure#handleFocusGained(FocusEvent)
811 */
812 public void handleFocusGained(FocusEvent event) {
813 Iterator iter = eventListeners.getListeners(FocusListener.classinfo);
814 while (iter.hasNext())
815 (cast(FocusListener)iter.next()).
816 focusGained(event);
817 }
818
819 /**
820 * @see IFigure#handleFocusLost(FocusEvent)
821 */
822 public void handleFocusLost(FocusEvent event) {
823 Iterator iter = eventListeners.getListeners(FocusListener.classinfo);
824 while (iter.hasNext())
825 (cast(FocusListener)iter.next()).
826 focusLost(event);
827 }
828
829 /**
830 * @see IFigure#handleKeyPressed(KeyEvent)
831 */
832 public void handleKeyPressed(KeyEvent event) {
833 Iterator iter = eventListeners.getListeners(KeyListener.classinfo);
834 while (!event.isConsumed() && iter.hasNext())
835 (cast(KeyListener)iter.next()).
836 keyPressed(event);
837 }
838
839 /**
840 * @see IFigure#handleKeyReleased(KeyEvent)
841 */
842 public void handleKeyReleased(KeyEvent event) {
843 Iterator iter = eventListeners.getListeners(KeyListener.classinfo);
844 while (!event.isConsumed() && iter.hasNext())
845 (cast(KeyListener)iter.next()).
846 keyReleased(event);
847 }
848
849 /**
850 * @see IFigure#handleMouseDoubleClicked(MouseEvent)
851 */
852 public void handleMouseDoubleClicked(MouseEvent event) {
853 Iterator iter = eventListeners.getListeners(MouseListener.classinfo);
854 while (!event.isConsumed() && iter.hasNext())
855 (cast(MouseListener)iter.next()).
856 mouseDoubleClicked(event);
857 }
858
859 /**
860 * @see IFigure#handleMouseDragged(MouseEvent)
861 */
862 public void handleMouseDragged(MouseEvent event) {
863 Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo);
864 while (!event.isConsumed() && iter.hasNext())
865 (cast(MouseMotionListener)iter.next()).
866 mouseDragged(event);
867 }
868
869 /**
870 * @see IFigure#handleMouseEntered(MouseEvent)
871 */
872 public void handleMouseEntered(MouseEvent event) {
873 Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo);
874 while (!event.isConsumed() && iter.hasNext())
875 (cast(MouseMotionListener)iter.next()).
876 mouseEntered(event);
877 }
878
879 /**
880 * @see IFigure#handleMouseExited(MouseEvent)
881 */
882 public void handleMouseExited(MouseEvent event) {
883 Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo);
884 while (!event.isConsumed() && iter.hasNext())
885 (cast(MouseMotionListener)iter.next()).
886 mouseExited(event);
887 }
888
889 /**
890 * @see IFigure#handleMouseHover(MouseEvent)
891 */
892 public void handleMouseHover(MouseEvent event) {
893 Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo);
894 while (!event.isConsumed() && iter.hasNext())
895 (cast(MouseMotionListener)iter.next()).
896 mouseHover(event);
897 }
898
899 /**
900 * @see IFigure#handleMouseMoved(MouseEvent)
901 */
902 public void handleMouseMoved(MouseEvent event) {
903 Iterator iter = eventListeners.getListeners(MouseMotionListener.classinfo);
904 while (!event.isConsumed() && iter.hasNext())
905 (cast(MouseMotionListener)iter.next()).
906 mouseMoved(event);
907 }
908
909 /**
910 * @see IFigure#handleMousePressed(MouseEvent)
911 */
912 public void handleMousePressed(MouseEvent event) {
913 Iterator iter = eventListeners.getListeners(MouseListener.classinfo);
914 while (!event.isConsumed() && iter.hasNext())
915 (cast(MouseListener)iter.next()).
916 mousePressed(event);
917 }
918
919 /**
920 * @see IFigure#handleMouseReleased(MouseEvent)
921 */
922 public void handleMouseReleased(MouseEvent event) {
923 Iterator iter = eventListeners.getListeners(MouseListener.classinfo);
924 while (!event.isConsumed() && iter.hasNext())
925 (cast(MouseListener)iter.next()).
926 mouseReleased(event);
927 }
928
929 /**
930 * @see IFigure#hasFocus()
931 */
932 public bool hasFocus() {
933 EventDispatcher dispatcher = internalGetEventDispatcher();
934 if (dispatcher is null)
935 return false;
936 return dispatcher.getFocusOwner() is this;
937 }
938
939 /**
940 * @see IFigure#internalGetEventDispatcher()
941 */
942 public EventDispatcher internalGetEventDispatcher() {
943 if (getParent() !is null)
944 return getParent().internalGetEventDispatcher();
945 return null;
946 }
947
948 /**
949 * @see IFigure#intersects(Rectangle)
950 */
951 public bool intersects(Rectangle rect) {
952 return getBounds().intersects(rect);
953 }
954
955 /**
956 * @see IFigure#invalidate()
957 */
958 public void invalidate() {
959 if (layoutManager !is null)
960 layoutManager.invalidate();
961 setValid(false);
962 }
963
964 /**
965 * @see IFigure#invalidateTree()
966 */
967 public void invalidateTree() {
968 invalidate();
969 for (Iterator iter = children.iterator(); iter.hasNext();) {
970 IFigure child = cast(IFigure) iter.next();
971 child.invalidateTree();
972 }
973 }
974
975 /**
976 * @see IFigure#isCoordinateSystem()
977 */
978 public bool isCoordinateSystem() {
979 return useLocalCoordinates();
980 }
981
982 /**
983 * @see IFigure#isEnabled()
984 */
985 public bool isEnabled() {
986 return (flags & FLAG_ENABLED) !is 0;
987 }
988
989 /**
990 * @see IFigure#isFocusTraversable()
991 */
992 public bool isFocusTraversable() {
993 return (flags & FLAG_FOCUS_TRAVERSABLE) !is 0;
994 }
995
996 /**
997 * Returns <code>true</code> if this Figure can receive {@link MouseEvent MouseEvents}.
998 * @return <code>true</code> if this Figure can receive {@link MouseEvent MouseEvents}
999 * @since 2.0
1000 */
1001 protected bool isMouseEventTarget() {
1002 return (eventListeners.containsListener(MouseListener.classinfo)
1003 || eventListeners.containsListener(MouseMotionListener.classinfo));
1004 }
1005
1006 /**
1007 * @see dwtx.draw2d.IFigure#isMirrored()
1008 */
1009 public bool isMirrored() {
1010 if (getParent() !is null)
1011 return getParent().isMirrored();
1012 return false;
1013 }
1014
1015 /**
1016 * @see IFigure#isOpaque()
1017 */
1018 public bool isOpaque() {
1019 return (flags & FLAG_OPAQUE) !is 0;
1020 }
1021
1022 /**
1023 * @see IFigure#isRequestFocusEnabled()
1024 */
1025 public bool isRequestFocusEnabled() {
1026 return (flags & FLAG_FOCUSABLE) !is 0;
1027 }
1028
1029 /**
1030 * @see IFigure#isShowing()
1031 */
1032 public bool isShowing() {
1033 return isVisible()
1034 && (getParent() is null
1035 || getParent().isShowing());
1036 }
1037
1038 /**
1039 * Returns <code>true</code> if this Figure is valid.
1040 * @return <code>true</code> if this Figure is valid
1041 * @since 2.0
1042 */
1043 protected bool isValid() {
1044 return (flags & FLAG_VALID) !is 0;
1045 }
1046
1047 /**
1048 * Returns <code>true</code> if revalidating this Figure does not require revalidating its
1049 * parent.
1050 * @return <code>true</code> if revalidating this Figure doesn't require revalidating its
1051 * parent.
1052 * @since 2.0
1053 */
1054 protected bool isValidationRoot() {
1055 return false;
1056 }
1057
1058 /**
1059 * @see IFigure#isVisible()
1060 */
1061 public bool isVisible() {
1062 return getFlag(FLAG_VISIBLE);
1063 }
1064
1065 /**
1066 * Lays out this Figure using its {@link LayoutManager}.
1067 *
1068 * @since 2.0
1069 */
1070 protected void layout() {
1071 if (layoutManager !is null)
1072 layoutManager.layout(this);
1073 }
1074
1075 /**
1076 * Paints this Figure and its children.
1077 * @param graphics The Graphics object used for painting
1078 * @see #paintFigure(Graphics)
1079 * @see #paintClientArea(Graphics)
1080 * @see #paintBorder(Graphics)
1081 */
1082 public void paint(Graphics graphics) {
1083 if (getLocalBackgroundColor() !is null)
1084 graphics.setBackgroundColor(getLocalBackgroundColor());
1085 if (getLocalForegroundColor() !is null)
1086 graphics.setForegroundColor(getLocalForegroundColor());
1087 if (font !is null)
1088 graphics.setFont(font);
1089
1090 graphics.pushState();
1091 try {
1092 paintFigure(graphics);
1093 graphics.restoreState();
1094 paintClientArea(graphics);
1095 paintBorder(graphics);
1096 } finally {
1097 graphics.popState();
1098 }
1099 }
1100
1101 /**
1102 * Paints the border associated with this Figure, if one exists.
1103 * @param graphics The Graphics used to paint
1104 * @see Border#paint(IFigure, Graphics, Insets)
1105 * @since 2.0
1106 */
1107 protected void paintBorder(Graphics graphics) {
1108 if (getBorder() !is null)
1109 getBorder().paint(this, graphics, IFigure_NO_INSETS);
1110 }
1111
1112 /**
1113 * Paints this Figure's children. The caller must save the state of the graphics prior to
1114 * calling this method, such that <code>graphics.restoreState()</code> may be called
1115 * safely, and doing so will return the graphics to its original state when the method was
1116 * entered.
1117 * <P>
1118 * This method must leave the Graphics in its original state upon return.
1119 * @param graphics the graphics used to paint
1120 * @since 2.0
1121 */
1122 protected void paintChildren(Graphics graphics) {
1123 IFigure child;
1124
1125 Rectangle clip = Rectangle.SINGLETON;
1126 for (int i = 0; i < children.size(); i++) {
1127 child = cast(IFigure)children.get(i);
1128 if (child.isVisible() && child.intersects(graphics.getClip(clip))) {
1129 graphics.clipRect(child.getBounds());
1130 child.paint(graphics);
1131 graphics.restoreState();
1132 }
1133 }
1134 }
1135
1136 /**
1137 * Paints this Figure's client area. The client area is typically defined as the anything
1138 * inside the Figure's {@link Border} or {@link Insets}, and by default includes the
1139 * children of this Figure. On return, this method must leave the given Graphics in its
1140 * initial state.
1141 * @param graphics The Graphics used to paint
1142 * @since 2.0
1143 */
1144 protected void paintClientArea(Graphics graphics) {
1145 if (children.isEmpty())
1146 return;
1147
1148 bool optimizeClip = getBorder() is null || getBorder().isOpaque();
1149
1150 if (useLocalCoordinates()) {
1151 graphics.translate(
1152 getBounds().x + getInsets().left,
1153 getBounds().y + getInsets().top);
1154 if (!optimizeClip)
1155 graphics.clipRect(getClientArea(PRIVATE_RECT));
1156 graphics.pushState();
1157 paintChildren(graphics);
1158 graphics.popState();
1159 graphics.restoreState();
1160 } else {
1161 if (optimizeClip)
1162 paintChildren(graphics);
1163 else {
1164 graphics.clipRect(getClientArea(PRIVATE_RECT));
1165 graphics.pushState();
1166 paintChildren(graphics);
1167 graphics.popState();
1168 graphics.restoreState();
1169 }
1170 }
1171 }
1172
1173 /**
1174 * Paints this Figure's primary representation, or background. Changes made to the
1175 * graphics to the graphics current state will not affect the subsequent calls to {@link
1176 * #paintClientArea(Graphics)} and {@link #paintBorder(Graphics)}. Furthermore, it is safe
1177 * to call <code>graphics.restoreState()</code> within this method, and doing so will
1178 * restore the graphics to its original state upon entry.
1179 * @param graphics The Graphics used to paint
1180 * @since 2.0
1181 */
1182 protected void paintFigure(Graphics graphics) {
1183 if (isOpaque())
1184 graphics.fillRectangle(getBounds());
1185 if (null !is cast(AbstractBackground)getBorder() )
1186 (cast(AbstractBackground) getBorder()).paintBackground(this, graphics, IFigure_NO_INSETS);
1187 }
1188
1189 /**
1190 * Translates this Figure's bounds, without firing a move.
1191 * @param dx The amount to translate horizontally
1192 * @param dy The amount to translate vertically
1193 * @see #translate(int, int)
1194 * @since 2.0
1195 */
1196 protected void primTranslate(int dx, int dy) {
1197 bounds.x += dx;
1198 bounds.y += dy;
1199 if (useLocalCoordinates()) {
1200 fireCoordinateSystemChanged();
1201 return;
1202 }
1203 for (int i = 0; i < children.size(); i++)
1204 (cast(IFigure)children.get(i)).translate(dx, dy);
1205 }
1206
1207 /**
1208 * Removes the given child Figure from this Figure's hierarchy and revalidates this
1209 * Figure. The child Figure's {@link #removeNotify()} method is also called.
1210 * @param figure The Figure to remove
1211 */
1212 public void remove(IFigure figure) {
1213 if ((figure.getParent() !is this))
1214 throw new IllegalArgumentException(
1215 "Figure is not a child"); //$NON-NLS-1$
1216 if (getFlag(FLAG_REALIZED))
1217 figure.removeNotify();
1218 if (layoutManager !is null)
1219 layoutManager.remove(figure);
1220 // The updates in the UpdateManager *have* to be
1221 // done asynchronously, else will result in
1222 // incorrect dirty region corrections.
1223 figure.erase();
1224 figure.setParent(null);
1225 children.remove(cast(Object)figure);
1226 revalidate();
1227 }
1228
1229 /**
1230 * Removes all children from this Figure.
1231 *
1232 * @see #remove(IFigure)
1233 * @since 2.0
1234 */
1235 public void removeAll() {
1236 List list = new ArrayList(getChildren());
1237 for (int i = 0; i < list.size(); i++) {
1238 remove(cast(IFigure)list.get(i));
1239 }
1240 }
1241
1242 /**
1243 * @see IFigure#removeAncestorListener(AncestorListener)
1244 */
1245 public void removeAncestorListener(AncestorListener listener) {
1246 if (ancestorHelper !is null) {
1247 ancestorHelper.removeAncestorListener(listener);
1248 if (ancestorHelper.isEmpty()) {
1249 ancestorHelper.dispose();
1250 ancestorHelper = null;
1251 }
1252 }
1253 }
1254
1255 /**
1256 * @see IFigure#removeCoordinateListener(CoordinateListener)
1257 */
1258 public void removeCoordinateListener(CoordinateListener listener) {
1259 eventListeners.removeListener(CoordinateListener.classinfo, cast(Object)listener);
1260 }
1261
1262 /**
1263 * @see IFigure#removeFigureListener(FigureListener)
1264 */
1265 public void removeFigureListener(FigureListener listener) {
1266 eventListeners.removeListener(FigureListener.classinfo, cast(Object)listener);
1267 }
1268
1269 /**
1270 * @see IFigure#removeFocusListener(FocusListener)
1271 */
1272 public void removeFocusListener(FocusListener listener) {
1273 eventListeners.removeListener(FocusListener.classinfo, cast(Object)listener);
1274 }
1275
1276 /**
1277 * @see IFigure#removeKeyListener(KeyListener)
1278 */
1279 public void removeKeyListener(KeyListener listener) {
1280 eventListeners.removeListener(KeyListener.classinfo, cast(Object)listener);
1281 }
1282
1283 /**
1284 * Removes the first occurence of the given listener.
1285 * @since 3.1
1286 * @param listener the listener being removed
1287 */
1288 public void removeLayoutListener(LayoutListener listener) {
1289 if (auto notifier = cast(LayoutNotifier)layoutManager ) {
1290 notifier.listeners.remove(cast(Object)listener);
1291 if (notifier.listeners.isEmpty())
1292 layoutManager = notifier.realLayout;
1293 }
1294 }
1295
1296 /**
1297 * Removes <i>listener</i> of type <i>clazz</i> from this Figure's list of listeners.
1298 * @param clazz The type of listener
1299 * @param listener The listener to remove
1300 * @since 2.0
1301 */
1302 protected void removeListener(ClassInfo clazz, Object listener) {
1303 if (eventListeners is null)
1304 return;
1305 eventListeners.removeListener(clazz, listener);
1306 }
1307
1308 /**
1309 * @see IFigure#removeMouseListener(MouseListener)
1310 */
1311 public void removeMouseListener(MouseListener listener) {
1312 eventListeners.removeListener(MouseListener.classinfo, cast(Object)listener);
1313 }
1314
1315 /**
1316 * @see IFigure#removeMouseMotionListener(MouseMotionListener)
1317 */
1318 public void removeMouseMotionListener(MouseMotionListener listener) {
1319 eventListeners.removeListener(MouseMotionListener.classinfo, cast(Object)listener);
1320 }
1321
1322 /**
1323 * Called prior to this figure's removal from its parent
1324 */
1325 public void removeNotify() {
1326 for (int i = 0; i < children.size(); i++)
1327 (cast(IFigure)children.get(i)).removeNotify();
1328 if (internalGetEventDispatcher() !is null)
1329 internalGetEventDispatcher().requestRemoveFocus(this);
1330 setFlag(FLAG_REALIZED, false);
1331 }
1332
1333 /**
1334 * @see IFigure#removePropertyChangeListener(PropertyChangeListener)
1335 */
1336 public void removePropertyChangeListener(PropertyChangeListener listener) {
1337 if (propertyListeners is null) return;
1338 propertyListeners.removePropertyChangeListener(listener);
1339 }
1340
1341 /**
1342 * @see IFigure#removePropertyChangeListener(String, PropertyChangeListener)
1343 */
1344 public void removePropertyChangeListener(
1345 String property,
1346 PropertyChangeListener listener) {
1347 if (propertyListeners is null) return;
1348 propertyListeners.removePropertyChangeListener(property, listener);
1349 }
1350
1351 /**
1352 * @see IFigure#repaint(Rectangle)
1353 */
1354 public final void repaint(Rectangle rect) {
1355 repaint(rect.x, rect.y, rect.width, rect.height);
1356 }
1357
1358 /**
1359 * @see IFigure#repaint(int, int, int, int)
1360 */
1361 public void repaint(int x, int y, int w, int h) {
1362 if (isVisible())
1363 getUpdateManager().addDirtyRegion(this, x, y, w, h);
1364 }
1365
1366 /**
1367 * @see IFigure#repaint()
1368 */
1369 public void repaint() {
1370 repaint(getBounds());
1371 }
1372
1373 /**
1374 * @see IFigure#requestFocus()
1375 */
1376 public final void requestFocus() {
1377 if (!isRequestFocusEnabled() || hasFocus())
1378 return;
1379 EventDispatcher dispatcher = internalGetEventDispatcher();
1380 if (dispatcher is null)
1381 return;
1382 dispatcher.requestFocus(this);
1383 }
1384
1385 /**
1386 * @see IFigure#revalidate()
1387 */
1388 public void revalidate() {
1389 invalidate();
1390 if (getParent() is null || isValidationRoot())
1391 getUpdateManager().addInvalidFigure(this);
1392 else
1393 getParent().revalidate();
1394 }
1395
1396 /**
1397 * @see IFigure#setBackgroundColor(Color)
1398 */
1399 public void setBackgroundColor(Color bg) {
1400 bgColor = bg;
1401 repaint();
1402 }
1403
1404 /**
1405 * @see IFigure#setBorder(Border)
1406 */
1407 public void setBorder(Border border) {
1408 this.border = border;
1409 revalidate();
1410 repaint();
1411 }
1412
1413 /**
1414 * Sets the bounds of this Figure to the Rectangle <i>rect</i>. Note that <i>rect</i> is
1415 * compared to the Figure's current bounds to determine what needs to be repainted and/or
1416 * exposed and if validation is required. Since {@link #getBounds()} may return the
1417 * current bounds by reference, it is not safe to modify that Rectangle and then call
1418 * setBounds() after making modifications. The figure would assume that the bounds are
1419 * unchanged, and no layout or paint would occur. For proper behavior, always use a copy.
1420 * @param rect The new bounds
1421 * @since 2.0
1422 */
1423 public void setBounds(Rectangle rect) {
1424 int x = bounds.x,
1425 y = bounds.y;
1426
1427 bool resize = (rect.width !is bounds.width) || (rect.height !is bounds.height),
1428 translate = (rect.x !is x) || (rect.y !is y);
1429
1430 if ((resize || translate) && isVisible())
1431 erase();
1432 if (translate) {
1433 int dx = rect.x - x;
1434 int dy = rect.y - y;
1435 primTranslate(dx, dy);
1436 }
1437
1438 bounds.width = rect.width;
1439 bounds.height = rect.height;
1440
1441 if (translate || resize) {
1442 if (resize)
1443 invalidate();
1444 fireFigureMoved();
1445 repaint();
1446 }
1447 }
1448
1449 /**
1450 * Sets the direction of any {@link Orientable} children. Allowable values for
1451 * <code>dir</code> are found in {@link PositionConstants}.
1452 * @param direction The direction
1453 * @see Orientable#setDirection(int)
1454 * @since 2.0
1455 */
1456 protected void setChildrenDirection(int direction) {
1457 FigureIterator iterator = new FigureIterator(this);
1458 IFigure child;
1459 while (iterator.hasNext()) {
1460 child = iterator.nextFigure();
1461 if ( auto c = cast(Orientable)child )
1462 c.setDirection(direction);
1463 }
1464 }
1465
1466 /**
1467 * Sets all childrens' enabled property to <i>value</i>.
1468 * @param value The enable value
1469 * @see #setEnabled(bool)
1470 * @since 2.0
1471 */
1472 protected void setChildrenEnabled(bool value) {
1473 FigureIterator iterator = new FigureIterator(this);
1474 while (iterator.hasNext())
1475 iterator.nextFigure().setEnabled(value);
1476 }
1477
1478 /**
1479 * Sets the orientation of any {@link Orientable} children. Allowable values for
1480 * <i>orientation</i> are found in {@link PositionConstants}.
1481 * @param orientation The Orientation
1482 * @see Orientable#setOrientation(int)
1483 * @since 2.0
1484 */
1485 protected void setChildrenOrientation(int orientation) {
1486 FigureIterator iterator = new FigureIterator(this);
1487 IFigure child;
1488 while (iterator.hasNext()) {
1489 child = iterator.nextFigure();
1490 if (auto c = cast(Orientable)child )
1491 c.setOrientation(orientation);
1492 }
1493 }
1494
1495 /**
1496 * @see IFigure#setConstraint(IFigure, Object)
1497 */
1498 public void setConstraint(IFigure child, Object constraint) {
1499 if (child.getParent() !is this)
1500 throw new IllegalArgumentException(
1501 "Figure must be a child"); //$NON-NLS-1$
1502
1503 if (layoutManager !is null)
1504 layoutManager.setConstraint(child, constraint);
1505 revalidate();
1506 }
1507
1508 /**
1509 * @see IFigure#setCursor(Cursor)
1510 */
1511 public void setCursor(Cursor cursor) {
1512 if (this.cursor is cursor)
1513 return;
1514 this.cursor = cursor;
1515 EventDispatcher dispatcher = internalGetEventDispatcher();
1516 if (dispatcher !is null)
1517 dispatcher.updateCursor_package();
1518 }
1519
1520 /**
1521 * @see IFigure#setEnabled(bool)
1522 */
1523 public void setEnabled(bool value) {
1524 if (isEnabled() is value)
1525 return;
1526 setFlag(FLAG_ENABLED, value);
1527 }
1528
1529 /**
1530 * Sets the given flag to the given value.
1531 * @param flag The flag to set
1532 * @param value The value
1533 * @since 2.0
1534 */
1535 protected final void setFlag(int flag, bool value) {
1536 if (value)
1537 flags |= flag;
1538 else
1539 flags &= ~flag;
1540 }
1541
1542 /**
1543 * @see IFigure#setFocusTraversable(bool)
1544 */
1545 public void setFocusTraversable(bool focusTraversable) {
1546 if (isFocusTraversable() is focusTraversable)
1547 return;
1548 setFlag(FLAG_FOCUS_TRAVERSABLE, focusTraversable);
1549 }
1550
1551 /**
1552 * @see IFigure#setFont(Font)
1553 */
1554 public void setFont(Font f) {
1555 if (font !is f) {
1556 font = f;
1557 revalidate();
1558 repaint();
1559 }
1560 }
1561
1562 /**
1563 * @see IFigure#setForegroundColor(Color)
1564 */
1565 public void setForegroundColor(Color fg) {
1566 if (fgColor !is null && fgColor.opEquals(fg))
1567 return;
1568 fgColor = fg;
1569 repaint();
1570 }
1571
1572 /**
1573 * @see IFigure#setLayoutManager(LayoutManager)
1574 */
1575 public void setLayoutManager(LayoutManager manager) {
1576 if (auto n = cast(LayoutNotifier)layoutManager )
1577 n.realLayout = manager;
1578 else
1579 layoutManager = manager;
1580 revalidate();
1581 }
1582
1583 /**
1584 * @see IFigure#setLocation(Point)
1585 */
1586 public void setLocation(Point p) {
1587 if (getLocation().opEquals(p))
1588 return;
1589 Rectangle r = new Rectangle(getBounds());
1590 r.setLocation(p);
1591 setBounds(r);
1592 }
1593
1594 /**
1595 * @see IFigure#setMaximumSize(Dimension)
1596 */
1597 public void setMaximumSize(Dimension d) {
1598 if (maxSize !is null && maxSize.opEquals(d))
1599 return;
1600 maxSize = d;
1601 revalidate();
1602 }
1603
1604 /**
1605 * @see IFigure#setMinimumSize(Dimension)
1606 */
1607 public void setMinimumSize(Dimension d) {
1608 if (minSize !is null && minSize.opEquals(d))
1609 return;
1610 minSize = d;
1611 revalidate();
1612 }
1613
1614 /**
1615 * @see IFigure#setOpaque(bool)
1616 */
1617 public void setOpaque(bool opaque) {
1618 if (isOpaque() is opaque)
1619 return;
1620 setFlag(FLAG_OPAQUE, opaque);
1621 repaint();
1622 }
1623
1624 /**
1625 * @see IFigure#setParent(IFigure)
1626 */
1627 public void setParent(IFigure p) {
1628 IFigure oldParent = parent;
1629 parent = p;
1630 firePropertyChange("parent", cast(Object)oldParent, cast(Object)p);//$NON-NLS-1$
1631 }
1632
1633 /**
1634 * @see IFigure#setPreferredSize(Dimension)
1635 */
1636 public void setPreferredSize(Dimension size) {
1637 if (prefSize !is null && prefSize.opEquals(size))
1638 return;
1639 prefSize = size;
1640 revalidate();
1641 }
1642
1643 /**
1644 * Sets the preferred size of this figure.
1645 * @param w The new preferred width
1646 * @param h The new preferred height
1647 * @see #setPreferredSize(Dimension)
1648 * @since 2.0
1649 */
1650 public final void setPreferredSize(int w, int h) {
1651 setPreferredSize(new Dimension(w, h));
1652 }
1653
1654 /**
1655 * @see IFigure#setRequestFocusEnabled(bool)
1656 */
1657 public void setRequestFocusEnabled(bool requestFocusEnabled) {
1658 if (isRequestFocusEnabled() is requestFocusEnabled)
1659 return;
1660 setFlag(FLAG_FOCUSABLE, requestFocusEnabled);
1661 }
1662
1663 /**
1664 * @see IFigure#setSize(Dimension)
1665 */
1666 public final void setSize(Dimension d) {
1667 setSize(d.width, d.height);
1668 }
1669
1670 /**
1671 * @see IFigure#setSize(int, int)
1672 */
1673 public void setSize(int w, int h) {
1674 Rectangle bounds = getBounds();
1675 if (bounds.width is w && bounds.height is h)
1676 return;
1677 Rectangle r = new Rectangle(getBounds());
1678 r.setSize(w, h);
1679 setBounds(r);
1680 }
1681
1682 /**
1683 * @see IFigure#setToolTip(IFigure)
1684 */
1685 public void setToolTip(IFigure f) {
1686 if (toolTip is f)
1687 return;
1688 toolTip = f;
1689 }
1690
1691 /**
1692 * Sets this figure to be valid if <i>value</i> is <code>true</code> and invalid
1693 * otherwise.
1694 * @param value The valid value
1695 * @since 2.0
1696 */
1697 public void setValid(bool value) {
1698 setFlag(FLAG_VALID, value);
1699 }
1700
1701 /**
1702 * @see IFigure#setVisible(bool)
1703 */
1704 public void setVisible(bool visible) {
1705 bool currentVisibility = isVisible();
1706 if (visible is currentVisibility)
1707 return;
1708 if (currentVisibility)
1709 erase();
1710 setFlag(FLAG_VISIBLE, visible);
1711 if (visible)
1712 repaint();
1713 revalidate();
1714 }
1715
1716 /**
1717 * @see IFigure#translate(int, int)
1718 */
1719 public final void translate(int x, int y) {
1720 primTranslate(x, y);
1721 fireFigureMoved();
1722 }
1723
1724 /**
1725 * @see IFigure#translateFromParent(Translatable)
1726 */
1727 public void translateFromParent(Translatable t) {
1728 if (useLocalCoordinates())
1729 t.performTranslate(
1730 -getBounds().x - getInsets().left,
1731 -getBounds().y - getInsets().top);
1732 }
1733
1734 /**
1735 * @see IFigure#translateToAbsolute(Translatable)
1736 */
1737 public final void translateToAbsolute(Translatable t) {
1738 if (getParent() !is null) {
1739 getParent().translateToParent(t);
1740 getParent().translateToAbsolute(t);
1741 }
1742 }
1743
1744 /**
1745 * @see IFigure#translateToParent(Translatable)
1746 */
1747 public void translateToParent(Translatable t) {
1748 if (useLocalCoordinates())
1749 t.performTranslate(
1750 getBounds().x + getInsets().left,
1751 getBounds().y + getInsets().top);
1752 }
1753
1754 /**
1755 * @see IFigure#translateToRelative(Translatable)
1756 */
1757 public final void translateToRelative(Translatable t) {
1758 if (getParent() !is null) {
1759 getParent().translateToRelative(t);
1760 getParent().translateFromParent(t);
1761 }
1762 }
1763
1764 /**
1765 * Returns <code>true</code> if this Figure uses local coordinates. This means its
1766 * children are placed relative to this Figure's top-left corner.
1767 * @return <code>true</code> if this Figure uses local coordinates
1768 * @since 2.0
1769 */
1770 protected bool useLocalCoordinates() {
1771 return false;
1772 }
1773
1774 /**
1775 * @see IFigure#validate()
1776 */
1777 public void validate() {
1778 if (isValid())
1779 return;
1780 setValid(true);
1781 layout();
1782 for (int i = 0; i < children.size(); i++)
1783 (cast(IFigure)children.get(i)).validate();
1784 }
1785
1786 /**
1787 * A search which does not filter any figures.
1788 * since 3.0
1789 */
1790 protected static final class IdentitySearch : TreeSearch {
1791 /**
1792 * The singleton instance.
1793 */
1794 public static const TreeSearch INSTANCE;
1795 static this(){
1796 INSTANCE = new IdentitySearch();
1797 }
1798 private this() { }
1799 /**
1800 * Always returns <code>true</code>.
1801 * @see TreeSearch#accept(IFigure)
1802 */
1803 public bool accept(IFigure f) {
1804 return true;
1805 }
1806 /**
1807 * Always returns <code>false</code>.
1808 * @see TreeSearch#prune(IFigure)
1809 */
1810 public bool prune(IFigure f) {
1811 return false;
1812 }
1813 }
1814
1815 final class LayoutNotifier : LayoutManager {
1816
1817 LayoutManager realLayout;
1818 List listeners;
1819
1820 this(LayoutManager layout, LayoutListener listener) {
1821 listeners = new ArrayList(1);
1822 realLayout = layout;
1823 listeners.add(cast(Object)listener);
1824 }
1825
1826 public Object getConstraint(IFigure child) {
1827 if (realLayout !is null)
1828 return realLayout.getConstraint(child);
1829 return null;
1830 }
1831
1832 public Dimension getMinimumSize(IFigure container, int wHint, int hHint) {
1833 if (realLayout !is null)
1834 return realLayout.getMinimumSize(container, wHint, hHint);
1835 return null;
1836 }
1837
1838 public Dimension getPreferredSize(IFigure container, int wHint, int hHint) {
1839 if (realLayout !is null)
1840 return realLayout.getPreferredSize(container, wHint, hHint);
1841 return null;
1842 }
1843
1844 public void invalidate() {
1845 for (int i = 0; i < listeners.size(); i++)
1846 (cast(LayoutListener)listeners.get(i)).invalidate(this.outer);
1847
1848 if (realLayout !is null)
1849 realLayout.invalidate();
1850 }
1851
1852 public void layout(IFigure container) {
1853 bool consumed = false;
1854 for (int i = 0; i < listeners.size(); i++)
1855 consumed |= (cast(LayoutListener)listeners.get(i)).layout(container);
1856
1857 if (realLayout !is null && !consumed)
1858 realLayout.layout(container);
1859 for (int i = 0; i < listeners.size(); i++)
1860 (cast(LayoutListener)listeners.get(i)).postLayout(container);
1861 }
1862
1863 public void remove(IFigure child) {
1864 for (int i = 0; i < listeners.size(); i++)
1865 (cast(LayoutListener)listeners.get(i)).remove(child);
1866 if (realLayout !is null)
1867 realLayout.remove(child);
1868 }
1869
1870 public void setConstraint(IFigure child, Object constraint) {
1871 for (int i = 0; i < listeners.size(); i++)
1872 (cast(LayoutListener)listeners.get(i)).setConstraint(child, constraint);
1873 if (realLayout !is null)
1874 realLayout.setConstraint(child, constraint);
1875 }
1876 }
1877
1878 /**
1879 * Iterates over a Figure's children.
1880 */
1881 public static class FigureIterator {
1882 private List list;
1883 private int index;
1884 /**
1885 * Constructs a new FigureIterator for the given Figure.
1886 * @param figure The Figure whose children to iterate over
1887 */
1888 public this(IFigure figure) {
1889 list = figure.getChildren();
1890 index = list.size();
1891 }
1892 /**
1893 * Returns the next Figure.
1894 * @return The next Figure
1895 */
1896 public IFigure nextFigure() {
1897 return cast(IFigure)list.get(--index);
1898 }
1899 /**
1900 * Returns <code>true</code> if there's another Figure to iterate over.
1901 * @return <code>true</code> if there's another Figure to iterate over
1902 */
1903 public bool hasNext() {
1904 return index > 0;
1905 }
1906 }
1907
1908 /**
1909 * An UpdateManager that does nothing.
1910 */
1911 protected static const UpdateManager NO_MANAGER;
1912 static this(){
1913 NO_MANAGER = new class() UpdateManager {
1914 public void addDirtyRegion (IFigure figure, int x, int y, int w, int h) { }
1915 public void addInvalidFigure(IFigure f) { }
1916 public void performUpdate() { }
1917 public void performUpdate(Rectangle region) { }
1918 public void setRoot(IFigure root) { }
1919 public void setGraphicsSource(GraphicsSource gs) { }
1920 };
1921 }
1922 }