1 | // License: GPL. For details, see LICENSE file.
|
---|
2 | package org.openstreetmap.josm.gui.widgets;
|
---|
3 |
|
---|
4 | import java.awt.Graphics;
|
---|
5 | import java.awt.Image;
|
---|
6 | import java.awt.Shape;
|
---|
7 | import java.lang.reflect.Field;
|
---|
8 | import java.lang.reflect.InvocationTargetException;
|
---|
9 | import java.lang.reflect.Method;
|
---|
10 | import java.net.URL;
|
---|
11 |
|
---|
12 | import javax.swing.text.AttributeSet;
|
---|
13 | import javax.swing.text.Element;
|
---|
14 | import javax.swing.text.html.ImageView;
|
---|
15 |
|
---|
16 | import org.openstreetmap.josm.Main;
|
---|
17 | import org.openstreetmap.josm.tools.ImageProvider;
|
---|
18 |
|
---|
19 | /**
|
---|
20 | * Specialized Image View allowing to display SVG images.
|
---|
21 | * @since 8933
|
---|
22 | */
|
---|
23 | public class JosmImageView extends ImageView {
|
---|
24 |
|
---|
25 | private static final int LOADING_FLAG = 1;
|
---|
26 | private static final int WIDTH_FLAG = 4;
|
---|
27 | private static final int HEIGHT_FLAG = 8;
|
---|
28 | private static final int RELOAD_FLAG = 16;
|
---|
29 | private static final int RELOAD_IMAGE_FLAG = 32;
|
---|
30 |
|
---|
31 | private final Field imageField;
|
---|
32 | private final Field stateField;
|
---|
33 | private final Field widthField;
|
---|
34 | private final Field heightField;
|
---|
35 |
|
---|
36 | /**
|
---|
37 | * Constructs a new {@code JosmImageView}.
|
---|
38 | * @param elem the element to create a view for
|
---|
39 | * @throws SecurityException see {@link Class#getDeclaredField} for details
|
---|
40 | * @throws NoSuchFieldException see {@link Class#getDeclaredField} for details
|
---|
41 | */
|
---|
42 | public JosmImageView(Element elem) throws NoSuchFieldException, SecurityException {
|
---|
43 | super(elem);
|
---|
44 | imageField = ImageView.class.getDeclaredField("image");
|
---|
45 | stateField = ImageView.class.getDeclaredField("state");
|
---|
46 | widthField = ImageView.class.getDeclaredField("width");
|
---|
47 | heightField = ImageView.class.getDeclaredField("height");
|
---|
48 | imageField.setAccessible(true);
|
---|
49 | stateField.setAccessible(true);
|
---|
50 | widthField.setAccessible(true);
|
---|
51 | heightField.setAccessible(true);
|
---|
52 | }
|
---|
53 |
|
---|
54 | /**
|
---|
55 | * Makes sure the necessary properties and image is loaded.
|
---|
56 | */
|
---|
57 | private void sync() {
|
---|
58 | try {
|
---|
59 | int s = (int) stateField.get(this);
|
---|
60 | if ((s & RELOAD_IMAGE_FLAG) != 0) {
|
---|
61 | refreshImage();
|
---|
62 | }
|
---|
63 | s = (int) stateField.get(this);
|
---|
64 | if ((s & RELOAD_FLAG) != 0) {
|
---|
65 | synchronized (this) {
|
---|
66 | stateField.set(this, ((int) stateField.get(this) | RELOAD_FLAG) ^ RELOAD_FLAG);
|
---|
67 | }
|
---|
68 | setPropertiesFromAttributes();
|
---|
69 | }
|
---|
70 | } catch (IllegalArgumentException | IllegalAccessException |
|
---|
71 | InvocationTargetException | NoSuchMethodException | SecurityException e) {
|
---|
72 | Main.error(e);
|
---|
73 | }
|
---|
74 | }
|
---|
75 |
|
---|
76 | /**
|
---|
77 | * Loads the image and updates the size accordingly. This should be
|
---|
78 | * invoked instead of invoking <code>loadImage</code> or
|
---|
79 | * <code>updateImageSize</code> directly.
|
---|
80 | * @throws IllegalAccessException see {@link Field#set} and {@link Method#invoke} for details
|
---|
81 | * @throws IllegalArgumentException see {@link Field#set} and {@link Method#invoke} for details
|
---|
82 | * @throws InvocationTargetException see {@link Method#invoke} for details
|
---|
83 | * @throws NoSuchMethodException see {@link Class#getDeclaredMethod} for details
|
---|
84 | * @throws SecurityException see {@link Class#getDeclaredMethod} for details
|
---|
85 | */
|
---|
86 | private void refreshImage() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException,
|
---|
87 | NoSuchMethodException, SecurityException {
|
---|
88 | synchronized (this) {
|
---|
89 | // clear out width/height/reloadimage flag and set loading flag
|
---|
90 | stateField.set(this, ((int) stateField.get(this) | LOADING_FLAG | RELOAD_IMAGE_FLAG | WIDTH_FLAG |
|
---|
91 | HEIGHT_FLAG) ^ (WIDTH_FLAG | HEIGHT_FLAG |
|
---|
92 | RELOAD_IMAGE_FLAG));
|
---|
93 | imageField.set(this, null);
|
---|
94 | widthField.set(this, 0);
|
---|
95 | heightField.set(this, 0);
|
---|
96 | }
|
---|
97 |
|
---|
98 | try {
|
---|
99 | // Load the image
|
---|
100 | loadImage();
|
---|
101 |
|
---|
102 | // And update the size params
|
---|
103 | Method updateImageSize = ImageView.class.getDeclaredMethod("updateImageSize");
|
---|
104 | updateImageSize.setAccessible(true);
|
---|
105 | updateImageSize.invoke(this);
|
---|
106 | } finally {
|
---|
107 | synchronized (this) {
|
---|
108 | // Clear out state in case someone threw an exception.
|
---|
109 | stateField.set(this, ((int) stateField.get(this) | LOADING_FLAG) ^ LOADING_FLAG);
|
---|
110 | }
|
---|
111 | }
|
---|
112 | }
|
---|
113 |
|
---|
114 | /**
|
---|
115 | * Loads the image from the URL <code>getImageURL</code>. This should
|
---|
116 | * only be invoked from <code>refreshImage</code>.
|
---|
117 | * @throws IllegalAccessException see {@link Field#set} and {@link Method#invoke} for details
|
---|
118 | * @throws IllegalArgumentException see {@link Field#set} and {@link Method#invoke} for details
|
---|
119 | * @throws InvocationTargetException see {@link Method#invoke} for details
|
---|
120 | * @throws NoSuchMethodException see {@link Class#getDeclaredMethod} for details
|
---|
121 | * @throws SecurityException see {@link Class#getDeclaredMethod} for details
|
---|
122 | */
|
---|
123 | private void loadImage() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException,
|
---|
124 | NoSuchMethodException, SecurityException {
|
---|
125 | URL src = getImageURL();
|
---|
126 | if (src != null) {
|
---|
127 | String urlStr = src.toExternalForm();
|
---|
128 | if (urlStr.endsWith(".svg") || urlStr.endsWith(".svg?format=raw")) {
|
---|
129 | imageField.set(this, new ImageProvider(urlStr).get().getImage());
|
---|
130 | } else {
|
---|
131 | Method loadImage = ImageView.class.getDeclaredMethod("loadImage");
|
---|
132 | loadImage.setAccessible(true);
|
---|
133 | loadImage.invoke(this);
|
---|
134 | }
|
---|
135 | } else {
|
---|
136 | imageField.set(this, null);
|
---|
137 | }
|
---|
138 | }
|
---|
139 |
|
---|
140 | @Override
|
---|
141 | public Image getImage() {
|
---|
142 | sync();
|
---|
143 | return super.getImage();
|
---|
144 | }
|
---|
145 |
|
---|
146 | @Override
|
---|
147 | public AttributeSet getAttributes() {
|
---|
148 | sync();
|
---|
149 | return super.getAttributes();
|
---|
150 | }
|
---|
151 |
|
---|
152 | @Override
|
---|
153 | public void paint(Graphics g, Shape a) {
|
---|
154 | sync();
|
---|
155 | super.paint(g, a);
|
---|
156 | }
|
---|
157 |
|
---|
158 | @Override
|
---|
159 | public float getPreferredSpan(int axis) {
|
---|
160 | sync();
|
---|
161 | return super.getPreferredSpan(axis);
|
---|
162 | }
|
---|
163 |
|
---|
164 | @Override
|
---|
165 | public void setSize(float width, float height) {
|
---|
166 | sync();
|
---|
167 | super.setSize(width, height);
|
---|
168 | }
|
---|
169 | }
|
---|