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

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

Test commit - corrected a spelling mistake on an error message

File size: 23.6 KB
Line 
1package org.expeditee.items;
2
3import java.awt.Color;
4import java.awt.Component;
5import java.awt.Container;
6import java.awt.Graphics;
7import java.awt.Point;
8import java.lang.reflect.Constructor;
9import java.util.ArrayList;
10import java.util.Collections;
11import java.util.LinkedList;
12import java.util.List;
13
14import javax.swing.JComponent;
15import javax.swing.SwingUtilities;
16
17import org.expeditee.gui.Browser;
18import org.expeditee.gui.DisplayIO;
19import org.expeditee.gui.Frame;
20import org.expeditee.gui.FrameGraphics;
21
22/**
23 * The bridge between swing space and expeditee space
24 *
25 * @author Brook
26 *
27 */
28public abstract class InteractiveWidget {
29
30 protected JComponent _component;
31
32 /** A widget is comprised of dots and lines that basically form a rectangle */
33 private WidgetCorner _d1, _d2, _d3, _d4;
34
35 private WidgetEdge _l1, _l2, _l3, _l4;
36
37 /*
38 * GUIDE: l1 d1-------d2 | | l4 | X | 12 | | d4-------d3 13
39 */
40 private List<Item> _items; // used for quickly returning item list
41
42 // Widget size restrictions
43 private int _minWidth = 50;
44
45 private int _minHeight = 50;
46
47 private int _maxWidth = 300;
48
49 private int _maxHeight = 300;
50
51 // The Expeditee item that is used for saving widget state in expiditee
52 // world
53 private Text _source;
54
55 // A flag for signifying whether the swing components are ready to paint.
56 // If the swing components has not been layed out, if they are painted they
57 // will
58 // not draw in the correct positions.
59 private boolean _isReadyToPaint = false;
60
61 /**
62 * Creates a InteractiveWidget from a text item.
63 *
64 * @param source
65 * Must not be null, first line of text used - which the format
66 * must be as follows: "@iw <<widget_class_name>> [<<width>>] [<<height>>] [: [<<arg1>>] [<<arg2>>]
67 * [...]]".
68 *
69 * e.g: "@iw org.expeditee.items.SampleWidget1 100 20 : 2" creates a
70 * SampleWidget1 with width = 100 and height = 20 with 1 argument = "2"
71 *
72 * @return An InteractiveWidget instance. Never null.
73 *
74 * @throws NullPointerException
75 * if source is null
76 *
77 * @throws IllegalArgumentException
78 * if source's text is in the incorrect format or if source's
79 * parent is null
80 *
81 * @throws InteractiveWidgetNotAvailableException
82 * If the given widget class name in the source text doesn't
83 * exist or not an InteractiveWidget or the widget does not
84 * supply a valid constructor for creation.
85 *
86 * Note: could throw other exceptions depending on the type of widget being
87 * instantainiated. source must have a parent
88 *
89 * class names beginning with $, the $ will be replaced with
90 * "org.expeditee.items."
91 */
92 public static InteractiveWidget CreateWidget(Text source)
93 throws InteractiveWidgetNotAvailableException {
94
95 if (source == null)
96 throw new NullPointerException("source");
97 if (source.getParent() == null)
98 throw new IllegalArgumentException(
99 "source's parent is null, InteractiveWidget's must be created from Text items with non-null parents");
100
101 String TAG = ItemUtils.GetTag(ItemUtils.TAG_IWIDGET);
102
103 String text = source.getTextNoList();
104 if (text == null)
105 throw new IllegalArgumentException("source does not have any text");
106
107 int index = text.indexOf(':'); // used for signifying start of
108 // arguments
109 if (index == 0)
110 throw new IllegalArgumentException("Source text must begin with \""
111 + TAG + "\"");
112
113 String[] tokens = (index == -1) ? text.split("\\s+") : text.substring(
114 0, index).split(" ");
115
116 // Check starts with the widget tag
117 if (tokens.length < 1 || !tokens[0].equals(TAG))
118 throw new IllegalArgumentException("Source text must begin with \""
119 + TAG + "\"");
120
121 int width = -1, height = -1;
122
123 if (tokens.length < 2)
124 throw new IllegalArgumentException(
125 "Missing widget class name in source text");
126
127 try {
128
129 if (tokens.length >= 3) { // parse optional width
130 width = Integer.parseInt(tokens[2]);
131 width = (width <= 0) ? width = -1 : width;
132 }
133
134 if (tokens.length >= 4) { // parse optional height
135 height = Integer.parseInt(tokens[3]);
136 height = (height <= 0) ? height = -1 : height;
137 }
138
139 } catch (NumberFormatException nfe) {
140 throw new IllegalArgumentException(
141 "Bad width or height given in source text", nfe);
142 }
143
144 if (tokens.length > 4)
145 throw new IllegalArgumentException(
146 "to many arguments given before \":\" in source text");
147
148 String classname = tokens[1];
149 if (classname.charAt(0) == '$')
150 classname = "org.expeditee.items." + classname.substring(1);
151 // Attempt to locate the class using reflection
152 Class<?> iwclass = findIWidgetClass(classname);
153
154 if (iwclass == null) // ensure it exists
155 throw new InteractiveWidgetNotAvailableException(classname
156 + " does not exist or is not an InteractiveWidget");
157
158 // Extract out the parameters - if any
159 String[] args = null;
160 if (index > 0) { // index of the first ":"
161 args = (text.length() == (index + 1)) ? null : parseArgs(text
162 .substring(index + 1));
163 }
164
165 InteractiveWidget inst = null;
166 try {
167 // Instantainiate the widget - passing the params
168 Class parameterTypes[] = new Class[] { Text.class, String[].class };
169 Constructor ct = iwclass.getConstructor(parameterTypes);
170
171 Object arglist[] = new Object[] { source, args };
172
173 inst = (InteractiveWidget) ct.newInstance(arglist);
174 } catch (Exception e) {
175 throw new InteractiveWidgetNotAvailableException(
176 "Failed to create instance via reflection: " + e.toString(),
177 e);
178 }
179
180 // Use default dimensions if not provided (or provided as negitive
181 // values)
182 if (width <= 0)
183 width = inst.getWidth();
184 if (height <= 0)
185 height = inst.getHeight();
186
187 inst.setSize(width, height);
188
189 return inst;
190 }
191
192 /**
193 * Locates the class from the classname of an InteractiveWidget class
194 *
195 * @param classname
196 * The name of the class to search
197 * @return Null if doesn't exist or not an InteractiveWidget
198 */
199 private static Class findIWidgetClass(String classname) {
200 try {
201 Class c = Class.forName(classname); // attempt to find the class
202
203 // If one is found, ensure that it is a descendant of an
204 // InteractiveWidget
205 for (Class superclass = c.getSuperclass(); superclass != null
206 && superclass != Item.class; superclass = superclass
207 .getSuperclass()) {
208 if (superclass == InteractiveWidget.class)
209 return c;
210 }
211
212 } catch (ClassNotFoundException e) {
213 }
214
215 // Doesn't exist or not an InteractiveWidget
216 return null;
217 }
218
219 /**
220 * Using Microsofts commandline convention: Args seperated with white
221 * spaces. Options with white spaces enclosed with quotes. Args with quotes
222 * must be double quoted args1 args2=sfasas args3="option with spaces"
223 * arg""4""
224 *
225 * @param args
226 * Null and empty excepted
227 * @return An array of args. null if none provided
228 */
229 static String[] parseArgs(String args) {
230
231 if (args == null)
232 return null;
233
234 args = args.trim();
235 if (args.length() == 0)
236 return null;
237
238 List<String> vargs = new LinkedList<String>();
239 StringBuilder sb = new StringBuilder();
240 boolean quoteOn = false;
241 for (int i = 0; i < args.length(); i++) {
242
243 char c = args.charAt(i);
244 if (c == ' ' && !quoteOn) {
245
246 // Extract arg
247 vargs.add(sb.toString());
248 sb = new StringBuilder();
249
250 // Consume white spaces
251 while (args.charAt(++i) == ' ' && i < args.length()) {
252 }
253 i--;
254
255 } else if (c == '\"') {
256 // If double qouted
257 if (args.length() >= (i + 2) && args.charAt(i + 1) == '\"') {
258
259 sb.append(c); // add escaped
260 i++;
261
262 } else {
263 quoteOn = !quoteOn;
264 }
265
266 } else {
267 sb.append(c);
268 }
269 }
270
271 if (sb.length() > 0)
272 vargs.add(sb.toString());
273
274 if (vargs.size() == 0)
275 return null;
276 else
277 return vargs.toArray(new String[vargs.size()]);
278 }
279
280 /**
281 * Reverse of parseArgs
282 *
283 * @return Null if args is null or empty / all whitespace
284 */
285 static String formatArgs(String[] args) {
286 if (args == null)
287 return null;
288
289 StringBuilder sb = new StringBuilder();
290
291 for (String s : args) {
292 if (s == null)
293 continue;
294
295 // Escape quotes
296 StringBuilder formatted = new StringBuilder(s.replaceAll("\"",
297 "\"\""));
298
299 // Encapsulate spaces
300 int index = formatted.indexOf(" ");
301 if (index >= 0) {
302 formatted.insert(index, "\"");
303 formatted.append('\"');
304 }
305
306 if (sb.length() > 0)
307 sb.append(' ');
308 sb.append(formatted);
309 }
310
311 return sb.length() > 0 ? sb.toString() : null;
312 }
313
314 /**
315 * Arguments represent state. They are used for saving, loading, creating
316 * and cloning Special formatting is done for you.
317 *
318 * @param isCopy
319 * If true, then the requested arguments returned should
320 * represent a copy of this instance Otherwise the arguments
321 * returned should represent this instance. This gives the widget
322 * the chance to allocate new/duplicate resources if they need
323 * to.
324 *
325 * @return Can be null for no params.
326 */
327 protected abstract String[] getArgs(boolean isCopy);
328
329 /**
330 * Constructor
331 *
332 * @param source
333 * Must not be null. Neither must it's parent
334 * @param component
335 * Must not be null
336 */
337 protected InteractiveWidget(Text source, JComponent component,
338 int minWidth, int maxWidth, int minHeight, int maxHeight) {
339
340 if (component == null)
341 throw new NullPointerException("component");
342 if (source == null)
343 throw new NullPointerException("source");
344 if (source.getParent() == null)
345 throw new IllegalArgumentException(
346 "source's parent is null, InteractiveWidget's must be created from Text items with non-null parents");
347
348 _component = component;
349 _source = source;
350
351 _minWidth = minWidth;
352
353 if (maxWidth < _minWidth && maxWidth >= 0)
354 throw new IllegalArgumentException(
355 "maxWidth smaller than the min Width");
356 _maxWidth = maxWidth;
357
358 _minHeight = minHeight;
359 if (maxHeight < _minHeight && maxHeight >= 0)
360 throw new IllegalArgumentException(
361 "maxHeight smaller than the min Height");
362 _maxHeight = maxHeight;
363
364 int x = source.getX();
365 int y = source.getY();
366 int width = (_minWidth < 0) ? 10 : _minWidth;
367 int height = (_minHeight < 0) ? 10 : _minHeight;
368
369 Frame idAllocator = _source.getParent();
370
371 // create WidgetCorners
372 _d1 = new WidgetCorner(x, y, idAllocator.getNextItemID(), this);
373 _d2 = new WidgetCorner(x + width, y, idAllocator.getNextItemID(), this);
374 _d3 = new WidgetCorner(x + width, y + height, idAllocator
375 .getNextItemID(), this);
376 _d4 = new WidgetCorner(x, y + height, idAllocator.getNextItemID(), this);
377
378 // create WidgetEdges
379 _l1 = new WidgetEdge(_d1, _d2, idAllocator.getNextItemID());
380 _l2 = new WidgetEdge(_d2, _d3, idAllocator.getNextItemID());
381 _l3 = new WidgetEdge(_d3, _d4, idAllocator.getNextItemID());
382 _l4 = new WidgetEdge(_d4, _d1, idAllocator.getNextItemID());
383
384 _items = new ArrayList<Item>(8); // Note: order important
385 _items.add(_d1);
386 _items.add(_d2);
387 _items.add(_d3);
388 _items.add(_d4);
389 _items.add(_l1);
390 _items.add(_l2);
391 _items.add(_l3);
392 _items.add(_l4);
393 }
394
395 /**
396 * This is for consistancy and to make the code clearer.
397 *
398 * @return the corner chosen to listen for events from.
399 */
400 private WidgetCorner getEventCorner() {
401 return _d4;
402 }
403
404 /**
405 * @return A copy of this widget
406 */
407 public final InteractiveWidget copy()
408 throws InteractiveWidgetNotAvailableException {
409
410 Text t = _source.copy();
411 String clonedAnnotation = getAnnotationString(true);
412 _source.setText(clonedAnnotation);
413 return InteractiveWidget.CreateWidget(t);
414 }
415
416 /**
417 * @return The top-most class name including the package.
418 */
419 protected abstract String getTopMostClassName();
420
421 /**
422 * Note updates the source text with current state info
423 *
424 * @return The Text item that this widget was created from.
425 */
426 public Text getSource() {
427
428 // Build the annotation string such that it represents this widgets
429 // current state
430 String newAnnotation = getAnnotationString(false);
431
432 // Set the new text
433 _source.setText(newAnnotation);
434
435 return _source;
436 }
437
438 /**
439 * @param copiedVersion
440 * True to return a copied representation of this instance. False
441 * to return this instances current state version.
442 *
443 * @return The expeditee anotation string.
444 */
445 private String getAnnotationString(boolean copiedVersion) {
446
447 // Create tag and append classname
448 StringBuilder sb = new StringBuilder(ItemUtils
449 .GetTag(ItemUtils.TAG_IWIDGET));
450 sb.append(' ');
451 sb.append(getTopMostClassName());
452
453 // Append size information if needed (not an attibute of text items)
454 if (!isFixedSize()) {
455 sb.append(' ');
456 sb.append(getWidth());
457 sb.append(' ');
458 sb.append(getHeight());
459 }
460
461 // Append arguments if any
462 String stateArgs = InteractiveWidget.formatArgs(getArgs(copiedVersion));
463 if (stateArgs != null) {
464 sb.append(':');
465 sb.append(stateArgs);
466 }
467
468 return sb.toString();
469 }
470
471 /**
472 * Clamped to current min/max width/height.
473 *
474 * @param width
475 * @param height
476 */
477 public void setSize(int width, int height) {
478
479 // Clamp
480 if (width < _minWidth && _minWidth >= 0)
481 width = _minWidth;
482 else if (width > _maxWidth && _maxWidth >= 0)
483 width = _maxWidth;
484
485 if (height < _minHeight && _minHeight >= 0)
486 height = _minHeight;
487 else if (height > _maxHeight && _maxHeight >= 0)
488 height = _maxHeight;
489
490 boolean vfloating[] = new boolean[] { _d1.isFloating(),
491 _d2.isFloating(), _d3.isFloating(), _d4.isFloating() };
492
493 _d1.setFloating(true);
494 _d2.setFloating(true);
495 _d3.setFloating(true);
496 _d4.setFloating(true);
497
498 int x = _d1.getX() + width;
499 int y = _d1.getY() + height;
500 _d2.setX(x);
501 _d3.setX(x);
502 _d3.setY(y);
503 _d4.setY(y);
504
505 _d1.setFloating(vfloating[0]);
506 _d2.setFloating(vfloating[1]);
507 _d3.setFloating(vfloating[2]);
508 _d4.setFloating(vfloating[3]);
509
510 }
511
512 public void setPosition(int x, int y) {
513
514 boolean vfloating[] = new boolean[] { _d1.isFloating(),
515 _d2.isFloating(), _d3.isFloating(), _d4.isFloating() };
516
517 int width = getWidth();
518 int height = getHeight();
519
520 _d1.setFloating(true);
521 _d2.setFloating(true);
522 _d3.setFloating(true);
523 _d4.setFloating(true);
524
525 _d1.setPosition(x, y);
526 _d2.setPosition(x + width, y);
527 _d3.setPosition(x + width, y + height);
528 _d4.setPosition(x, y + height);
529
530 _d1.setFloating(vfloating[0]);
531 _d2.setFloating(vfloating[1]);
532 _d3.setFloating(vfloating[2]);
533 _d4.setFloating(vfloating[3]);
534
535 }
536
537 private boolean _settingPositionFlag = false; // used for recursion
538
539 /**
540 * to be called from corners only
541 *
542 * @param src
543 * @param x
544 * @param y
545 * @return False if need to call super.setPosition
546 */
547 boolean setPositions(WidgetCorner src, int x, int y) {
548
549 if (_settingPositionFlag)
550 return false;
551 _settingPositionFlag = true;
552
553 // Check to see if the widget is fully being picked up
554 boolean isAllPickedUp = (_d1.isFloating() && _d2.isFloating()
555 && _d3.isFloating() && _d4.isFloating());
556
557 int newX = x;
558
559 // X Positions
560 if (src == _d1 || src == _d4) {
561
562 if (src == _d1 && isAllPickedUp)
563 _d1.setX(newX);
564 else if (src == _d4 && isAllPickedUp)
565 _d4.setX(newX);
566 else {
567 // Check min X constraint
568 if (_minWidth >= 0) {
569 if ((_d2.getX() - x) < _minWidth) {
570 newX = _d2.getX() - _minWidth;
571 }
572 }
573 // Check max X constraint
574 if (_maxWidth >= 0) {
575 if ((_d2.getX() - x) > _maxWidth) {
576 newX = _d2.getX() - _maxWidth;
577 }
578 }
579
580 if (!(src == _d4 && _d1.isFloating() && _d4.isFloating()))
581 _d1.setX(newX);
582 if (!(src == _d1 && _d4.isFloating() && _d1.isFloating()))
583 _d4.setX(newX);
584 }
585
586 } else if (src == _d2 || src == _d3) {
587
588 if (src == _d2 && isAllPickedUp)
589 _d2.setX(newX);
590 else if (src == _d3 && isAllPickedUp)
591 _d3.setX(newX);
592 else {
593 // Check min X constraint
594 if (_minWidth >= 0) {
595 if ((x - _d1.getX()) < _minWidth) {
596 newX = _d1.getX() + _minWidth;
597 }
598 }
599 // Check max X constraint
600 if (_maxWidth >= 0) {
601 if ((x - _d1.getX()) > _maxWidth) {
602 newX = _d1.getX() + _maxWidth;
603 }
604 }
605
606 if (!(src == _d3 && _d2.isFloating() && _d3.isFloating()))
607 _d2.setX(newX);
608 if (!(src == _d2 && _d3.isFloating() && _d2.isFloating()))
609 _d3.setX(newX);
610 }
611
612 }
613
614 int newY = y;
615
616 // Y Positions
617 if (src == _d1 || src == _d2) {
618
619 if (src == _d1 && isAllPickedUp)
620 _d1.setY(newY);
621 else if (src == _d2 && isAllPickedUp)
622 _d2.setY(newY);
623 else {
624 // Check min Y constraint
625 if (_minHeight >= 0) {
626 if ((_d4.getY() - y) < _minHeight) {
627 newY = _d4.getY() - _minHeight;
628 }
629 }
630 // Check max Y constraint
631 if (_maxHeight >= 0) {
632 if ((_d4.getY() - y) > _maxHeight) {
633 newY = _d4.getY() - _maxHeight;
634 }
635 }
636
637 if (!(src == _d2 && _d1.isFloating() && _d2.isFloating()))
638 _d1.setY(newY);
639 if (!(src == _d1 && _d2.isFloating() && _d1.isFloating()))
640 _d2.setY(newY);
641
642 }
643
644 } else if (src == _d3 || src == _d4) {
645
646 if (src == _d3 && isAllPickedUp)
647 _d3.setY(newY);
648 else if (src == _d4 && isAllPickedUp)
649 _d4.setY(newY);
650 else {
651 // Check min Y constraint
652 if (_minHeight >= 0) {
653 if ((y - _d1.getY()) < _minHeight) {
654 newY = _d1.getY() + _minHeight;
655 }
656 }
657 // Check max Y constraint
658 if (_maxHeight >= 0) {
659 if ((y - _d1.getY()) > _maxHeight) {
660 newY = _d1.getY() + _maxHeight;
661 }
662 }
663
664 if (!(src == _d4 && _d3.isFloating() && _d4.isFloating()))
665 _d3.setY(newY);
666 if (!(src == _d3 && _d4.isFloating() && _d3.isFloating()))
667 _d4.setY(newY);
668 }
669 }
670
671 // Update source text position so position is remembered from loading
672 int newTextX = getX();
673 int newTextY = getY();
674 if (_source.getX() != newTextX || _source.getY() != newTextY)
675 _source.setPosition(newTextX, newTextY);
676
677 _settingPositionFlag = false;
678 return true;
679 }
680
681 public int getX() {
682 return Math.min(_d1.getX(), _d2.getX());
683 }
684
685 public int getY() {
686 return Math.min(_d1.getY(), _d4.getY());
687 }
688
689 public int getWidth() {
690 return Math.abs(_d2.getX() - _d1.getX());
691 }
692
693 public int getHeight() {
694 return Math.abs(_d4.getY() - _d1.getY());
695 }
696
697 /**
698 * The order of the items in the list is as specified: _d1 _d2 _d3 _d4 _l1
699 * _l2 _l3 _l4
700 *
701 * @return All of the Expeditee items that form the bounderies of this
702 * widget in an unmodifiable list
703 */
704 public List<Item> getItems() {
705 return Collections.unmodifiableList(_items);
706 }
707
708 public JComponent getComponant() {
709 return _component;
710 }
711
712 public void onParentStateChanged(ItemParentStateChangedEvent e) {
713 switch (e.getEventType()) {
714
715 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED:
716 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED_VIA_OVERLAY:
717 case ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN:
718 if (_component.getParent() != null) {
719 _component.getParent().remove(_component);
720 }
721 break;
722
723 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED:
724 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY:
725 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN:
726 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY:
727 if (_component.getParent() == null) {
728 addJComponantToFrame(e);
729 }
730 break;
731
732 }
733 }
734
735 protected void addJComponantToFrame(ItemParentStateChangedEvent e) {
736
737 if ((e.getEventType() == ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY || e
738 .getEventType() == ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY)
739 && e.getOverlayLevel() == Item.PERMISSION_NONE) {
740 return; // item belongs to a non-active overlay
741 }
742
743 if (_component.getParent() == null) {
744
745 if (Browser._theBrowser != null) {
746 // Due to precaching - before adding physical swing
747 // componant must check to see that this widget belongs to a
748 // frame that is
749 // considered current. If the widget is shown however this does
750 // not apply -
751 // since it has been explicitly made clear the the widget is
752 // shown.
753 if (e.getEventType() == ItemParentStateChangedEvent.EVENT_TYPE_SHOWN
754 || e.getEventType() == ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY
755 || e.getSource() == DisplayIO.getCurrentFrame()) {
756 Browser._theBrowser.getContentPane().add(_component);
757 layout(_component);
758 }
759
760 } else { // if widgets exist on startup frame this will occur
761
762 synchronized (_widgetsToAddLater) {
763 _widgetsToAddLater.add(new WidgetInfo(this, e));
764 }
765 SwingUtilities.invokeLater(new AddToFrameLater());
766 }
767
768 }
769
770 }
771
772 /**
773 * Used for passing info to the swing thread
774 *
775 * @author Brook Novak
776 */
777 class WidgetInfo {
778
779 WidgetInfo(InteractiveWidget widget, ItemParentStateChangedEvent e) {
780 _widget = widget;
781 _e = e;
782 }
783
784 InteractiveWidget _widget;
785
786 ItemParentStateChangedEvent _e;
787 }
788
789 /**
790 * Must be able to add widgets on first loaded frame: these are loaded
791 * before the browser singleton is made available.
792 */
793 private static List<WidgetInfo> _widgetsToAddLater = new LinkedList<WidgetInfo>();
794
795 /**
796 * Ensures widgets are added correctly to first loaded frame
797 */
798 class AddToFrameLater implements Runnable {
799 public void run() {
800 if (!_widgetsToAddLater.isEmpty()) {
801 List<WidgetInfo> tmp = null;
802 synchronized (_widgetsToAddLater) {
803 tmp = new LinkedList<WidgetInfo>(_widgetsToAddLater);
804 }
805 _widgetsToAddLater.clear();
806 for (WidgetInfo iwi : tmp) {
807 iwi._widget.addJComponantToFrame(iwi._e);
808 }
809 }
810 }
811 }
812
813 /**
814 * Due to absolute positioning...
815 *
816 * @param parent
817 */
818 private void layout(Component parent) {
819
820 parent.doLayout();
821
822 if (parent instanceof Container) {
823 for (Component c : ((Container) parent).getComponents()) {
824
825 if (c instanceof Container)
826 layout(c);
827 else
828 c.doLayout();
829 }
830 }
831
832 _isReadyToPaint = true;
833 }
834
835 public void onBoundsChanged() {
836
837 _component.setBounds(getX(), getY(), getWidth(), getHeight());
838
839 }
840
841 /**
842 * Paints the widget excluding the boundries. That is, the Swing graphics
843 *
844 * @param g
845 */
846 public void paint(Graphics g) {
847
848 if (!_isReadyToPaint) {
849 layout(_component);
850 }
851
852 Point loc = _component.getLocation();
853 g.translate(loc.x, loc.y - 1);
854 _component.paint(g);
855 g.translate(-loc.x, -(loc.y - 1));
856
857 }
858
859 /**
860 * Called from the widgets corners: Whenever the corners are repainted and
861 * the widget is floating (e.g. currently picked up / rubberbanding) then a
862 * shaded area is drawn instead of the actual widget so the manipulation of
863 * the widget is as smooth as possible. The shaded area is only draw once so
864 * it selects one of the 4 possible notifiers to be the actual trigger to
865 * draw the shaded area
866 *
867 * @param g
868 * @param notifier
869 */
870 void paint(Graphics g, WidgetCorner notifier) {
871 if (_component.getParent() == null) {
872 // Note that frames with @f may want to paint the widgets so do not
873 // paint over the widget interface in these cases: must only
874 // paint if an object is floating
875 if (notifier == _d4
876 && (_d1.isFloating() || _d2.isFloating()
877 || _d3.isFloating() || _d4.isFloating())) {
878 g.setColor(Color.DARK_GRAY);
879 g.fillRect(getX(), getY(), getWidth(), getHeight());
880 }
881 }
882 }
883
884 /**
885 * @return True if this widget cannot be resized in either directions
886 */
887 public boolean isFixedSize() {
888 return this._minHeight == this._maxHeight
889 && this._minWidth == this._maxWidth && this._minHeight >= 0
890 && this._minWidth >= 0;
891 }
892
893 private static boolean _isExepiteeHighlightingEnabled = true;
894
895 /**
896 * Enables/Disables highlighting of all widgets - if the enable value has
897 * changed the current frame will repaint.
898 *
899 * The purpose of this is so that widgets can have an extra context: no
900 * expeditee highlighting if focus is on a swing componet.
901 *
902 * @param enable
903 * Set to true to enable painting, false to disable painting.
904 */
905 public static void enableExepiteeHighlighting(boolean enable) {
906 if (_isExepiteeHighlightingEnabled != enable) {
907 _isExepiteeHighlightingEnabled = enable;
908 FrameGraphics.Repaint();
909 }
910 }
911
912 public static boolean isExepiteeHighlightingEnabled() {
913 return _isExepiteeHighlightingEnabled;
914 }
915
916}
Note: See TracBrowser for help on using the repository browser.