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

Last change on this file since 539 was 539, checked in by jts21, 11 years ago

Override that event in JfxBrowser to allow navigation to a new URL

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