1 package thirdparty.javaworld;
2
3 import javax.swing.*;
4 import javax.swing.event.DocumentEvent;
5 import javax.swing.text.*;
6 import javax.swing.text.html.HTML;
7 import javax.swing.text.html.HTMLDocument;
8 import javax.swing.text.html.StyleSheet;
9 import java.awt.*;
10 import java.awt.event.MouseEvent;
11 import java.awt.event.MouseListener;
12 import java.awt.event.MouseMotionListener;
13 import java.awt.image.ImageObserver;
14 import java.io.BufferedInputStream;
15 import java.io.ByteArrayOutputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.net.MalformedURLException;
19 import java.net.URL;
20 import java.util.Dictionary;
21
22
23
24
25
26
27
28
29 public class ClasspathImageView extends View implements ImageObserver, MouseListener, MouseMotionListener {
30
31
32
33 public static final String
34 TOP = "top",
35 TEXTTOP = "texttop",
36 MIDDLE = "middle",
37 ABSMIDDLE = "absmiddle",
38 CENTER = "center",
39 BOTTOM = "bottom";
40
41
42
43
44
45
46
47
48 public ClasspathImageView(Element elem) {
49 super(elem);
50 initialize(elem);
51 StyleSheet sheet = getStyleSheet();
52 attr = sheet.getViewAttributes(this);
53 }
54
55
56 private void initialize(Element elem) {
57 synchronized (this) {
58 loading = true;
59 fWidth = fHeight = 0;
60 }
61 int width = 0;
62 int height = 0;
63 boolean customWidth = false;
64 boolean customHeight = false;
65 try {
66 fElement = elem;
67
68
69 AttributeSet attr = elem.getAttributes();
70 if (isURL()) {
71 URL src = getSourceURL();
72 if (src != null) {
73 Dictionary cache = (Dictionary) getDocument().getProperty(IMAGE_CACHE_PROPERTY);
74 if (cache != null) {
75 fImage = (Image) cache.get(src);
76 } else {
77 fImage = Toolkit.getDefaultToolkit().getImage(src);
78 }
79 }
80 }
81 else
82 {
83
84
85 String src = (String) fElement.getAttributes().getAttribute(HTML.Attribute.SRC);
86
87
88 URL imageUrl = ClasspathImageView.class.getResource(src);
89
90 fImage = Toolkit.getDefaultToolkit().createImage(imageUrl);
91
92
93
94
95
96
97
98
99
100 }
101
102
103 height = getIntAttr(HTML.Attribute.HEIGHT, -1);
104 customHeight = (height > 0);
105 if (!customHeight && fImage != null) {
106 height = fImage.getHeight(this);
107 }
108 if (height <= 0) {
109 height = DEFAULT_HEIGHT;
110 }
111
112 width = getIntAttr(HTML.Attribute.WIDTH, -1);
113 customWidth = (width > 0);
114 if (!customWidth && fImage != null) {
115 width = fImage.getWidth(this);
116 }
117 if (width <= 0) {
118 width = DEFAULT_WIDTH;
119 }
120
121
122 if (fImage != null) {
123 if (customWidth && customHeight) {
124 Toolkit.getDefaultToolkit().prepareImage(fImage, height,
125 width, this);
126 } else {
127 Toolkit.getDefaultToolkit().prepareImage(fImage, -1, -1,
128 this);
129 }
130 }
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147 } finally {
148 synchronized (this) {
149 loading = false;
150 if (customWidth || fWidth == 0) {
151 fWidth = width;
152 }
153 if (customHeight || fHeight == 0) {
154 fHeight = height;
155 }
156 }
157 }
158 }
159
160
161
162
163 private boolean isURL() {
164 String src =
165 (String) fElement.getAttributes().getAttribute(HTML.Attribute.SRC);
166 return src.toLowerCase().startsWith("file") ||
167 src.toLowerCase().startsWith("http");
168 }
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203 private void waitForImage() throws InterruptedException {
204 int w = fImage.getWidth(this);
205 int h = fImage.getHeight(this);
206
207 while (true) {
208 int flags = Toolkit.getDefaultToolkit().checkImage(fImage, w, h, this);
209
210 if (((flags & ERROR) != 0) || ((flags & ABORT) != 0)) {
211 throw new InterruptedException();
212 } else if ((flags & (ALLBITS | FRAMEBITS)) != 0) {
213 return;
214 }
215 Thread.sleep(10);
216
217 }
218 }
219
220
221
222
223
224
225
226 public AttributeSet getAttributes() {
227 return attr;
228 }
229
230
231
232
233 boolean isLink() {
234
235
236 AttributeSet anchorAttr = (AttributeSet)
237 fElement.getAttributes().getAttribute(HTML.Tag.A);
238 if (anchorAttr != null) {
239 return anchorAttr.isDefined(HTML.Attribute.HREF);
240 }
241 return false;
242 }
243
244
245
246
247 int getBorder() {
248 return getIntAttr(HTML.Attribute.BORDER, isLink() ? DEFAULT_BORDER : 0);
249 }
250
251
252
253
254 int getSpace(int axis) {
255 return getIntAttr(axis == X_AXIS ? HTML.Attribute.HSPACE : HTML.Attribute.VSPACE,
256 0);
257 }
258
259
260
261
262 Color getBorderColor() {
263 StyledDocument doc = (StyledDocument) getDocument();
264 return doc.getForeground(getAttributes());
265 }
266
267
268
269
270 float getVerticalAlignment() {
271 String align = (String) fElement.getAttributes().getAttribute(HTML.Attribute.ALIGN);
272 if (align != null) {
273 align = align.toLowerCase();
274 if (align.equals(TOP) || align.equals(TEXTTOP)) {
275 return 0.0f;
276 } else if (align.equals(this.CENTER) || align.equals(MIDDLE)
277 || align.equals(ABSMIDDLE)) {
278 return 0.5f;
279 }
280 }
281 return 1.0f;
282 }
283
284 boolean hasPixels(ImageObserver obs) {
285 return fImage != null && fImage.getHeight(obs) > 0
286 && fImage.getWidth(obs) > 0;
287 }
288
289
290
291
292
293
294 private URL getSourceURL() {
295 String src = (String) fElement.getAttributes().getAttribute(HTML.Attribute.SRC);
296 if (src == null) {
297 return null;
298 }
299
300 URL reference = ((HTMLDocument) getDocument()).getBase();
301 try {
302 URL u = new URL(reference, src);
303 return u;
304 } catch (MalformedURLException e) {
305 return null;
306 }
307 }
308
309
310
311
312 private int getIntAttr(HTML.Attribute name, int deflt) {
313 AttributeSet attr = fElement.getAttributes();
314 if (attr.isDefined(name)) {
315 int i;
316 String val = (String) attr.getAttribute(name);
317 if (val == null) {
318 i = deflt;
319 } else {
320 try {
321 i = Math.max(0, Integer.parseInt(val));
322 } catch (NumberFormatException x) {
323 i = deflt;
324 }
325 }
326 return i;
327 } else {
328 return deflt;
329 }
330 }
331
332
333
334
335
336
337 public void setParent(View parent) {
338 super.setParent(parent);
339 fContainer = parent != null ? getContainer() : null;
340 if (parent == null && fComponent != null) {
341 fComponent.getParent().remove(fComponent);
342 fComponent = null;
343 }
344 }
345
346
347
348
349 public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
350 if (DEBUG) {
351 System.out.println("ImageView: changedUpdate begin...");
352 }
353 super.changedUpdate(e, a, f);
354 float align = getVerticalAlignment();
355
356 int height = fHeight;
357 int width = fWidth;
358
359 initialize(getElement());
360
361 boolean hChanged = fHeight != height;
362 boolean wChanged = fWidth != width;
363 if (hChanged || wChanged || getVerticalAlignment() != align) {
364 if (DEBUG) {
365 System.out.println("ImageView: calling preferenceChanged");
366 }
367 getParent().preferenceChanged(this, hChanged, wChanged);
368 }
369 if (DEBUG) {
370 System.out.println("ImageView: changedUpdate end; valign=" + getVerticalAlignment());
371 }
372 }
373
374
375
376
377
378
379
380
381
382
383 public void paint(Graphics g, Shape a) {
384 Color oldColor = g.getColor();
385 fBounds = a.getBounds();
386 int border = getBorder();
387 int x = fBounds.x + border + getSpace(X_AXIS);
388 int y = fBounds.y + border + getSpace(Y_AXIS);
389 int width = fWidth;
390 int height = fHeight;
391 int sel = getSelectionState();
392
393
394
395
396
397
398
399
400
401
402
403
404
405 if (!hasPixels(this)) {
406 g.setColor(Color.lightGray);
407 g.drawRect(x, y, width - 1, height - 1);
408 g.setColor(oldColor);
409 loadIcons();
410 Icon icon = fImage == null ? sMissingImageIcon : sPendingImageIcon;
411 if (icon != null) {
412 icon.paintIcon(getContainer(), g, x, y);
413 }
414 }
415
416
417 if (fImage != null) {
418 g.drawImage(fImage, x, y, width, height, this);
419
420
421
422
423
424
425
426
427
428
429
430
431
432 }
433
434
435 Color bc = getBorderColor();
436 if (sel == 2) {
437
438 int delta = 2 - border;
439 if (delta > 0) {
440 x += delta;
441 y += delta;
442 width -= delta << 1;
443 height -= delta << 1;
444 border = 2;
445 }
446 bc = null;
447 g.setColor(Color.black);
448
449 g.fillRect(x + width - 5, y + height - 5, 5, 5);
450 }
451
452
453 if (border > 0) {
454 if (bc != null) {
455 g.setColor(bc);
456 }
457
458 for (int i = 1; i <= border; i++) {
459 g.drawRect(x - i, y - i, width - 1 + i + i, height - 1 + i + i);
460 }
461 g.setColor(oldColor);
462 }
463 }
464
465
466
467
468
469 protected void repaint(long delay) {
470 if (fContainer != null && fBounds != null) {
471 fContainer.repaint(delay,
472 fBounds.x, fBounds.y, fBounds.width, fBounds.height);
473 }
474 }
475
476
477
478
479
480
481
482 protected int getSelectionState() {
483 int p0 = fElement.getStartOffset();
484 int p1 = fElement.getEndOffset();
485 if (fContainer instanceof JTextComponent) {
486 JTextComponent textComp = (JTextComponent) fContainer;
487 int start = textComp.getSelectionStart();
488 int end = textComp.getSelectionEnd();
489 if (start <= p0 && end >= p1) {
490 if (start == p0 && end == p1 && isEditable()) {
491 return 2;
492 } else {
493 return 1;
494 }
495 }
496 }
497 return 0;
498 }
499
500 protected boolean isEditable() {
501 return fContainer instanceof JEditorPane
502 && ((JEditorPane) fContainer).isEditable();
503 }
504
505
506
507
508 protected Color getHighlightColor() {
509 JTextComponent textComp = (JTextComponent) fContainer;
510 return textComp.getSelectionColor();
511 }
512
513
514
515
516
517
518
519
520
521 public boolean imageUpdate(Image img, int flags, int x, int y,
522 int width, int height) {
523 if (fImage == null || fImage != img) {
524 return false;
525 }
526
527
528 if ((flags & (ABORT | ERROR)) != 0) {
529 fImage = null;
530 repaint(0);
531 return false;
532 }
533
534
535 short changed = 0;
536 if ((flags & ImageObserver.HEIGHT) != 0) {
537 if (!getElement().getAttributes().isDefined(HTML.Attribute.HEIGHT)) {
538 changed |= 1;
539 }
540 }
541 if ((flags & ImageObserver.WIDTH) != 0) {
542 if (!getElement().getAttributes().isDefined(HTML.Attribute.WIDTH)) {
543 changed |= 2;
544 }
545 }
546 synchronized (this) {
547 if ((changed & 1) == 1) {
548 fWidth = width;
549 }
550 if ((changed & 2) == 2) {
551 fHeight = height;
552 }
553 if (loading) {
554
555
556 return true;
557 }
558 }
559 if (changed != 0) {
560
561 if (DEBUG) {
562 System.out.println("ImageView: resized to " + fWidth + "x" + fHeight);
563 }
564
565 Document doc = getDocument();
566 try {
567 if (doc instanceof AbstractDocument) {
568 ((AbstractDocument) doc).readLock();
569 }
570 preferenceChanged(this, true, true);
571 } finally {
572 if (doc instanceof AbstractDocument) {
573 ((AbstractDocument) doc).readUnlock();
574 }
575 }
576
577 return true;
578 }
579
580
581 if ((flags & (FRAMEBITS | ALLBITS)) != 0) {
582 repaint(0);
583 } else if ((flags & SOMEBITS) != 0) {
584 if (sIsInc) {
585 repaint(sIncRate);
586 }
587 }
588
589 return ((flags & ALLBITS) == 0);
590 }
591
592
593
594
595
596
597
598 private static boolean sIsInc = true;
599 private static int sIncRate = 100;
600
601
602
603
604
605
606
607
608
609
610
611
612
613 public float getPreferredSpan(int axis) {
614
615 int extra = 2 * (getBorder() + getSpace(axis));
616 switch (axis) {
617 case View.X_AXIS:
618 return fWidth + extra;
619 case View.Y_AXIS:
620 return fHeight + extra;
621 default:
622 throw new IllegalArgumentException("Invalid axis: " + axis);
623 }
624 }
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639 public float getAlignment(int axis) {
640 switch (axis) {
641 case View.Y_AXIS:
642 return getVerticalAlignment();
643 default:
644 return super.getAlignment(axis);
645 }
646 }
647
648
649
650
651
652
653
654
655
656
657
658
659
660 public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
661 int p0 = getStartOffset();
662 int p1 = getEndOffset();
663 if ((pos >= p0) && (pos <= p1)) {
664 Rectangle r = a.getBounds();
665 if (pos == p1) {
666 r.x += r.width;
667 }
668 r.width = 0;
669 return r;
670 }
671 return null;
672 }
673
674
675
676
677
678
679
680
681
682
683
684
685 public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
686 Rectangle alloc = (Rectangle) a;
687 if (x < alloc.x + alloc.width) {
688 bias[0] = Position.Bias.Forward;
689 return getStartOffset();
690 }
691 bias[0] = Position.Bias.Backward;
692 return getEndOffset();
693 }
694
695
696
697
698
699
700
701 public void setSize(float width, float height) {
702
703
704 }
705
706
707
708
709
710 protected void resize(int width, int height) {
711 if (width == fWidth && height == fHeight) {
712 return;
713 }
714
715 fWidth = width;
716 fHeight = height;
717
718
719 MutableAttributeSet attr = new SimpleAttributeSet();
720 attr.addAttribute(HTML.Attribute.WIDTH, Integer.toString(width));
721 attr.addAttribute(HTML.Attribute.HEIGHT, Integer.toString(height));
722 ((StyledDocument) getDocument()).setCharacterAttributes(
723 fElement.getStartOffset(),
724 fElement.getEndOffset(),
725 attr, false);
726 }
727
728
729
730
731
732
733 public void mousePressed(MouseEvent e) {
734 Dimension size = fComponent.getSize();
735 if (e.getX() >= size.width - 7 && e.getY() >= size.height - 7
736 && getSelectionState() == 2) {
737
738 if (DEBUG) {
739 System.out.println("ImageView: grow!!! Size=" + fWidth + "x" + fHeight);
740 }
741 Point loc = fComponent.getLocationOnScreen();
742 fGrowBase = new Point(loc.x + e.getX() - fWidth,
743 loc.y + e.getY() - fHeight);
744 fGrowProportionally = e.isShiftDown();
745 } else {
746
747 fGrowBase = null;
748 JTextComponent comp = (JTextComponent) fContainer;
749 int start = fElement.getStartOffset();
750 int end = fElement.getEndOffset();
751 int mark = comp.getCaret().getMark();
752 int dot = comp.getCaret().getDot();
753 if (e.isShiftDown()) {
754
755 if (mark <= start) {
756 comp.moveCaretPosition(end);
757 } else {
758 comp.moveCaretPosition(start);
759 }
760 } else {
761
762 if (mark != start) {
763 comp.setCaretPosition(start);
764 }
765 if (dot != end) {
766 comp.moveCaretPosition(end);
767 }
768 }
769 }
770 }
771
772
773
774
775 public void mouseDragged(MouseEvent e) {
776 if (fGrowBase != null) {
777 Point loc = fComponent.getLocationOnScreen();
778 int width = Math.max(2, loc.x + e.getX() - fGrowBase.x);
779 int height = Math.max(2, loc.y + e.getY() - fGrowBase.y);
780
781 if (e.isShiftDown() && fImage != null) {
782
783 float imgWidth = fImage.getWidth(this);
784 float imgHeight = fImage.getHeight(this);
785 if (imgWidth > 0 && imgHeight > 0) {
786 float prop = imgHeight / imgWidth;
787 float pwidth = height / prop;
788 float pheight = width * prop;
789 if (pwidth > width) {
790 width = (int) pwidth;
791 } else {
792 height = (int) pheight;
793 }
794 }
795 }
796
797 resize(width, height);
798 }
799 }
800
801 public void mouseReleased(MouseEvent e) {
802 fGrowBase = null;
803
804 }
805
806
807
808
809 public void mouseClicked(MouseEvent e) {
810 if (e.getClickCount() == 2) {
811
812 }
813 }
814
815 public void mouseEntered(MouseEvent e) {
816 }
817
818 public void mouseMoved(MouseEvent e) {
819 }
820
821 public void mouseExited(MouseEvent e) {
822 }
823
824
825
826 private Icon makeIcon(final String gifFile) throws IOException {
827
828
829
830
831
832
833
834 InputStream resource = ClasspathImageView.class.getResourceAsStream(gifFile);
835
836 if (resource == null) {
837 System.err.println(ClasspathImageView.class.getName() + "/" +
838 gifFile + " not found.");
839 return null;
840 }
841 BufferedInputStream in =
842 new BufferedInputStream(resource);
843 ByteArrayOutputStream out =
844 new ByteArrayOutputStream(1024);
845 byte[] buffer = new byte[1024];
846 int n;
847 while ((n = in.read(buffer)) > 0) {
848 out.write(buffer, 0, n);
849 }
850 in.close();
851 out.flush();
852
853 buffer = out.toByteArray();
854 if (buffer.length == 0) {
855 System.err.println("warning: " + gifFile +
856 " is zero-length");
857 return null;
858 }
859 return new ImageIcon(buffer);
860 }
861
862 private void loadIcons() {
863 try {
864 if (sPendingImageIcon == null) {
865 sPendingImageIcon = makeIcon(PENDING_IMAGE_SRC);
866 }
867 if (sMissingImageIcon == null) {
868 sMissingImageIcon = makeIcon(MISSING_IMAGE_SRC);
869 }
870 } catch (Exception x) {
871 System.err.println("ImageView: Couldn't load image icons");
872 }
873 }
874
875 protected StyleSheet getStyleSheet() {
876 HTMLDocument doc = (HTMLDocument) getDocument();
877 return doc.getStyleSheet();
878 }
879
880
881
882 private AttributeSet attr;
883 private Element fElement;
884 private Image fImage;
885 private int fHeight, fWidth;
886 private Container fContainer;
887 private Rectangle fBounds;
888 private Component fComponent;
889 private Point fGrowBase;
890 private boolean fGrowProportionally;
891
892
893
894
895 private boolean loading;
896
897
898
899 private static Icon sPendingImageIcon,
900 sMissingImageIcon;
901 private static final String
902 PENDING_IMAGE_SRC = "/icons/icn_plan_disabled.gif",
903 MISSING_IMAGE_SRC = "/icons/icn_plan_failed.gif";
904
905 private static final boolean DEBUG = false;
906
907
908 static final String IMAGE_CACHE_PROPERTY = "imageCache";
909
910
911 private static final int
912 DEFAULT_WIDTH = 32,
913 DEFAULT_HEIGHT = 32,
914
915 DEFAULT_BORDER = 2;
916
917 }