Index: /trunk/src/org/openstreetmap/josm/Main.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/Main.java	(revision 8496)
+++ /trunk/src/org/openstreetmap/josm/Main.java	(revision 8497)
@@ -531,8 +531,10 @@
     public static interface InitStatusListener {
 
-        void updateStatus(String event);
+        Object updateStatus(String event);
+        void finish(Object status);
     }
 
     public static void setInitStatusListener(InitStatusListener listener) {
+        CheckParameterUtil.ensureParameterNotNull(listener);
         initListener = listener;
     }
@@ -545,15 +547,20 @@
         isOpenjdk = System.getProperty("java.vm.name").toUpperCase(Locale.ENGLISH).indexOf("OPENJDK") != -1;
 
-        if (initListener != null) {
-            initListener.updateStatus(tr("Executing platform startup hook"));
-        }
-        platform.startupHook();
-
-        if (initListener != null) {
-            initListener.updateStatus(tr("Building main menu"));
-        }
-        contentPanePrivate.add(panel, BorderLayout.CENTER);
-        panel.add(gettingStarted, BorderLayout.CENTER);
-        menu = new MainMenu();
+        new InitializationTask(tr("Executing platform startup hook")) {
+            @Override
+            public void initialize() {
+                platform.startupHook();
+            }
+        }.call();
+
+        new InitializationTask(tr("Building main menu")) {
+
+            @Override
+            public void initialize() {
+                contentPanePrivate.add(panel, BorderLayout.CENTER);
+                panel.add(gettingStarted, BorderLayout.CENTER);
+                menu = new MainMenu();
+            }
+        }.call();
 
         undoRedo.addCommandQueueListener(redoUndoListener);
@@ -571,5 +578,5 @@
 
             @Override
-            public void initialize() throws Exception {
+            public void initialize() {
                 // We try to establish an API connection early, so that any API
                 // capabilities are already known to the editor instance. However
@@ -586,5 +593,5 @@
 
             @Override
-            public void initialize() throws Exception {
+            public void initialize() {
                 validator = new OsmValidator();
                 MapView.addLayerChangeListener(validator);
@@ -595,5 +602,5 @@
 
             @Override
-            public void initialize() throws Exception {
+            public void initialize() {
                 TaggingPresets.initialize();
             }
@@ -603,5 +610,5 @@
 
             @Override
-            public void initialize() throws Exception {
+            public void initialize() {
                 MapPaintPreference.initialize();
             }
@@ -611,5 +618,5 @@
 
             @Override
-            public void initialize() throws Exception {
+            public void initialize() {
                 ImageryPreference.initialize();
             }
@@ -670,12 +677,13 @@
         });
 
-        if (initListener != null) {
-            initListener.updateStatus(tr("Updating user interface"));
-        }
-
-        toolbar.refreshToolbarControl();
-
-        toolbar.control.updateUI();
-        contentPanePrivate.updateUI();
+        new InitializationTask(tr("Updating user interface")) {
+
+            @Override
+            public void initialize() {
+                toolbar.refreshToolbarControl();
+                toolbar.control.updateUI();
+                contentPanePrivate.updateUI();
+            }
+        }.call();
     }
 
@@ -688,16 +696,15 @@
         }
 
-        public abstract void initialize() throws Exception;
+        public abstract void initialize();
 
         @Override
-        public Void call() throws Exception {
+        public Void call() {
+            Object status = null;
             if (initListener != null) {
-                initListener.updateStatus(name);
-            }
-            final long startTime = System.currentTimeMillis();
+                status = initListener.updateStatus(name);
+            }
             initialize();
-            if (isDebugEnabled()) {
-                final long elapsedTime = System.currentTimeMillis() - startTime;
-                Main.debug(tr("{0} completed in {1}", name, Utils.getDurationString(elapsedTime)));
+            if (initListener != null) {
+                initListener.finish(status);
             }
             return null;
Index: /trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 8496)
+++ /trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 8497)
@@ -53,5 +53,4 @@
 import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
 import org.openstreetmap.josm.gui.preferences.server.ProxyPreference;
-import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.io.DefaultProxySelector;
@@ -404,5 +403,5 @@
 
         final SplashScreen splash = new SplashScreen();
-        final ProgressMonitor monitor = splash.getProgressMonitor();
+        final SplashScreen.SplashProgressMonitor monitor = splash.getProgressMonitor();
         monitor.beginTask(tr("Initializing"));
         splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true));
@@ -410,6 +409,14 @@
 
             @Override
-            public void updateStatus(String event) {
-                monitor.indeterminateSubTask(event);
+            public Object updateStatus(String event) {
+                monitor.beginTask(event);
+                return event;
+            }
+
+            @Override
+            public void finish(Object status) {
+                if (status instanceof String) {
+                    monitor.finishTask((String) status);
+                }
             }
         });
Index: /trunk/src/org/openstreetmap/josm/gui/SplashScreen.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/SplashScreen.java	(revision 8496)
+++ /trunk/src/org/openstreetmap/josm/gui/SplashScreen.java	(revision 8497)
@@ -5,5 +5,6 @@
 
 import java.awt.Color;
-import java.awt.Dimension;
+import java.awt.Component;
+import java.awt.Graphics;
 import java.awt.GridBagConstraints;
 import java.awt.GridBagLayout;
@@ -12,6 +13,8 @@
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
-import java.util.Arrays;
-import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
 
 import javax.swing.ImageIcon;
@@ -24,11 +27,14 @@
 import javax.swing.border.EmptyBorder;
 import javax.swing.border.EtchedBorder;
-
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.Version;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
-import org.openstreetmap.josm.gui.progress.ProgressRenderer;
-import org.openstreetmap.josm.gui.progress.SwingRenderingProgressMonitor;
+import org.openstreetmap.josm.gui.progress.ProgressTaskId;
 import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.tools.ImageProvider;
+import org.openstreetmap.josm.tools.Predicates;
 import org.openstreetmap.josm.tools.Utils;
 import org.openstreetmap.josm.tools.WindowGeometry;
@@ -38,7 +44,8 @@
  * @since 976
  */
-public class SplashScreen extends JFrame {
-
-    private final transient SwingRenderingProgressMonitor progressMonitor;
+public class SplashScreen extends JFrame implements ChangeListener {
+
+    private final SplashProgressMonitor progressMonitor;
+    private final SplashScreenProgressRenderer progressRenderer;
 
     /**
@@ -90,9 +97,9 @@
 
         // Add a status message
-        SplashScreenProgressRenderer progressRenderer = new SplashScreenProgressRenderer();
+        progressRenderer = new SplashScreenProgressRenderer();
         gbc.gridy = 3;
         gbc.insets = new Insets(0, 0, 10, 0);
         innerContentPane.add(progressRenderer, gbc);
-        progressMonitor = new SwingRenderingProgressMonitor(progressRenderer);
+        progressMonitor = new SplashProgressMonitor(null, this);
 
         pack();
@@ -109,15 +116,217 @@
     }
 
+    @Override
+    public void stateChanged(ChangeEvent ignore) {
+        progressRenderer.setTasks("<html>"
+                + "<style>ul {margin-top: 0; margin-bottom: 0; padding: 0;} li {margin: 0; padding: 0;}</style>"
+                + "<body height='320'>"
+                + progressMonitor.toString());
+    }
+
+    /**
+     * A task (of a {@link ProgressMonitor}).
+     */
+    private static abstract class Task {
+
+        /**
+         * Returns a HTML representation for this task.
+         */
+        public abstract String toHtml();
+
+        @Override
+        public final String toString() {
+            return toHtml();
+        }
+    }
+
+    /**
+     * A single task (of a {@link ProgressMonitor}) which keeps track of its execution duration
+     * (requires a call to {@link #finish()}).
+     */
+    private static class MeasurableTask extends Task {
+        private final String name;
+        private final long start;
+        private String duration = "";
+
+        public MeasurableTask(String name) {
+            this.name = name;
+            this.start = System.currentTimeMillis();
+        }
+
+        public void finish() {
+            if (!"".equals(duration)) {
+                throw new IllegalStateException("This tasks has already been finished");
+            }
+            duration = tr(" ({0})", Utils.getDurationString(System.currentTimeMillis() - start));
+        }
+
+        @Override
+        public String toHtml() {
+            return name + "<i style='color: #666666;'>" + duration + "</i>";
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            MeasurableTask that = (MeasurableTask) o;
+            return Objects.equals(name, that.name);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hashCode(name);
+        }
+    }
+
+    /**
+     * A {@link ProgressMonitor} which stores the (sub)tasks in a tree.
+     */
+    public static class SplashProgressMonitor extends Task implements ProgressMonitor {
+
+        private final String name;
+        private final ChangeListener listener;
+        private final List<Task> tasks = Collections.synchronizedList(new ArrayList<Task>());
+        private SplashProgressMonitor latestSubtask;
+
+        public SplashProgressMonitor(String name, ChangeListener listener) {
+            this.name = name;
+            this.listener = listener;
+        }
+
+        @Override
+        public String toHtml() {
+            synchronized (tasks) {
+                return Utils.firstNonNull(name, "") + (tasks.isEmpty() ? "" : Utils.joinAsHtmlUnorderedList(tasks));
+            }
+        }
+
+        @Override
+        public void beginTask(String title) {
+            if (title != null) {
+                final MeasurableTask task = new MeasurableTask(title);
+                tasks.add(task);
+                listener.stateChanged(null);
+            }
+        }
+
+        @Override
+        public void beginTask(String title, int ticks) {
+            this.beginTask(title);
+        }
+
+        @Override
+        public void setCustomText(String text) {
+            this.beginTask(text);
+        }
+
+        @Override
+        public void setExtraText(String text) {
+            this.beginTask(text);
+        }
+
+        @Override
+        public void indeterminateSubTask(String title) {
+            this.subTask(title);
+        }
+
+        @Override
+        public void subTask(String title) {
+            latestSubtask = new SplashProgressMonitor(title, listener);
+            tasks.add(latestSubtask);
+            listener.stateChanged(null);
+        }
+
+        @Override
+        public ProgressMonitor createSubTaskMonitor(int ticks, boolean internal) {
+            return latestSubtask;
+        }
+
+        @Override
+        @Deprecated
+        public void finishTask() {
+        }
+
+        public void finishTask(String title) {
+            final Task task = Utils.find(tasks, Predicates.<Task>equalTo(new MeasurableTask(title)));
+            if (task != null && task instanceof MeasurableTask) {
+                ((MeasurableTask) task).finish();
+                Main.debug(tr("{0} completed in {1}", title, ((MeasurableTask) task).duration));
+                listener.stateChanged(null);
+            }
+        }
+
+        @Override
+        public void invalidate() {
+        }
+
+        @Override
+        public void setTicksCount(int ticks) {
+        }
+
+        @Override
+        public int getTicksCount() {
+            return 0;
+        }
+
+        @Override
+        public void setTicks(int ticks) {
+        }
+
+        @Override
+        public int getTicks() {
+            return 0;
+        }
+
+        @Override
+        public void worked(int ticks) {
+        }
+
+        @Override
+        public boolean isCanceled() {
+            return false;
+        }
+
+        @Override
+        public void cancel() {
+        }
+
+        @Override
+        public void addCancelListener(CancelListener listener) {
+        }
+
+        @Override
+        public void removeCancelListener(CancelListener listener) {
+        }
+
+        @Override
+        public void appendLogMessage(String message) {
+        }
+
+        @Override
+        public void setProgressTaskId(ProgressTaskId taskId) {
+        }
+
+        @Override
+        public ProgressTaskId getProgressTaskId() {
+            return null;
+        }
+
+        @Override
+        public Component getWindowParent() {
+            return Main.parent;
+        }
+    }
+
     /**
      * Returns the progress monitor.
      * @return The progress monitor
      */
-    public ProgressMonitor getProgressMonitor() {
+    public SplashProgressMonitor getProgressMonitor() {
         return progressMonitor;
     }
 
-    private static class SplashScreenProgressRenderer extends JPanel implements ProgressRenderer {
+    private static class SplashScreenProgressRenderer extends JPanel {
         private JLabel lblTaskTitle;
-        private JLabel lblCustomText;
         private JProgressBar progressBar;
 
@@ -133,27 +342,8 @@
             add(lblTaskTitle = new JLabel(" "), gc);
 
-            gc.gridx = 0;
             gc.gridy = 1;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            gc.weighty = 0.0;
-            gc.insets = new Insets(5,0,0,0);
-            add(lblCustomText = new JLabel(" ") {
-                @Override
-                public Dimension getPreferredSize() {
-                    Dimension d = super.getPreferredSize();
-                    if(d.width < 600) d.width = 600;
-                    d.height *= MAX_NUMBER_OF_MESSAGES;
-                    return d;
-                }
-            }, gc);
-
-            gc.gridx = 0;
-            gc.gridy = 2;
-            gc.fill = GridBagConstraints.HORIZONTAL;
-            gc.weightx = 1.0;
-            gc.weighty = 0.0;
-            gc.insets = new Insets(5,0,0,0);
+            gc.insets = new Insets(15,0,0,0);
             add(progressBar = new JProgressBar(JProgressBar.HORIZONTAL), gc);
+            progressBar.setIndeterminate(true);
         }
 
@@ -163,66 +353,16 @@
 
         @Override
-        public void setCustomText(String message) {
-            if(message.isEmpty())
-                message = " "; // prevent killing of additional line
-            lblCustomText.setText(message);
+        public void paint(Graphics g) {
+            try {
+                super.paint(g);
+            } catch (NullPointerException ignore) {
+                // NullPointerException at javax.swing.text.html.StyleSheet$ListPainter.paint
+            }
+        }
+
+        public void setTasks(String tasks) {
+            lblTaskTitle.setText(tasks);
             repaint();
         }
-
-        @Override
-        public void setIndeterminate(boolean indeterminate) {
-            progressBar.setIndeterminate(indeterminate);
-            repaint();
-        }
-
-        @Override
-        public void setMaximum(int maximum) {
-            progressBar.setMaximum(maximum);
-            repaint();
-        }
-
-        private static final int MAX_NUMBER_OF_MESSAGES = 3;
-        private LinkedList<String> messages = new LinkedList<>(Arrays.asList("", "", "")); //update when changing MAX_NUMBER_OF_MESSAGES
-        private long time = System.currentTimeMillis();
-
-        /**
-         * Stores and displays the {@code MAX_NUMBER_OF_MESSAGES} most recent
-         * task titles together with their execution time.
-         */
-        @Override
-        public void setTaskTitle(String taskTitle) {
-
-            while (messages.size() >= MAX_NUMBER_OF_MESSAGES) {
-                messages.removeFirst();
-            }
-            long now = System.currentTimeMillis();
-            String prevMessageTitle = messages.getLast();
-            // now should always be >= time but if can be inferior sometimes, see #10287
-            if (!prevMessageTitle.isEmpty() && now >= time) {
-                messages.removeLast();
-                messages.add(tr("{0} ({1})", prevMessageTitle, Utils.getDurationString(now - time)));
-            }
-            time = now;
-            if (!taskTitle.isEmpty()) {
-                messages.add(taskTitle);
-            }
-            StringBuilder html = new StringBuilder();
-            int i = 0;
-            for (String m : messages) {
-                html.append("<p class=\"entry").append(++i).append("\">").append(m).append("</p>");
-            }
-
-            lblTaskTitle.setText("<html><style>"
-                    + ".entry1{color:#CCCCCC;}"
-                    + ".entry2{color:#999999;}"
-                    + ".entry3{color:#000000;}</style>" + html + "</html>");  //update when changing MAX_NUMBER_OF_MESSAGES
-            repaint();
-        }
-
-        @Override
-        public void setValue(int value) {
-            progressBar.setValue(value);
-            repaint();
-        }
     }
 }
