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

Last change on this file since 11377 was 10781, checked in by simon04, 8 years ago

fix #11874 - No progress bar while downloading plugins

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