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

Last change on this file since 13782 was 12678, checked in by Don-vip, 7 years ago

see #15182 - move WindowGeometry from tools to gui.util

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