source: josm/trunk/src/org/openstreetmap/josm/gui/SplashScreen.java@ 10508

Last change on this file since 10508 was 10428, checked in by stoecker, 8 years ago

see #9995 - patch mainly by strump - improve HIDPI behaviour

  • Property svn:eol-style set to native
File size: 12.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Component;
8import java.awt.Dimension;
9import java.awt.GridBagConstraints;
10import java.awt.GridBagLayout;
11import java.awt.Insets;
12import java.awt.event.MouseAdapter;
13import java.awt.event.MouseEvent;
14import java.util.List;
15import java.util.Objects;
16import java.util.concurrent.CopyOnWriteArrayList;
17
18import javax.swing.BorderFactory;
19import javax.swing.JFrame;
20import javax.swing.JLabel;
21import javax.swing.JPanel;
22import javax.swing.JProgressBar;
23import javax.swing.JScrollPane;
24import javax.swing.JSeparator;
25import javax.swing.ScrollPaneConstants;
26import javax.swing.border.Border;
27import javax.swing.border.EmptyBorder;
28import javax.swing.border.EtchedBorder;
29import javax.swing.event.ChangeEvent;
30import javax.swing.event.ChangeListener;
31
32import org.openstreetmap.josm.Main;
33import org.openstreetmap.josm.data.Version;
34import org.openstreetmap.josm.gui.progress.ProgressMonitor;
35import org.openstreetmap.josm.gui.progress.ProgressTaskId;
36import org.openstreetmap.josm.gui.util.GuiHelper;
37import org.openstreetmap.josm.gui.widgets.JosmEditorPane;
38import org.openstreetmap.josm.tools.GBC;
39import org.openstreetmap.josm.tools.ImageProvider;
40import org.openstreetmap.josm.tools.Predicates;
41import org.openstreetmap.josm.tools.Utils;
42import org.openstreetmap.josm.tools.WindowGeometry;
43
44/**
45 * Show a splash screen so the user knows what is happening during startup.
46 * @since 976
47 */
48public class SplashScreen extends JFrame implements ChangeListener {
49
50 private final transient SplashProgressMonitor progressMonitor;
51 private final SplashScreenProgressRenderer progressRenderer;
52
53 /**
54 * Constructs a new {@code SplashScreen}.
55 */
56 public SplashScreen() {
57 setUndecorated(true);
58
59 // Add a nice border to the main splash screen
60 JPanel contentPane = (JPanel) this.getContentPane();
61 Border margin = new EtchedBorder(1, Color.white, Color.gray);
62 contentPane.setBorder(margin);
63
64 // Add a margin from the border to the content
65 JPanel innerContentPane = new JPanel(new GridBagLayout());
66 innerContentPane.setBorder(new EmptyBorder(10, 10, 2, 10));
67 contentPane.add(innerContentPane);
68
69 // Add the logo
70 JLabel logo = new JLabel(ImageProvider.get("logo.svg", ImageProvider.ImageSizes.SPLASH_LOGO));
71 GridBagConstraints gbc = new GridBagConstraints();
72 gbc.gridheight = 2;
73 gbc.insets = new Insets(0, 0, 0, 70);
74 innerContentPane.add(logo, gbc);
75
76 // Add the name of this application
77 JLabel caption = new JLabel("JOSM – " + tr("Java OpenStreetMap Editor"));
78 caption.setFont(GuiHelper.getTitleFont());
79 gbc.gridheight = 1;
80 gbc.gridx = 1;
81 gbc.insets = new Insets(30, 0, 0, 0);
82 innerContentPane.add(caption, gbc);
83
84 // Add the version number
85 JLabel version = new JLabel(tr("Version {0}", Version.getInstance().getVersionString()));
86 gbc.gridy = 1;
87 gbc.insets = new Insets(0, 0, 0, 0);
88 innerContentPane.add(version, gbc);
89
90 // Add a separator to the status text
91 JSeparator separator = new JSeparator(JSeparator.HORIZONTAL);
92 gbc.gridx = 0;
93 gbc.gridy = 2;
94 gbc.gridwidth = 2;
95 gbc.fill = GridBagConstraints.HORIZONTAL;
96 gbc.insets = new Insets(15, 0, 5, 0);
97 innerContentPane.add(separator, gbc);
98
99 // Add a status message
100 progressRenderer = new SplashScreenProgressRenderer();
101 gbc.gridy = 3;
102 gbc.insets = new Insets(0, 0, 10, 0);
103 innerContentPane.add(progressRenderer, gbc);
104 progressMonitor = new SplashProgressMonitor(null, this);
105
106 pack();
107
108 WindowGeometry.centerOnScreen(this.getSize(), "gui.geometry").applySafe(this);
109
110 // Add ability to hide splash screen by clicking it
111 addMouseListener(new MouseAdapter() {
112 @Override
113 public void mousePressed(MouseEvent event) {
114 setVisible(false);
115 }
116 });
117 }
118
119 @Override
120 public void stateChanged(ChangeEvent ignore) {
121 GuiHelper.runInEDT(new Runnable() {
122 @Override
123 public void run() {
124 progressRenderer.setTasks(progressMonitor.toString());
125 }
126 });
127 }
128
129 /**
130 * A task (of a {@link ProgressMonitor}).
131 */
132 private abstract static class Task {
133
134 /**
135 * Returns a HTML representation for this task.
136 * @param sb a {@code StringBuilder} used to build the HTML code
137 * @return {@code sb}
138 */
139 public abstract StringBuilder toHtml(StringBuilder sb);
140
141 @Override
142 public final String toString() {
143 return toHtml(new StringBuilder(1024)).toString();
144 }
145 }
146
147 /**
148 * A single task (of a {@link ProgressMonitor}) which keeps track of its execution duration
149 * (requires a call to {@link #finish()}).
150 */
151 private static class MeasurableTask extends Task {
152 private final String name;
153 private final long start;
154 private String duration = "";
155
156 MeasurableTask(String name) {
157 this.name = name;
158 this.start = System.currentTimeMillis();
159 }
160
161 public void finish() {
162 if (!"".equals(duration)) {
163 throw new IllegalStateException("This tasks has already been finished");
164 }
165 duration = tr(" ({0})", Utils.getDurationString(System.currentTimeMillis() - start));
166 }
167
168 @Override
169 public StringBuilder toHtml(StringBuilder sb) {
170 return sb.append(name).append("<i style='color: #666666;'>").append(duration).append("</i>");
171 }
172
173 @Override
174 public boolean equals(Object o) {
175 if (this == o) return true;
176 if (o == null || getClass() != o.getClass()) return false;
177 MeasurableTask that = (MeasurableTask) o;
178 return Objects.equals(name, that.name);
179 }
180
181 @Override
182 public int hashCode() {
183 return Objects.hashCode(name);
184 }
185 }
186
187 /**
188 * A {@link ProgressMonitor} which stores the (sub)tasks in a tree.
189 */
190 public static class SplashProgressMonitor extends Task implements ProgressMonitor {
191
192 private final String name;
193 private final ChangeListener listener;
194 private final List<Task> tasks = new CopyOnWriteArrayList<>();
195 private SplashProgressMonitor latestSubtask;
196
197 /**
198 * Constructs a new {@code SplashProgressMonitor}.
199 * @param name name
200 * @param listener change listener
201 */
202 public SplashProgressMonitor(String name, ChangeListener listener) {
203 this.name = name;
204 this.listener = listener;
205 }
206
207 @Override
208 public StringBuilder toHtml(StringBuilder sb) {
209 sb.append(Utils.firstNonNull(name, ""));
210 if (!tasks.isEmpty()) {
211 sb.append("<ul>");
212 for (Task i : tasks) {
213 sb.append("<li>");
214 i.toHtml(sb);
215 sb.append("</li>");
216 }
217 sb.append("</ul>");
218 }
219 return sb;
220 }
221
222 @Override
223 public void beginTask(String title) {
224 if (title != null) {
225 if (Main.isDebugEnabled()) {
226 Main.debug(title);
227 }
228 final MeasurableTask task = new MeasurableTask(title);
229 tasks.add(task);
230 listener.stateChanged(null);
231 }
232 }
233
234 @Override
235 public void beginTask(String title, int ticks) {
236 this.beginTask(title);
237 }
238
239 @Override
240 public void setCustomText(String text) {
241 this.beginTask(text);
242 }
243
244 @Override
245 public void setExtraText(String text) {
246 this.beginTask(text);
247 }
248
249 @Override
250 public void indeterminateSubTask(String title) {
251 this.subTask(title);
252 }
253
254 @Override
255 public void subTask(String title) {
256 if (Main.isDebugEnabled()) {
257 Main.debug(title);
258 }
259 latestSubtask = new SplashProgressMonitor(title, listener);
260 tasks.add(latestSubtask);
261 listener.stateChanged(null);
262 }
263
264 @Override
265 public ProgressMonitor createSubTaskMonitor(int ticks, boolean internal) {
266 return latestSubtask;
267 }
268
269 /**
270 * @deprecated Use {@link #finishTask(String)} instead.
271 */
272 @Override
273 @Deprecated
274 public void finishTask() {
275 // Not used
276 }
277
278 /**
279 * Displays the given task as finished.
280 * @param title the task title
281 */
282 public void finishTask(String title) {
283 final Task task = Utils.find(tasks, Predicates.<Task>equalTo(new MeasurableTask(title)));
284 if (task instanceof MeasurableTask) {
285 ((MeasurableTask) task).finish();
286 if (Main.isDebugEnabled()) {
287 Main.debug(tr("{0} completed in {1}", title, ((MeasurableTask) task).duration));
288 }
289 listener.stateChanged(null);
290 }
291 }
292
293 @Override
294 public void invalidate() {
295 // Not used
296 }
297
298 @Override
299 public void setTicksCount(int ticks) {
300 // Not used
301 }
302
303 @Override
304 public int getTicksCount() {
305 return 0;
306 }
307
308 @Override
309 public void setTicks(int ticks) {
310 // Not used
311 }
312
313 @Override
314 public int getTicks() {
315 return 0;
316 }
317
318 @Override
319 public void worked(int ticks) {
320 // Not used
321 }
322
323 @Override
324 public boolean isCanceled() {
325 return false;
326 }
327
328 @Override
329 public void cancel() {
330 // Not used
331 }
332
333 @Override
334 public void addCancelListener(CancelListener listener) {
335 // Not used
336 }
337
338 @Override
339 public void removeCancelListener(CancelListener listener) {
340 // Not used
341 }
342
343 @Override
344 public void appendLogMessage(String message) {
345 // Not used
346 }
347
348 @Override
349 public void setProgressTaskId(ProgressTaskId taskId) {
350 // Not used
351 }
352
353 @Override
354 public ProgressTaskId getProgressTaskId() {
355 return null;
356 }
357
358 @Override
359 public Component getWindowParent() {
360 return Main.parent;
361 }
362 }
363
364 /**
365 * Returns the progress monitor.
366 * @return The progress monitor
367 */
368 public SplashProgressMonitor getProgressMonitor() {
369 return progressMonitor;
370 }
371
372 private static class SplashScreenProgressRenderer extends JPanel {
373 private final JosmEditorPane lblTaskTitle = new JosmEditorPane();
374 private final JProgressBar progressBar = new JProgressBar(JProgressBar.HORIZONTAL);
375 private static final String LABEL_HTML = "<html>"
376 + "<style>ul {margin-top: 0; margin-bottom: 0; padding: 0;} li {margin: 0; padding: 0;}</style>";
377
378 protected void build() {
379 setLayout(new GridBagLayout());
380
381 JosmEditorPane.makeJLabelLike(lblTaskTitle, false);
382 lblTaskTitle.setText(LABEL_HTML);
383 final JScrollPane scrollPane = new JScrollPane(lblTaskTitle,
384 ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
385 scrollPane.setPreferredSize(new Dimension(0, 320));
386 scrollPane.setBorder(BorderFactory.createEmptyBorder());
387 add(scrollPane, GBC.eol().insets(5, 5, 0, 0).fill(GridBagConstraints.HORIZONTAL));
388
389 progressBar.setIndeterminate(true);
390 add(progressBar, GBC.eol().insets(5, 15, 0, 0).fill(GridBagConstraints.HORIZONTAL));
391 }
392
393 /**
394 * Constructs a new {@code SplashScreenProgressRenderer}.
395 */
396 SplashScreenProgressRenderer() {
397 build();
398 }
399
400 /**
401 * Sets the tasks to displayed. A HTML formatted list is expected.
402 * @param tasks HTML formatted list of tasks
403 */
404 public void setTasks(String tasks) {
405 lblTaskTitle.setText(LABEL_HTML + tasks);
406 lblTaskTitle.setCaretPosition(lblTaskTitle.getDocument().getLength());
407 }
408 }
409}
Note: See TracBrowser for help on using the repository browser.