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

Last change on this file since 8525 was 8525, checked in by simon04, 10 years ago

see #11591, see #11355 - Splash screen: attempt to fix lock issue (2)

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