source: trunk/src/org/expeditee/items/widgets/InteractiveWidget.java@ 296

Last change on this file since 296 was 296, checked in by bjn8, 16 years ago

3rd party agents can load themselves into expeditee agent collection at runtime

File size: 43.3 KB
Line 
1package org.expeditee.items.widgets;
2
3import java.awt.Color;
4import java.awt.Component;
5import java.awt.Container;
6import java.awt.Graphics;
7import java.awt.Graphics2D;
8import java.awt.Point;
9import java.awt.Rectangle;
10import java.awt.event.MouseEvent;
11import java.lang.reflect.Constructor;
12import java.util.ArrayList;
13import java.util.Collection;
14import java.util.Collections;
15import java.util.LinkedList;
16import java.util.List;
17
18import javax.swing.JComponent;
19import javax.swing.SwingUtilities;
20
21import org.expeditee.actions.Actions;
22import org.expeditee.gui.Browser;
23import org.expeditee.gui.DisplayIO;
24import org.expeditee.gui.Frame;
25import org.expeditee.gui.FrameGraphics;
26import org.expeditee.gui.FrameIO;
27import org.expeditee.gui.FreeItems;
28import org.expeditee.gui.MouseEventRouter;
29import org.expeditee.items.Item;
30import org.expeditee.items.ItemParentStateChangedEvent;
31import org.expeditee.items.ItemUtils;
32import org.expeditee.items.Permission;
33import org.expeditee.items.Text;
34
35/**
36 * The bridge between swing space and expeditee space
37 *
38 * @author Brook
39 *
40 */
41public abstract class InteractiveWidget {
42
43 protected JComponent _swingComponent;
44
45 /** A widget is comprised of dots and lines that basically form a rectangle */
46 private WidgetCorner _d1, _d2, _d3, _d4;
47
48 private WidgetEdge _l1, _l2, _l3, _l4;
49
50 /*
51 * GUIDE: l1 d1-------d2 | | l4 | X | 12 | | d4-------d3 13
52 */
53 private List<Item> _expediteeItems; // used for quickly returning item list
54
55 // Widget size restrictions
56 private int _minWidth = 50;
57
58 private int _minHeight = 50;
59
60 private int _maxWidth = 300;
61
62 private int _maxHeight = 300;
63
64 // The Expeditee item that is used for saving widget state in expiditee
65 // world
66 private Text _textRepresentation;
67
68 protected final static Color FREESPACE_BACKCOLOR = new Color(100, 100, 100);
69
70 // A flag for signifying whether the swing components are ready to paint.
71 // If the swing components has not been layed out, if they are painted they
72 // will not draw in the correct positions.
73 // Also for setting AWT and Swing -Related drawing options after
74 // construction
75 private boolean _isReadyToPaint = false;
76
77 /** For ensuring only one event is listened to - instead of four. */
78 private MouseEvent _lastParentStateChangedMouseEvent = null;
79
80 /** For ensuring only one event is listened to - instead of four. */
81 private ItemParentStateChangedEvent _lastParentStateChangedEvent = null;
82
83 /** The minum border thickness for widgets. */
84 public final static float DEFAULT_MINIMUM_BORDER_THICKNESS = 1.0f;
85
86 /**
87 * Creates a InteractiveWidget from a text item.
88 *
89 * @param source
90 * Must not be null, first line of text used - which the format
91 * must be as follows: "@iw: <<widget_class_name>> [<<width>>] [<<height>>] [: [<<arg1>>] [<<arg2>>]
92 * [...]]".
93 *
94 * e.g: "@iw: org.expeditee.items.SampleWidget1 100 20 : 2" creates a
95 * SampleWidget1 with width = 100 and height = 20 with 1 argument = "2"
96 *
97 * @return An InteractiveWidget instance. Never null.
98 *
99 * @throws NullPointerException
100 * if source is null
101 *
102 * @throws IllegalArgumentException
103 * if source's text is in the incorrect format or if source's
104 * parent is null
105 *
106 * @throws InteractiveWidgetNotAvailableException
107 * If the given widget class name in the source text doesn't
108 * exist or not an InteractiveWidget or the widget does not
109 * supply a valid constructor for creation.
110 *
111 * @throws InteractiveWidgetInitialisationFailedException
112 * The sub-constructor failed - depends on the type of widget
113 * being instantainiated.
114 *
115 * class names beginning with $, the $ will be replaced with
116 * "org.expeditee.items."
117 */
118 public static InteractiveWidget createWidget(Text source)
119 throws InteractiveWidgetNotAvailableException,
120 InteractiveWidgetInitialisationFailedException {
121
122 if (source == null)
123 throw new NullPointerException("source");
124 if (source.getParent() == null)
125 throw new IllegalArgumentException(
126 "source's parent is null, InteractiveWidget's must be created from Text items with non-null parents");
127
128 String TAG = ItemUtils.GetTag(ItemUtils.TAG_IWIDGET);
129
130 String text = source.getText();
131 if (text == null)
132 throw new IllegalArgumentException("source does not have any text");
133
134 text = text.trim();
135
136 // Check starts with the widget tag and seporator
137 if (!text.startsWith(TAG + ":"))
138 throw new IllegalArgumentException("Source text must begin with \""
139 + TAG + ":\"");
140
141 text = text.substring(TAG.length() + 1).trim();
142
143 int index = 0;
144 if (text.length() > 0) {
145 index = text.indexOf(':'); // used for
146 // signifying start
147 // of arguments
148 }
149
150 if (index == 0)
151 throw new IllegalArgumentException("Source text must begin with \""
152 + TAG + "\"");
153
154 String[] tokens = (index == -1) ? text.split("\\s+") : text.substring(
155 0, index).split(" ");
156
157 float width = -1, height = -1;
158
159 if (tokens.length < 1)
160 throw new IllegalArgumentException(
161 "Missing widget class name in source text");
162
163 try {
164
165 if (tokens.length >= 2) { // parse optional width
166 width = Integer.parseInt(tokens[1]);
167 width = (width <= 0) ? width = -1 : width;
168 }
169
170 if (tokens.length >= 3) { // parse optional height
171 height = Integer.parseInt(tokens[2]);
172 height = (height <= 0) ? height = -1 : height;
173 }
174
175 } catch (NumberFormatException nfe) {
176 throw new IllegalArgumentException(
177 "Bad width or height given in source text", nfe);
178 }
179
180 if (tokens.length > 3)
181 throw new IllegalArgumentException(
182 "to many arguments given before \":\" in source text");
183
184 String classname = tokens[0];
185 if (classname.charAt(0) == '$')
186 classname = Actions.getClassName(classname.substring(1));
187 // Attempt to locate the class using reflection
188 Class<?> iwclass = findIWidgetClass(classname);
189
190 if (iwclass == null) // ensure it exists
191 throw new InteractiveWidgetNotAvailableException(classname
192 + " does not exist or is not an InteractiveWidget");
193
194 // Extract out the parameters - if any
195 String[] args = null;
196 if (index > 0) { // index of the first ":"
197 args = (text.length() == (index + 1)) ? null : parseArgs(text
198 .substring(index + 1));
199 }
200
201 InteractiveWidget inst = null;
202 try {
203 // Instantainiate the widget - passing the params
204 Class parameterTypes[] = new Class[] { Text.class, String[].class };
205 Constructor ct = iwclass.getConstructor(parameterTypes);
206
207 Object arglist[] = new Object[] { source, args };
208
209 inst = (InteractiveWidget) ct.newInstance(arglist);
210 } catch (Exception e) {
211 throw new InteractiveWidgetNotAvailableException(
212 "Failed to create instance via reflection: " + e.toString(),
213 e);
214 }
215
216 // Use default dimensions if not provided (or provided as negitive
217 // values)
218 if (width <= 0)
219 width = inst.getWidth();
220 if (height <= 0)
221 height = inst.getHeight();
222
223 inst.setSize(width, height);
224
225 return inst;
226 }
227
228 /**
229 * Locates the class from the classname of an InteractiveWidget class
230 *
231 * @param classname
232 * The name of the class to search
233 * @return Null if doesn't exist or not an InteractiveWidget
234 */
235 private static Class findIWidgetClass(String classname) {
236 try {
237 Class c = Class.forName(classname); // attempt to find the class
238
239 // If one is found, ensure that it is a descendant of an
240 // InteractiveWidget
241 for (Class superclass = c.getSuperclass(); superclass != null
242 && superclass != Item.class; superclass = superclass
243 .getSuperclass()) {
244 if (superclass == InteractiveWidget.class)
245 return c;
246 }
247
248 } catch (ClassNotFoundException e) {
249 }
250
251 // Doesn't exist or not an InteractiveWidget
252 return null;
253 }
254
255 /**
256 * Using Microsofts commandline convention: Args seperated with white
257 * spaces. Options with white spaces enclosed with quotes. Args with quotes
258 * must be double quoted args1 args2=sfasas args3="option with spaces"
259 * arg""4""
260 *
261 * @param args
262 * Null and empty excepted
263 * @return An array of args. null if none provided
264 */
265 static String[] parseArgs(String args) {
266
267 if (args == null)
268 return null;
269
270 args = args.trim();
271 if (args.length() == 0)
272 return null;
273
274 List<String> vargs = new LinkedList<String>();
275 StringBuilder sb = new StringBuilder();
276 boolean quoteOn = false;
277 for (int i = 0; i < args.length(); i++) {
278
279 char c = args.charAt(i);
280 if (c == ' ' && !quoteOn) {
281
282 // Extract arg
283 vargs.add(sb.toString());
284 sb = new StringBuilder();
285
286 // Consume white spaces
287 while (args.charAt(++i) == ' ' && i < args.length()) {
288 }
289 i--;
290
291 } else if (c == '\"') {
292 // If double qouted
293 if (args.length() >= (i + 2) && args.charAt(i + 1) == '\"') {
294
295 sb.append(c); // add escaped
296 i++;
297
298 } else {
299 quoteOn = !quoteOn;
300 }
301
302 } else {
303 sb.append(c);
304 }
305 }
306
307 if (sb.length() > 0)
308 vargs.add(sb.toString());
309
310 if (vargs.size() == 0)
311 return null;
312 else
313 return vargs.toArray(new String[vargs.size()]);
314 }
315
316 /**
317 * Reverse of parseArgs
318 *
319 * @return Null if args is null or empty / all whitespace
320 */
321 public static String formatArgs(String[] args) {
322 if (args == null)
323 return null;
324
325 StringBuilder sb = new StringBuilder();
326
327 for (String s : args) {
328 if (s == null)
329 continue;
330
331 // Escape quotes
332 StringBuilder formatted = new StringBuilder(s.replaceAll("\"",
333 "\"\""));
334
335 // Encapsulate spaces
336 int index = formatted.indexOf(" ");
337 if (index >= 0) {
338 formatted.insert(index, "\"");
339 formatted.append('\"');
340 }
341
342 if (sb.length() > 0)
343 sb.append(' ');
344 sb.append(formatted);
345 }
346
347 return sb.length() > 0 ? sb.toString() : null;
348 }
349
350 /**
351 * Arguments represent the widgets <i>current state</i> state. They are
352 * used for saving, loading, creating and cloning Special formatting is done
353 * for you.
354 *
355 * @see #getData()
356 *
357 * @return Can be null for no params.
358 */
359 protected abstract String[] getArgs();
360
361 /**
362 * Data represents the widgets <i>current state</i> state. For any state
363 * information you do not wish to be defined as arguments (e.g. metadata),
364 * you can set as the widgets source items data.
365 *
366 * The default implementation returns null. Override to make use of.
367 *
368 * @see #getArgs()
369 *
370 * @return Null for for data. Otherwise the data that represent this widgets
371 * <i>current state</i>
372 */
373 protected List<String> getData() {
374 return null;
375 }
376
377 /**
378 * Constructor
379 *
380 * @param source
381 * Must not be null. Neither must it's parent
382 *
383 * @param component
384 * Must not be null.
385 *
386 * @param minWidth
387 * The min width restriction for the widget. If negative, then
388 * there is no restriction.
389 *
390 * @param maxWidth
391 * The max width restriction for the widget. If negative, then
392 * there is no restriction.
393 *
394 * @param minHeight
395 * The min height restriction for the widget. If negative, then
396 * there is no restriction.
397 *
398 * @param maxHeight
399 * The max height restriction for the widget. If negative, then
400 * there is no restriction.
401 *
402 * @throws NullPointerException
403 * If source, component if null.
404 *
405 * @throws IllegalArgumentException
406 * If source's parent is null. If maxWidth smaller than minWidth
407 * and maxWidth larger or equal to zero or if maxHeight smaller
408 * than minHeight && maxHeight is larger or equal to zero
409 *
410 */
411 protected InteractiveWidget(Text source, JComponent component,
412 int minWidth, int maxWidth, int minHeight, int maxHeight) {
413
414 if (component == null)
415 throw new NullPointerException("component");
416 if (source == null)
417 throw new NullPointerException("source");
418 if (source.getParent() == null)
419 throw new IllegalArgumentException(
420 "source's parent is null, InteractiveWidget's must be created from Text items with non-null parents");
421
422 _swingComponent = component;
423 _textRepresentation = source;
424
425 setSizeRestrictions(minWidth, maxWidth, minHeight, maxHeight); // throws
426 // IllegalArgumentException's
427
428 int x = source.getX();
429 int y = source.getY();
430 int width = (int) ((_minWidth < 0) ? 10 : _minWidth);
431 int height = (int) ((_minHeight < 0) ? 10 : _minHeight);
432
433 Frame idAllocator = _textRepresentation.getParent();
434
435 // create WidgetCorners
436 _d1 = new WidgetCorner(x, y, idAllocator.getNextItemID(), this);
437 _d2 = new WidgetCorner(x + width, y, idAllocator.getNextItemID(), this);
438 _d3 = new WidgetCorner(x + width, y + height, idAllocator
439 .getNextItemID(), this);
440 _d4 = new WidgetCorner(x, y + height, idAllocator.getNextItemID(), this);
441
442 // create WidgetEdges
443 _l1 = new WidgetEdge(_d1, _d2, idAllocator.getNextItemID(), this);
444 _l2 = new WidgetEdge(_d2, _d3, idAllocator.getNextItemID(), this);
445 _l3 = new WidgetEdge(_d3, _d4, idAllocator.getNextItemID(), this);
446 _l4 = new WidgetEdge(_d4, _d1, idAllocator.getNextItemID(), this);
447
448 Collection<Item> enclist = new ArrayList<Item>(4);
449 enclist.add(_d1);
450 enclist.add(_d2);
451 enclist.add(_d3);
452 enclist.add(_d4);
453 _d1.setEnclosedList(enclist);
454 _d2.setEnclosedList(enclist);
455 _d3.setEnclosedList(enclist);
456 _d4.setEnclosedList(enclist);
457
458 _expediteeItems = new ArrayList<Item>(8); // Note: order important
459 _expediteeItems.add(_d1);
460 _expediteeItems.add(_d2);
461 _expediteeItems.add(_d3);
462 _expediteeItems.add(_d4);
463 _expediteeItems.add(_l1);
464 _expediteeItems.add(_l2);
465 _expediteeItems.add(_l3);
466 _expediteeItems.add(_l4);
467
468 for (Item i : _d1.getAllConnected()) {
469 i.setColor(source.getBorderColor());
470 i.setThickness(source.getThickness(), false);
471 }
472 }
473
474 /**
475 * Sets the restrictions - checks values.
476 *
477 * @param minWidth
478 * The min width restriction for the widget. If negative, then
479 * there is no restriction.
480 *
481 * @param maxWidth
482 * The max width restriction for the widget. If negative, then
483 * there is no restriction.
484 *
485 * @param minHeight
486 * The min height restriction for the widget. If negative, then
487 * there is no restriction.
488 *
489 * @param maxHeight
490 * The max height restriction for the widget. If negative, then
491 * there is no restriction.
492 *
493 * @throws IllegalArgumentException
494 * If maxWidth smaller than minWidth and maxWidth larger or
495 * equal to zero or if maxHeight smaller than minHeight &&
496 * maxHeight is larger or equal to zero
497 *
498 */
499 private void setSizeRestrictions(int minWidth, int maxWidth, int minHeight,
500 int maxHeight) {
501
502 _minWidth = minWidth;
503
504 if (maxWidth < _minWidth && maxWidth >= 0)
505 throw new IllegalArgumentException(
506 "maxWidth smaller than the min Width");
507 _maxWidth = maxWidth;
508
509 _minHeight = minHeight;
510 if (maxHeight < _minHeight && maxHeight >= 0)
511 throw new IllegalArgumentException(
512 "maxHeight smaller than the min Height");
513 _maxHeight = maxHeight;
514 }
515
516 /**
517 * This can be overrided for creating custom copies. The default
518 * implementation creates a new widget based on the current state of the
519 * widget (via getArgs).
520 *
521 * @see InteractiveWidget#getArgs().
522 *
523 * @return A copy of this widget.
524 *
525 */
526 public InteractiveWidget copy()
527 throws InteractiveWidgetNotAvailableException,
528 InteractiveWidgetInitialisationFailedException {
529
530 Text t = _textRepresentation.copy();
531 String clonedAnnotation = getAnnotationString();
532 t.setText(clonedAnnotation);
533 t.setData(getData());
534 return InteractiveWidget.createWidget(t);
535
536 }
537
538 /**
539 * Notifies widget of delete
540 */
541 public void onDelete() {
542
543 // Allocate new ID's
544 Frame parent = getParentFrame();
545 if (parent == null)
546 parent = DisplayIO.getCurrentFrame();
547
548 if (parent != null) {
549 for (Item i : _expediteeItems) {
550 i.setID(parent.getNextItemID());
551 }
552 }
553
554 invalidateLink();
555
556 }
557
558 /**
559 * Note updates the source text with current state info
560 *
561 * @return The Text item that this widget was created from.
562 */
563 public Item getSource() {
564
565 // Build the annotation string such that it represents this widgets
566 // current state
567 String newAnnotation = getAnnotationString();
568
569 // Set the new text
570 _textRepresentation.setText(newAnnotation);
571
572 // Set the data
573 _textRepresentation.setData(getData());
574
575 return _textRepresentation;
576 }
577
578 /**
579 * @return The current representation for this widget. The representation
580 * stores link infomation, data etc... It is used for saving and
581 * loading of the widget. Never null.
582 *
583 */
584 protected Item getCurrentRepresentation() {
585 return _textRepresentation;
586 }
587
588 /**
589 * @return The expeditee anotation string.
590 */
591 protected String getAnnotationString() {
592
593 // Create tag and append classname
594 StringBuilder sb = new StringBuilder(ItemUtils
595 .GetTag(ItemUtils.TAG_IWIDGET));
596 sb.append(':');
597 sb.append(' ');
598 sb.append(getClass().getName());
599
600 // Append size information if needed (not an attibute of text items)
601 if (!isFixedSize()) {
602 sb.append(' ');
603 sb.append(getWidth());
604 sb.append(' ');
605 sb.append(getHeight());
606 }
607
608 // Append arguments if any
609 String stateArgs = InteractiveWidget.formatArgs(getArgs());
610 if (stateArgs != null) {
611 sb.append(':');
612 sb.append(stateArgs);
613 }
614
615 return sb.toString();
616 }
617
618 /**
619 * Sets both the new size as well as the new min/max widdet/height
620 * restricitons.
621 *
622 * @param minWidth
623 * The min width restriction for the widget. If negative, then
624 * there is no restriction.
625 *
626 * @param maxWidth
627 * The max width restriction for the widget. If negative, then
628 * there is no restriction.
629 *
630 * @param minHeight
631 * The min height restriction for the widget. If negative, then
632 * there is no restriction.
633 *
634 * @param maxHeight
635 * The max height restriction for the widget. If negative, then
636 * there is no restriction.
637 *
638 * @param newWidth
639 * Clamped to new restrictions.
640 *
641 * @param newHeight
642 * Clamped to new restrictions.
643 *
644 * @throws IllegalArgumentException
645 * If maxWidth smaller than minWidth and maxWidth larger or
646 * equal to zero or if maxHeight smaller than minHeight &&
647 * maxHeight is larger or equal to zero
648 *
649 * @see #setSize(float, float)
650 *
651 */
652 public void setSize(int minWidth, int maxWidth, int minHeight,
653 int maxHeight, float newWidth, float newHeight) {
654
655 setSizeRestrictions(minWidth, maxWidth, minHeight, maxHeight); // throws
656 // IllegalArgumentException's
657 setSize(newWidth, newHeight);
658 }
659
660 /**
661 * Clamped to current min/max width/height.
662 *
663 * @param width
664 * Clamped to current restrictions.
665 * @param height
666 * Clamped to current restrictions.
667 *
668 * @see #setSize(int, int, int, int, float, float)
669 */
670 public void setSize(float width, float height) {
671
672 // Clamp
673 if (width < _minWidth && _minWidth >= 0)
674 width = _minWidth;
675 else if (width > _maxWidth && _maxWidth >= 0)
676 width = _maxWidth;
677
678 if (height < _minHeight && _minHeight >= 0)
679 height = _minHeight;
680 else if (height > _maxHeight && _maxHeight >= 0)
681 height = _maxHeight;
682
683 boolean vfloating[] = new boolean[] { _d1.isFloating(),
684 _d2.isFloating(), _d3.isFloating(), _d4.isFloating() };
685
686 _d1.setFloating(true);
687 _d2.setFloating(true);
688 _d3.setFloating(true);
689 _d4.setFloating(true);
690
691 float x = _d1.getX() + width;
692 float y = _d1.getY() + height;
693
694 _d2.setX(x);
695 _d3.setX(x);
696 _d3.setY(y);
697 _d4.setY(y);
698
699 _d1.setFloating(vfloating[0]);
700 _d2.setFloating(vfloating[1]);
701 _d3.setFloating(vfloating[2]);
702 _d4.setFloating(vfloating[3]);
703
704 onSizeChanged();
705 }
706
707 public void setPosition(int x, int y) {
708 if (x == getX() && y == getY())
709 return;
710
711 boolean vfloating[] = new boolean[] { _d1.isFloating(),
712 _d2.isFloating(), _d3.isFloating(), _d4.isFloating() };
713
714 int width = getWidth();
715 int height = getHeight();
716
717 invalidateLink();
718
719 _d1.setFloating(true);
720 _d2.setFloating(true);
721 _d3.setFloating(true);
722 _d4.setFloating(true);
723
724 _d1.setPosition(x, y);
725 _d2.setPosition(x + width, y);
726 _d3.setPosition(x + width, y + height);
727 _d4.setPosition(x, y + height);
728
729 _d1.setFloating(vfloating[0]);
730 _d2.setFloating(vfloating[1]);
731 _d3.setFloating(vfloating[2]);
732 _d4.setFloating(vfloating[3]);
733
734 invalidateLink();
735
736 onMoved();
737
738 }
739
740 private boolean _settingPositionFlag = false; // used for recursion
741
742 /**
743 * to be called from corners only
744 *
745 * @param src
746 * @param x
747 * @param y
748 * @return False if need to call super.setPosition
749 */
750 boolean setPositions(WidgetCorner src, float x, float y) {
751
752 if (_settingPositionFlag)
753 return false;
754 _settingPositionFlag = true;
755
756 invalidateLink();
757
758 // Check to see if the widget is fully being picked up
759 boolean isAllPickedUp = (_d1.isFloating() && _d2.isFloating()
760 && _d3.isFloating() && _d4.isFloating());
761
762 // If so, then this will be called one by one ..
763 if (isAllPickedUp) {
764 src.setPosition(x, y);
765 } else {
766
767 float newX = x;
768
769 // Reference:
770 // D1 D2
771 // D3 D4
772
773 //
774 // GUIDE:
775 // l1
776 // d1-------d2
777 // | |
778 // l4 | X | 12
779 // | |
780 // d4-------d3
781 // 13
782 //
783
784 // X Positions
785 if (src == _d1 || src == _d4) {
786
787 // Check min X constraint
788 if (_minWidth >= 0) {
789 if ((_d2.getX() - x) < _minWidth) {
790 newX = _d2.getX() - _minWidth;
791 }
792 }
793 // Check max X constraint
794 if (_maxWidth >= 0) {
795 if ((_d2.getX() - x) > _maxWidth) {
796 newX = _d2.getX() - _maxWidth;
797 }
798 }
799
800 if (!(src == _d4 && _d1.isFloating() && _d4.isFloating()))
801 _d1.setX(newX);
802 if (!(src == _d1 && _d4.isFloating() && _d1.isFloating()))
803 _d4.setX(newX);
804
805 } else if (src == _d2 || src == _d3) {
806
807 // Check min X constraint
808 if (_minWidth >= 0) {
809 if ((x - _d1.getX()) < _minWidth) {
810 newX = _d1.getX() + _minWidth;
811 }
812 }
813 // Check max X constraint
814 if (_maxWidth >= 0) {
815 if ((x - _d1.getX()) > _maxWidth) {
816 newX = _d1.getX() + _maxWidth;
817 }
818 }
819
820 if (!(src == _d3 && _d2.isFloating() && _d3.isFloating()))
821 _d2.setX(newX);
822 if (!(src == _d2 && _d3.isFloating() && _d2.isFloating()))
823 _d3.setX(newX);
824 }
825
826 float newY = y;
827
828 // Y Positions
829 if (src == _d1 || src == _d2) {
830
831 // Check min Y constraint
832 if (_minHeight >= 0) {
833 if ((_d4.getY() - y) < _minHeight) {
834 newY = _d4.getY() - _minHeight;
835 }
836 }
837 // Check max Y constraint
838 if (_maxHeight >= 0) {
839 if ((_d4.getY() - y) > _maxHeight) {
840 newY = _d4.getY() - _maxHeight;
841 }
842 }
843
844 if (!(src == _d2 && _d1.isFloating() && _d2.isFloating()))
845 _d1.setY(newY);
846 if (!(src == _d1 && _d2.isFloating() && _d1.isFloating()))
847 _d2.setY(newY);
848
849 } else if (src == _d3 || src == _d4) {
850
851 // Check min Y constraint
852 if (_minHeight >= 0) {
853 if ((y - _d1.getY()) < _minHeight) {
854 newY = _d1.getY() + _minHeight;
855 }
856 }
857 // Check max Y constraint
858 if (_maxHeight >= 0) {
859 if ((y - _d1.getY()) > _maxHeight) {
860 newY = _d1.getY() + _maxHeight;
861 }
862 }
863
864 if (!(src == _d4 && _d3.isFloating() && _d4.isFloating()))
865 _d3.setY(newY);
866 if (!(src == _d3 && _d4.isFloating() && _d3.isFloating()))
867 _d4.setY(newY);
868 }
869 }
870
871 // Update source text position so position is remembered from loading
872 float newTextX = getX();
873 float newTextY = getY();
874 if (_textRepresentation.getX() != newTextX
875 || _textRepresentation.getY() != newTextY)
876 _textRepresentation.setPosition(newTextX, newTextY);
877
878 _settingPositionFlag = false;
879
880 invalidateLink();
881
882 onMoved();
883
884 return true;
885 }
886
887 public int getX() {
888 return Math.min(_d1.getX(), _d2.getX());
889 }
890
891 public int getY() {
892 return Math.min(_d1.getY(), _d4.getY());
893 }
894
895 public int getWidth() {
896
897 return Math.abs(_d2.getX() - _d1.getX());
898 }
899
900 public int getHeight() {
901 return Math.abs(_d4.getY() - _d1.getY());
902 }
903
904 /**
905 * The order of the items in the list is as specified: _d1 _d2 _d3 _d4 _l1
906 * _l2 _l3 _l4
907 *
908 * @return All of the Expeditee items that form the bounderies of this
909 * widget in an unmodifiable list
910 */
911 public List<Item> getItems() {
912 return Collections.unmodifiableList(_expediteeItems);
913 }
914
915 public JComponent getComponant() {
916 return _swingComponent;
917 }
918
919 public final void onParentStateChanged(ItemParentStateChangedEvent e) {
920
921 // Because widgets are comprised of four corners - they all report this
922 // event one after the
923 // other. So must filter out redunant notifications like so:
924 if (_lastParentStateChangedEvent != null
925 && _lastParentStateChangedEvent.getEventType() == e
926 .getEventType()
927 && _lastParentStateChangedMouseEvent == MouseEventRouter
928 .getCurrentMouseEvent())
929 return; // already dealt with this event
930
931 _lastParentStateChangedEvent = e;
932 _lastParentStateChangedMouseEvent = MouseEventRouter
933 .getCurrentMouseEvent();
934
935 switch (e.getEventType()) {
936
937 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED:
938 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED_VIA_OVERLAY:
939 case ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN:
940 if (_swingComponent.getParent() != null) {
941 _swingComponent.getParent().remove(_swingComponent);
942 }
943 break;
944
945 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED:
946 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY:
947 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN:
948 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY:
949 if (_swingComponent.getParent() == null) {
950 addJComponantToFrame(e);
951 }
952 break;
953
954 }
955
956 FrameGraphics.invalidateItem(_d1, _swingComponent.getBounds());
957
958 // Forward filtered event to upper classeses...
959 onParentStateChanged(e.getEventType());
960 }
961
962 /**
963 * Override to make use of. Internally this is reported once by all corners,
964 * but is filterted out so that this method is invoked once per event.
965 *
966 * @param eventType
967 * The {@link ItemParentStateChangedEvent#getEventType()} that
968 * occured.
969 *
970 */
971 protected void onParentStateChanged(int eventType) {
972 }
973
974 protected void addJComponantToFrame(ItemParentStateChangedEvent e) {
975
976 if ((e.getEventType() == ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY || e
977 .getEventType() == ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY)
978 && e.getOverlayLevel().equals(Permission.none)) {
979 return; // item belongs to a non-active overlay
980 }
981
982 if (_swingComponent.getParent() == null) {
983
984 if (Browser._theBrowser != null) {
985 // Due to precaching - before adding physical swing
986 // componant must check to see that this widget belongs to a
987 // frame that is
988 // considered current. If the widget is shown however this does
989 // not apply -
990 // since it has been explicitly made clear the the widget is
991 // shown.
992 if (e.getEventType() == ItemParentStateChangedEvent.EVENT_TYPE_SHOWN
993 || e.getEventType() == ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY
994 || e.getSource() == DisplayIO.getCurrentFrame()) {
995
996 onBoundsChanged();
997 Browser._theBrowser.getContentPane().add(_swingComponent);
998 layout(_swingComponent);
999 }
1000
1001 } else { // if widgets exist on startup frame this will occur
1002
1003 synchronized (_widgetsToAddLater) {
1004 _widgetsToAddLater.add(new DelayedWidgetEvent(this, e));
1005 }
1006 SwingUtilities.invokeLater(new AddToFrameLater());
1007 }
1008
1009 }
1010
1011 }
1012
1013 /**
1014 * @return True if at least one corner is floating
1015 */
1016 public boolean isFloating() {
1017 return _d1.isFloating() || _d2.isFloating() || _d3.isFloating()
1018 || _d4.isFloating();
1019 }
1020
1021 public boolean areCornersFullyAnchored() {
1022 return _d1.getParent() != null && _d2.getParent() != null
1023 && _d3.getParent() != null && _d4.getParent() != null;
1024 }
1025
1026 /**
1027 * Used for passing info to the swing thread
1028 *
1029 * @author Brook Novak
1030 */
1031 private class DelayedWidgetEvent {
1032
1033 DelayedWidgetEvent(InteractiveWidget widget,
1034 ItemParentStateChangedEvent e) {
1035 _widget = widget;
1036 _e = e;
1037 }
1038
1039 InteractiveWidget _widget;
1040
1041 ItemParentStateChangedEvent _e;
1042 }
1043
1044 /**
1045 * Must be able to add widgets on first loaded frame: these are loaded
1046 * before the browser singleton is made available.
1047 */
1048 private static List<DelayedWidgetEvent> _widgetsToAddLater = new LinkedList<DelayedWidgetEvent>();
1049
1050 /**
1051 * Ensures widgets are added correctly to first loaded frame
1052 */
1053 class AddToFrameLater implements Runnable {
1054 public void run() {
1055 if (!_widgetsToAddLater.isEmpty()) {
1056 List<DelayedWidgetEvent> tmp = null;
1057 synchronized (_widgetsToAddLater) {
1058 tmp = new LinkedList<DelayedWidgetEvent>(_widgetsToAddLater);
1059 }
1060 _widgetsToAddLater.clear();
1061 for (DelayedWidgetEvent iwi : tmp) {
1062 iwi._widget.addJComponantToFrame(iwi._e);
1063 iwi._widget.invalidateSelf();
1064 }
1065 }
1066 }
1067 }
1068
1069 final void onBoundsChanged() {
1070 if (isFixedSize())
1071 _swingComponent.setBounds(getX(), getY(), _maxWidth, _maxHeight);
1072 else
1073 _swingComponent.setBounds(getX(), getY(), getWidth(), getHeight());
1074 }
1075
1076 /**
1077 *
1078 * @return The current bounds for this widget. Never null.
1079 */
1080 public Rectangle getBounds() {
1081 return new Rectangle(getX(), getY(), getWidth(), getHeight());
1082 }
1083
1084 /**
1085 * Due to absolute positioning...
1086 *
1087 * @param parent
1088 */
1089 private void layout(Component parent) {
1090
1091 parent.validate();
1092
1093 if (parent instanceof Container) {
1094 for (Component c : ((Container) parent).getComponents()) {
1095
1096 if (c instanceof Container)
1097 layout(c);
1098 else
1099 c.validate();
1100 }
1101 }
1102
1103 }
1104
1105 private void ignoreAWTPainting(Component c) {
1106
1107 if (c instanceof JComponent) {
1108 ((JComponent) c).setDoubleBuffered(false);
1109 }
1110
1111 c.setIgnoreRepaint(true);
1112
1113 if (c instanceof Container) {
1114 for (Component child : ((Container) c).getComponents()) {
1115
1116 if (child instanceof Container) {
1117 ignoreAWTPainting(child);
1118 } else {
1119 if (child instanceof JComponent) {
1120 ((JComponent) child).setDoubleBuffered(false);
1121 }
1122
1123 child.setIgnoreRepaint(true);
1124 }
1125 }
1126 }
1127 }
1128
1129 private void prepareToPaint() {
1130 _isReadyToPaint = true;
1131 layout(_swingComponent);
1132 ignoreAWTPainting(_swingComponent);
1133 }
1134
1135 /**
1136 * Paints the widget excluding the boundries. That is, the Swing graphics
1137 *
1138 * @param g
1139 */
1140 public void paint(Graphics g) {
1141
1142 if (!_isReadyToPaint) {
1143 prepareToPaint();
1144 }
1145
1146 Point loc = _swingComponent.getLocation();
1147
1148 g.translate(loc.x, loc.y);
1149 _swingComponent.paint(g);
1150 g.translate(-loc.x, -loc.y);
1151
1152 paintLink((Graphics2D) g);
1153
1154 }
1155
1156 protected void paintLink(Graphics2D g) {
1157 // If this widget is linked .. then draw the link icon
1158 if (_textRepresentation.getLink() != null
1159 || _textRepresentation.hasAction()) {
1160 // System.out.println("Painted link");
1161 _textRepresentation.paintLinkGraphic(g, getLinkX(), getLinkY());
1162
1163 }
1164
1165 }
1166
1167 private int getLinkX() {
1168 return getX() - Item.LEFT_MARGIN;
1169 }
1170
1171 private int getLinkY() {
1172 return getY() + (getHeight() / 2);
1173 }
1174
1175 /**
1176 * Invoked whenever the widget is to be repainted in free space.
1177 *
1178 * @param g
1179 */
1180 protected void paintInFreeSpace(Graphics g) {
1181 g.setColor(FREESPACE_BACKCOLOR);
1182 g.fillRect(getX(), getY(), getWidth(), getHeight());
1183 }
1184
1185 /**
1186 * Called from the widgets corners: Whenever one of the corners are invoked
1187 * for a refill of the enclosed area.
1188 *
1189 * If the widget is floating (e.g. currently picked up / rubberbanding) then
1190 * a shaded area is drawn instead of the actual widget so the manipulation
1191 * of the widget is as smooth as possible.
1192 *
1193 * @param g
1194 * @param notifier
1195 */
1196 void paintFill(Graphics g) {
1197 if (_swingComponent.getParent() == null) {
1198 // Note that frames with @f may want to paint the widgets so do not
1199 // paint over the widget interface in these cases: must only
1200 // paint if an object is floating
1201 if (isFloating()) {
1202 paintInFreeSpace(g);
1203 paintLink((Graphics2D) g);
1204 }
1205 }
1206 }
1207
1208 /**
1209 * @return True if this widget cannot be resized in either directions
1210 */
1211 public boolean isFixedSize() {
1212 return this._minHeight == this._maxHeight
1213 && this._minWidth == this._maxWidth && this._minHeight >= 0
1214 && this._minWidth >= 0;
1215 }
1216
1217 /**
1218 * Removes this widget from the parent frame or free space.
1219 *
1220 * @return True if removed from a parent frame. Thus a parent changed event
1221 * will be invoked.
1222 *
1223 * False if removed purely from free space.
1224 */
1225 protected boolean removeSelf() {
1226
1227 Frame parent = getParentFrame();
1228
1229 if (parent != null) {
1230 parent.removeAllItems(_expediteeItems);
1231 }
1232
1233 FreeItems.getInstance().removeAll(_expediteeItems);
1234
1235 return (parent != null);
1236
1237 }
1238
1239 /**
1240 * @return The parent frame. Null if has none. Note: Based on corners
1241 * parents.
1242 */
1243 public Frame getParentFrame() {
1244
1245 Frame parent = null;
1246 if (_d1.getParent() != null)
1247 parent = _d1.getParent();
1248 else if (_d2.getParent() != null)
1249 parent = _d2.getParent();
1250 else if (_d3.getParent() != null)
1251 parent = _d3.getParent();
1252 else if (_d4.getParent() != null)
1253 parent = _d4.getParent();
1254
1255 return parent;
1256 }
1257
1258 protected void invalidateSelf() {
1259 Rectangle dirty = new Rectangle((int) getX(), (int) getY(),
1260 (int) getWidth(), (int) getHeight());
1261 FrameGraphics.invalidateArea(dirty);
1262 invalidateLink();
1263 //FrameGraphics.refresh(true);
1264 }
1265
1266 /**
1267 * Invalidates the link for this widget - if it has one.
1268 */
1269 protected void invalidateLink() {
1270 if (_textRepresentation.getLink() != null
1271 || _textRepresentation.hasAction()) {
1272 Rectangle linkArea = _textRepresentation.getLinkDrawArea(
1273 getLinkX(), getLinkY());
1274 FrameGraphics.invalidateArea(linkArea);
1275 }
1276
1277 }
1278
1279 /**
1280 * @see ItemUtils#isVisible(Item)
1281 *
1282 * @return True if this widget is visible from the current frame. Considers
1283 * overlays and vectors.
1284 *
1285 */
1286 public boolean isVisible() {
1287 return ItemUtils.isVisible(_d1);
1288 }
1289
1290 /**
1291 * Invoked whenever the widget have moved. Can override.
1292 *
1293 */
1294 protected void onMoved() {
1295 }
1296
1297 /**
1298 * Invoked whenever the widget have moved. Can override.
1299 *
1300 */
1301 protected void onSizeChanged() {
1302 }
1303
1304 /**
1305 * Override to have a custom min border thickness for your widget.
1306 *
1307 * @see #DEFAULT_MINIMUM_BORDER_THICKNESS
1308 *
1309 * @return The minimum border thickness. Should be larger or equal to zero.
1310 *
1311 */
1312 public float getMinimumBorderThickness() {
1313 return DEFAULT_MINIMUM_BORDER_THICKNESS;
1314 }
1315
1316 /**
1317 * Looks fors a dataline in the current representation of the widget.
1318 *
1319 * @see #getCurrentRepresentation
1320 * @see #getStrippedDataInt(String, int)
1321 * @see #getStrippedDataLong(String, long)
1322 *
1323 * @param tag
1324 * The prefix of a dataline that will be matched. Must be larger
1325 * the zero and not null.
1326 *
1327 * @return The <i>first</i> dataline that matched the prefix - without the
1328 * prefix. Null if their was no data that matched the given prefix.
1329 *
1330 * @throws IllegalArgumentException
1331 * If tag is null.
1332 *
1333 * @throws NullPointerException
1334 * If tag is empty.
1335 */
1336 protected String getStrippedDataString(String tag) {
1337 if (tag == null)
1338 throw new NullPointerException("tag");
1339 else if (tag.length() == 0)
1340 throw new IllegalArgumentException("tag is empty");
1341
1342 if (getCurrentRepresentation().getData() != null) {
1343 for (String str : getCurrentRepresentation().getData()) {
1344 if (str != null && str.startsWith(tag)
1345 && str.length() > tag.length()) {
1346 return str.substring(tag.length());
1347 }
1348 }
1349 }
1350 return null;
1351 }
1352
1353 /**
1354 * Looks fors a dataline in the current representation of the widget.
1355 *
1356 * @see #getCurrentRepresentation
1357 * @see #getStrippedDataString(String)
1358 * @see #getStrippedDataLong(String, long)
1359 *
1360 * @param tag
1361 * The prefix of a dataline that will be matched. Must be larger
1362 * the zero and not null.
1363 *
1364 * @param defaultValue
1365 * The default value if the tag does not exist or contains
1366 * invalid data.
1367 *
1368 * @return The <i>first</i> dataline that matched the prefix - parsed as an
1369 * int (after the prefix). defaultValue if their was no data that
1370 * matched the given prefix or the data was invalid.
1371 *
1372 * @throws IllegalArgumentException
1373 * If tag is null.
1374 *
1375 * @throws NullPointerException
1376 * If tag is empty.
1377 *
1378 */
1379 protected Integer getStrippedDataInt(String tag, Integer defaultValue) {
1380
1381 String strippedStr = getStrippedDataString(tag);
1382
1383 if (strippedStr != null) {
1384 strippedStr = strippedStr.trim();
1385 if (strippedStr.length() > 0) {
1386 try {
1387 return Integer.parseInt(strippedStr);
1388 } catch (NumberFormatException e) { /* Consume */
1389 }
1390 }
1391 }
1392
1393 return defaultValue;
1394 }
1395
1396 /**
1397 * Looks fors a dataline in the current representation of the widget.
1398 *
1399 * @see #getCurrentRepresentation
1400 * @see #getStrippedDataString(String)
1401 * @see #getStrippedDataInt(String, int)
1402 *
1403 * @param tag
1404 * The prefix of a dataline that will be matched. Must be larger
1405 * the zero and not null.
1406 *
1407 * @param defaultValue
1408 * The default value if the tag does not exist or contains
1409 * invalid data.
1410 *
1411 * @return The <i>first</i> dataline that matched the prefix - parsed as a
1412 * long (after the prefix). defaultValue if their was no data that
1413 * matched the given prefix or the data was invalid.
1414 *
1415 * @throws IllegalArgumentException
1416 * If tag is null.
1417 *
1418 * @throws NullPointerException
1419 * If tag is empty.
1420 *
1421 */
1422 protected Long getStrippedDataLong(String tag, Long defaultValue) {
1423 String strippedStr = getStrippedDataString(tag);
1424
1425 if (strippedStr != null) {
1426 strippedStr = strippedStr.trim();
1427 if (strippedStr.length() > 0) {
1428 try {
1429 return Long.parseLong(strippedStr);
1430 } catch (NumberFormatException e) { /* Consume */
1431 }
1432 }
1433 }
1434
1435 return defaultValue;
1436 }
1437
1438 /**
1439 * All data is removed that is prefixed with the given tag.
1440 *
1441 * @param tag
1442 * The prefix of the data lines to remove. Must be larger the
1443 * zero and not null.
1444 *
1445 * @throws IllegalArgumentException
1446 * If tag is null.
1447 *
1448 * @throws NullPointerException
1449 * If tag is empty.
1450 *
1451 */
1452 protected void removeData(String tag) {
1453 updateData(tag, null);
1454 }
1455
1456 protected void addDataIfCaseInsensitiveNotExists(String tag) {
1457 if (tag == null) throw new NullPointerException("tag");
1458
1459 List<String> data = getCurrentRepresentation().getData();
1460
1461 if (data == null) {
1462 data = new LinkedList<String>();
1463 }
1464
1465 for (String s : data) {
1466 if (s != null && s.equalsIgnoreCase(tag)) {
1467 return;
1468 }
1469 }
1470
1471 data.add(tag);
1472 getCurrentRepresentation().setData(data);
1473 }
1474
1475
1476 /**
1477 * Updates the data with a given tag. All data is removed that is prefixed
1478 * with the given tag. Then a new line is added (if not null).
1479 *
1480 * Note that passing newData with null is the equivelant of removing tag
1481 * lines.
1482 *
1483 * @param tag
1484 * The prefix of the data lines to remove. Must be larger the
1485 * zero and not null.
1486 *
1487 * @param newData
1488 * The new line to add. Can be null - for not adding anything.
1489 *
1490 * @throws IllegalArgumentException
1491 * If tag is null.
1492 *
1493 * @throws NullPointerException
1494 * If tag is empty.
1495 *
1496 * @see #removeData(String)
1497 *
1498 */
1499 protected void updateData(String tag, String newData) {
1500 if (tag == null)
1501 throw new NullPointerException("tag");
1502 else if (tag.length() == 0)
1503 throw new IllegalArgumentException("tag is empty");
1504
1505 // Get current data
1506 List<String> data = getCurrentRepresentation().getData();
1507
1508 if (data != null) {
1509 for (int i = 0; i < data.size(); i++) {
1510 String str = data.get(i);
1511 if (str != null && str.startsWith(tag)) {
1512 data.remove(i);
1513 }
1514 }
1515 }
1516
1517 if (newData != null) {
1518 if (data != null)
1519 data.add(newData);
1520 else {
1521 data = new LinkedList<String>();
1522 data.add(newData);
1523 getCurrentRepresentation().setData(data);
1524
1525 }
1526 }
1527 }
1528
1529 public boolean containsData(String str) {
1530 assert(str != null);
1531 if (getCurrentRepresentation().getData() != null)
1532 return getCurrentRepresentation().getData().contains(str);
1533 return false;
1534 }
1535
1536 public boolean containsDataTrimmedIgnoreCase(String str) {
1537 assert(str != null);
1538 if (getCurrentRepresentation().getData() != null) {
1539 for (String data : getCurrentRepresentation().getData()) {
1540 if (data != null && data.trim().equalsIgnoreCase(str)) {
1541 return true;
1542 }
1543 }
1544 }
1545
1546 return false;
1547 }
1548
1549 /**
1550 * Sets the link for this widget.
1551 *
1552 * @param link
1553 * The new frame link. Can be null (for no link)
1554 *
1555 */
1556 public void setLink(String link) {
1557 // Make sure the link is redrawn when a link is added
1558 if (link == null)
1559 invalidateLink();
1560 getSource().setLink(link);
1561 if (link != null)
1562 invalidateLink();
1563 }
1564
1565 public void setBackgroundColor(Color c) {
1566 getSource().setBackgroundColor(c);
1567 }
1568
1569 /**
1570 * @return The link for this widget. Null if none.
1571 */
1572 public String getLink() {
1573 return _textRepresentation.getLink();
1574 }
1575
1576 /**
1577 * <b>Note:</b> That if the widget has no parent (e.g. the widget is a
1578 * free-item) then the absolute link returned will be for the frameset of
1579 * the current frame.
1580 *
1581 * @return The absolute link for this item. Null if there is no link, or if
1582 * there is no parent for this widget and the current frame is
1583 * unavailable.
1584 *
1585 */
1586 public String getAbsoluteLink() {
1587
1588 // Note: cannot return the source absolute link since it does not have
1589 // a parent ... thus must manually format link
1590
1591 String link = getLink();
1592
1593 if (link == null || link.length() == 0)
1594 return null;
1595
1596 if (FrameIO.isPositiveInteger(link)) { // relative - convert to
1597 // absolute
1598
1599 // Get the frameset of this item
1600 Frame parent = getParentFrame();
1601 if (parent == null)
1602 parent = DisplayIO.getCurrentFrame();
1603 if (parent == null)
1604 return null;
1605
1606 String framesetName = parent.getFramesetName();
1607
1608 if (framesetName == null)
1609 return null;
1610
1611 return framesetName + link;
1612
1613 } else if (FrameIO.isValidFrameName(link)) { // already absolute
1614 return link;
1615 }
1616
1617 return null;
1618 }
1619
1620 /**
1621 * Sets the border color for the widget.
1622 * That is, for the source (so it is remembered) and also for all the
1623 * corners/edges.
1624 *
1625 * @param c
1626 * The color to set.
1627 */
1628 public void setWidgetEdgeColor(Color c) {
1629 for (Item i : _expediteeItems) i.setColor(c);
1630 // Above indirectly invokes setSourceBorderColor accordingly
1631 }
1632
1633 /**
1634 * Sets the thickness of the widget edge.
1635 *
1636 * @see Item#setThickness(float)
1637 *
1638 * @param thickness
1639 * The new thickness to set.
1640 */
1641 public void setWidgetEdgeThickness(float thickness) {
1642 for (Item i : _expediteeItems) i.setThickness(thickness);
1643// Above indirectly invokes setSourceThickness accordingly
1644 }
1645
1646 // TODO: Maybe rename setSource* .. to update* ... These should actually be friendly!
1647 public void setSourceColor(Color c) {
1648 _textRepresentation.setColor(c);
1649 }
1650
1651 public void setSourceBorderColor(Color c) {
1652 _textRepresentation.setBorderColor(c);
1653 }
1654
1655 public void setSourceFillColor(Color c) {
1656 _textRepresentation.setFillColor(c);
1657 }
1658
1659 public void setSourceThickness(float newThickness, boolean setConnected) {
1660 _textRepresentation.setThickness(newThickness, setConnected);
1661 }
1662
1663 public void setSourceData(List<String> data) {
1664 _textRepresentation.setData(data);
1665 }
1666
1667 protected Point getOrigin() {
1668 return _d1.getPosition(); // BROOK: This flips around ... the origin can be any point
1669 }
1670
1671 protected Item getFirstCorner() {
1672 return _d1;
1673 }
1674
1675 public void setAnchorBottom(Float anchor) {
1676 setPosition(getX(),
1677 Math.round(FrameGraphics.getMaxFrameSize().height - anchor - getHeight()));
1678 getSource().setAnchorBottom(anchor);
1679 }
1680
1681 public void setAnchorRight(Float anchor) {
1682 setPosition(
1683 Math.round(FrameGraphics.getMaxFrameSize().width - anchor - getWidth()),
1684 getY());
1685 getSource().setAnchorRight(anchor);
1686 }
1687}
Note: See TracBrowser for help on using the repository browser.