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

Last change on this file since 10254 was 10254, checked in by Don-vip, 8 years ago

sonar - squid:S1948 - Fields in a "Serializable" class should either be transient or serializable

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