source: josm/trunk/src/org/openstreetmap/josm/gui/widgets/JosmImageView.java@ 13652

Last change on this file since 13652 was 13649, checked in by Don-vip, 6 years ago

fix #16204 - allow to create a new layer, draw, drag, open a few windows. Nothing more to hope in sandbox mode. At least JOSM is now more robust than ever.

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