Changeset 8497 in josm


Ignore:
Timestamp:
2015-06-19T20:37:48+02:00 (9 years ago)
Author:
simon04
Message:

fix #11355 - Splash screen: display parallel initialization tasks in a sensible way

Location:
trunk/src/org/openstreetmap/josm
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/Main.java

    r8443 r8497  
    531531    public static interface InitStatusListener {
    532532
    533         void updateStatus(String event);
     533        Object updateStatus(String event);
     534        void finish(Object status);
    534535    }
    535536
    536537    public static void setInitStatusListener(InitStatusListener listener) {
     538        CheckParameterUtil.ensureParameterNotNull(listener);
    537539        initListener = listener;
    538540    }
     
    545547        isOpenjdk = System.getProperty("java.vm.name").toUpperCase(Locale.ENGLISH).indexOf("OPENJDK") != -1;
    546548
    547         if (initListener != null) {
    548             initListener.updateStatus(tr("Executing platform startup hook"));
    549         }
    550         platform.startupHook();
    551 
    552         if (initListener != null) {
    553             initListener.updateStatus(tr("Building main menu"));
    554         }
    555         contentPanePrivate.add(panel, BorderLayout.CENTER);
    556         panel.add(gettingStarted, BorderLayout.CENTER);
    557         menu = new MainMenu();
     549        new InitializationTask(tr("Executing platform startup hook")) {
     550            @Override
     551            public void initialize() {
     552                platform.startupHook();
     553            }
     554        }.call();
     555
     556        new InitializationTask(tr("Building main menu")) {
     557
     558            @Override
     559            public void initialize() {
     560                contentPanePrivate.add(panel, BorderLayout.CENTER);
     561                panel.add(gettingStarted, BorderLayout.CENTER);
     562                menu = new MainMenu();
     563            }
     564        }.call();
    558565
    559566        undoRedo.addCommandQueueListener(redoUndoListener);
     
    571578
    572579            @Override
    573             public void initialize() throws Exception {
     580            public void initialize() {
    574581                // We try to establish an API connection early, so that any API
    575582                // capabilities are already known to the editor instance. However
     
    586593
    587594            @Override
    588             public void initialize() throws Exception {
     595            public void initialize() {
    589596                validator = new OsmValidator();
    590597                MapView.addLayerChangeListener(validator);
     
    595602
    596603            @Override
    597             public void initialize() throws Exception {
     604            public void initialize() {
    598605                TaggingPresets.initialize();
    599606            }
     
    603610
    604611            @Override
    605             public void initialize() throws Exception {
     612            public void initialize() {
    606613                MapPaintPreference.initialize();
    607614            }
     
    611618
    612619            @Override
    613             public void initialize() throws Exception {
     620            public void initialize() {
    614621                ImageryPreference.initialize();
    615622            }
     
    670677        });
    671678
    672         if (initListener != null) {
    673             initListener.updateStatus(tr("Updating user interface"));
    674         }
    675 
    676         toolbar.refreshToolbarControl();
    677 
    678         toolbar.control.updateUI();
    679         contentPanePrivate.updateUI();
     679        new InitializationTask(tr("Updating user interface")) {
     680
     681            @Override
     682            public void initialize() {
     683                toolbar.refreshToolbarControl();
     684                toolbar.control.updateUI();
     685                contentPanePrivate.updateUI();
     686            }
     687        }.call();
    680688    }
    681689
     
    688696        }
    689697
    690         public abstract void initialize() throws Exception;
     698        public abstract void initialize();
    691699
    692700        @Override
    693         public Void call() throws Exception {
     701        public Void call() {
     702            Object status = null;
    694703            if (initListener != null) {
    695                 initListener.updateStatus(name);
    696             }
    697             final long startTime = System.currentTimeMillis();
     704                status = initListener.updateStatus(name);
     705            }
    698706            initialize();
    699             if (isDebugEnabled()) {
    700                 final long elapsedTime = System.currentTimeMillis() - startTime;
    701                 Main.debug(tr("{0} completed in {1}", name, Utils.getDurationString(elapsedTime)));
     707            if (initListener != null) {
     708                initListener.finish(status);
    702709            }
    703710            return null;
  • trunk/src/org/openstreetmap/josm/gui/MainApplication.java

    r8443 r8497  
    5353import org.openstreetmap.josm.gui.preferences.server.OAuthAccessTokenHolder;
    5454import org.openstreetmap.josm.gui.preferences.server.ProxyPreference;
    55 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    5655import org.openstreetmap.josm.gui.util.GuiHelper;
    5756import org.openstreetmap.josm.io.DefaultProxySelector;
     
    404403
    405404        final SplashScreen splash = new SplashScreen();
    406         final ProgressMonitor monitor = splash.getProgressMonitor();
     405        final SplashScreen.SplashProgressMonitor monitor = splash.getProgressMonitor();
    407406        monitor.beginTask(tr("Initializing"));
    408407        splash.setVisible(Main.pref.getBoolean("draw.splashscreen", true));
     
    410409
    411410            @Override
    412             public void updateStatus(String event) {
    413                 monitor.indeterminateSubTask(event);
     411            public Object updateStatus(String event) {
     412                monitor.beginTask(event);
     413                return event;
     414            }
     415
     416            @Override
     417            public void finish(Object status) {
     418                if (status instanceof String) {
     419                    monitor.finishTask((String) status);
     420                }
    414421            }
    415422        });
  • trunk/src/org/openstreetmap/josm/gui/SplashScreen.java

    r8442 r8497  
    55
    66import java.awt.Color;
    7 import java.awt.Dimension;
     7import java.awt.Component;
     8import java.awt.Graphics;
    89import java.awt.GridBagConstraints;
    910import java.awt.GridBagLayout;
     
    1213import java.awt.event.MouseAdapter;
    1314import java.awt.event.MouseEvent;
    14 import java.util.Arrays;
    15 import java.util.LinkedList;
     15import java.util.ArrayList;
     16import java.util.Collections;
     17import java.util.List;
     18import java.util.Objects;
    1619
    1720import javax.swing.ImageIcon;
     
    2427import javax.swing.border.EmptyBorder;
    2528import javax.swing.border.EtchedBorder;
    26 
     29import javax.swing.event.ChangeEvent;
     30import javax.swing.event.ChangeListener;
     31
     32import org.openstreetmap.josm.Main;
    2733import org.openstreetmap.josm.data.Version;
    2834import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    29 import org.openstreetmap.josm.gui.progress.ProgressRenderer;
    30 import org.openstreetmap.josm.gui.progress.SwingRenderingProgressMonitor;
     35import org.openstreetmap.josm.gui.progress.ProgressTaskId;
    3136import org.openstreetmap.josm.gui.util.GuiHelper;
    3237import org.openstreetmap.josm.tools.ImageProvider;
     38import org.openstreetmap.josm.tools.Predicates;
    3339import org.openstreetmap.josm.tools.Utils;
    3440import org.openstreetmap.josm.tools.WindowGeometry;
     
    3844 * @since 976
    3945 */
    40 public class SplashScreen extends JFrame {
    41 
    42     private final transient SwingRenderingProgressMonitor progressMonitor;
     46public class SplashScreen extends JFrame implements ChangeListener {
     47
     48    private final SplashProgressMonitor progressMonitor;
     49    private final SplashScreenProgressRenderer progressRenderer;
    4350
    4451    /**
     
    9097
    9198        // Add a status message
    92         SplashScreenProgressRenderer progressRenderer = new SplashScreenProgressRenderer();
     99        progressRenderer = new SplashScreenProgressRenderer();
    93100        gbc.gridy = 3;
    94101        gbc.insets = new Insets(0, 0, 10, 0);
    95102        innerContentPane.add(progressRenderer, gbc);
    96         progressMonitor = new SwingRenderingProgressMonitor(progressRenderer);
     103        progressMonitor = new SplashProgressMonitor(null, this);
    97104
    98105        pack();
     
    109116    }
    110117
     118    @Override
     119    public void stateChanged(ChangeEvent ignore) {
     120        progressRenderer.setTasks("<html>"
     121                + "<style>ul {margin-top: 0; margin-bottom: 0; padding: 0;} li {margin: 0; padding: 0;}</style>"
     122                + "<body height='320'>"
     123                + progressMonitor.toString());
     124    }
     125
     126    /**
     127     * A task (of a {@link ProgressMonitor}).
     128     */
     129    private static abstract class Task {
     130
     131        /**
     132         * Returns a HTML representation for this task.
     133         */
     134        public abstract String toHtml();
     135
     136        @Override
     137        public final String toString() {
     138            return toHtml();
     139        }
     140    }
     141
     142    /**
     143     * A single task (of a {@link ProgressMonitor}) which keeps track of its execution duration
     144     * (requires a call to {@link #finish()}).
     145     */
     146    private static class MeasurableTask extends Task {
     147        private final String name;
     148        private final long start;
     149        private String duration = "";
     150
     151        public MeasurableTask(String name) {
     152            this.name = name;
     153            this.start = System.currentTimeMillis();
     154        }
     155
     156        public void finish() {
     157            if (!"".equals(duration)) {
     158                throw new IllegalStateException("This tasks has already been finished");
     159            }
     160            duration = tr(" ({0})", Utils.getDurationString(System.currentTimeMillis() - start));
     161        }
     162
     163        @Override
     164        public String toHtml() {
     165            return name + "<i style='color: #666666;'>" + duration + "</i>";
     166        }
     167
     168        @Override
     169        public boolean equals(Object o) {
     170            if (this == o) return true;
     171            if (o == null || getClass() != o.getClass()) return false;
     172            MeasurableTask that = (MeasurableTask) o;
     173            return Objects.equals(name, that.name);
     174        }
     175
     176        @Override
     177        public int hashCode() {
     178            return Objects.hashCode(name);
     179        }
     180    }
     181
     182    /**
     183     * A {@link ProgressMonitor} which stores the (sub)tasks in a tree.
     184     */
     185    public static class SplashProgressMonitor extends Task implements ProgressMonitor {
     186
     187        private final String name;
     188        private final ChangeListener listener;
     189        private final List<Task> tasks = Collections.synchronizedList(new ArrayList<Task>());
     190        private SplashProgressMonitor latestSubtask;
     191
     192        public SplashProgressMonitor(String name, ChangeListener listener) {
     193            this.name = name;
     194            this.listener = listener;
     195        }
     196
     197        @Override
     198        public String toHtml() {
     199            synchronized (tasks) {
     200                return Utils.firstNonNull(name, "") + (tasks.isEmpty() ? "" : Utils.joinAsHtmlUnorderedList(tasks));
     201            }
     202        }
     203
     204        @Override
     205        public void beginTask(String title) {
     206            if (title != null) {
     207                final MeasurableTask task = new MeasurableTask(title);
     208                tasks.add(task);
     209                listener.stateChanged(null);
     210            }
     211        }
     212
     213        @Override
     214        public void beginTask(String title, int ticks) {
     215            this.beginTask(title);
     216        }
     217
     218        @Override
     219        public void setCustomText(String text) {
     220            this.beginTask(text);
     221        }
     222
     223        @Override
     224        public void setExtraText(String text) {
     225            this.beginTask(text);
     226        }
     227
     228        @Override
     229        public void indeterminateSubTask(String title) {
     230            this.subTask(title);
     231        }
     232
     233        @Override
     234        public void subTask(String title) {
     235            latestSubtask = new SplashProgressMonitor(title, listener);
     236            tasks.add(latestSubtask);
     237            listener.stateChanged(null);
     238        }
     239
     240        @Override
     241        public ProgressMonitor createSubTaskMonitor(int ticks, boolean internal) {
     242            return latestSubtask;
     243        }
     244
     245        @Override
     246        @Deprecated
     247        public void finishTask() {
     248        }
     249
     250        public void finishTask(String title) {
     251            final Task task = Utils.find(tasks, Predicates.<Task>equalTo(new MeasurableTask(title)));
     252            if (task != null && task instanceof MeasurableTask) {
     253                ((MeasurableTask) task).finish();
     254                Main.debug(tr("{0} completed in {1}", title, ((MeasurableTask) task).duration));
     255                listener.stateChanged(null);
     256            }
     257        }
     258
     259        @Override
     260        public void invalidate() {
     261        }
     262
     263        @Override
     264        public void setTicksCount(int ticks) {
     265        }
     266
     267        @Override
     268        public int getTicksCount() {
     269            return 0;
     270        }
     271
     272        @Override
     273        public void setTicks(int ticks) {
     274        }
     275
     276        @Override
     277        public int getTicks() {
     278            return 0;
     279        }
     280
     281        @Override
     282        public void worked(int ticks) {
     283        }
     284
     285        @Override
     286        public boolean isCanceled() {
     287            return false;
     288        }
     289
     290        @Override
     291        public void cancel() {
     292        }
     293
     294        @Override
     295        public void addCancelListener(CancelListener listener) {
     296        }
     297
     298        @Override
     299        public void removeCancelListener(CancelListener listener) {
     300        }
     301
     302        @Override
     303        public void appendLogMessage(String message) {
     304        }
     305
     306        @Override
     307        public void setProgressTaskId(ProgressTaskId taskId) {
     308        }
     309
     310        @Override
     311        public ProgressTaskId getProgressTaskId() {
     312            return null;
     313        }
     314
     315        @Override
     316        public Component getWindowParent() {
     317            return Main.parent;
     318        }
     319    }
     320
    111321    /**
    112322     * Returns the progress monitor.
    113323     * @return The progress monitor
    114324     */
    115     public ProgressMonitor getProgressMonitor() {
     325    public SplashProgressMonitor getProgressMonitor() {
    116326        return progressMonitor;
    117327    }
    118328
    119     private static class SplashScreenProgressRenderer extends JPanel implements ProgressRenderer {
     329    private static class SplashScreenProgressRenderer extends JPanel {
    120330        private JLabel lblTaskTitle;
    121         private JLabel lblCustomText;
    122331        private JProgressBar progressBar;
    123332
     
    133342            add(lblTaskTitle = new JLabel(" "), gc);
    134343
    135             gc.gridx = 0;
    136344            gc.gridy = 1;
    137             gc.fill = GridBagConstraints.HORIZONTAL;
    138             gc.weightx = 1.0;
    139             gc.weighty = 0.0;
    140             gc.insets = new Insets(5,0,0,0);
    141             add(lblCustomText = new JLabel(" ") {
    142                 @Override
    143                 public Dimension getPreferredSize() {
    144                     Dimension d = super.getPreferredSize();
    145                     if(d.width < 600) d.width = 600;
    146                     d.height *= MAX_NUMBER_OF_MESSAGES;
    147                     return d;
    148                 }
    149             }, gc);
    150 
    151             gc.gridx = 0;
    152             gc.gridy = 2;
    153             gc.fill = GridBagConstraints.HORIZONTAL;
    154             gc.weightx = 1.0;
    155             gc.weighty = 0.0;
    156             gc.insets = new Insets(5,0,0,0);
     345            gc.insets = new Insets(15,0,0,0);
    157346            add(progressBar = new JProgressBar(JProgressBar.HORIZONTAL), gc);
     347            progressBar.setIndeterminate(true);
    158348        }
    159349
     
    163353
    164354        @Override
    165         public void setCustomText(String message) {
    166             if(message.isEmpty())
    167                 message = " "; // prevent killing of additional line
    168             lblCustomText.setText(message);
     355        public void paint(Graphics g) {
     356            try {
     357                super.paint(g);
     358            } catch (NullPointerException ignore) {
     359                // NullPointerException at javax.swing.text.html.StyleSheet$ListPainter.paint
     360            }
     361        }
     362
     363        public void setTasks(String tasks) {
     364            lblTaskTitle.setText(tasks);
    169365            repaint();
    170366        }
    171 
    172         @Override
    173         public void setIndeterminate(boolean indeterminate) {
    174             progressBar.setIndeterminate(indeterminate);
    175             repaint();
    176         }
    177 
    178         @Override
    179         public void setMaximum(int maximum) {
    180             progressBar.setMaximum(maximum);
    181             repaint();
    182         }
    183 
    184         private static final int MAX_NUMBER_OF_MESSAGES = 3;
    185         private LinkedList<String> messages = new LinkedList<>(Arrays.asList("", "", "")); //update when changing MAX_NUMBER_OF_MESSAGES
    186         private long time = System.currentTimeMillis();
    187 
    188         /**
    189          * Stores and displays the {@code MAX_NUMBER_OF_MESSAGES} most recent
    190          * task titles together with their execution time.
    191          */
    192         @Override
    193         public void setTaskTitle(String taskTitle) {
    194 
    195             while (messages.size() >= MAX_NUMBER_OF_MESSAGES) {
    196                 messages.removeFirst();
    197             }
    198             long now = System.currentTimeMillis();
    199             String prevMessageTitle = messages.getLast();
    200             // now should always be >= time but if can be inferior sometimes, see #10287
    201             if (!prevMessageTitle.isEmpty() && now >= time) {
    202                 messages.removeLast();
    203                 messages.add(tr("{0} ({1})", prevMessageTitle, Utils.getDurationString(now - time)));
    204             }
    205             time = now;
    206             if (!taskTitle.isEmpty()) {
    207                 messages.add(taskTitle);
    208             }
    209             StringBuilder html = new StringBuilder();
    210             int i = 0;
    211             for (String m : messages) {
    212                 html.append("<p class=\"entry").append(++i).append("\">").append(m).append("</p>");
    213             }
    214 
    215             lblTaskTitle.setText("<html><style>"
    216                     + ".entry1{color:#CCCCCC;}"
    217                     + ".entry2{color:#999999;}"
    218                     + ".entry3{color:#000000;}</style>" + html + "</html>");  //update when changing MAX_NUMBER_OF_MESSAGES
    219             repaint();
    220         }
    221 
    222         @Override
    223         public void setValue(int value) {
    224             progressBar.setValue(value);
    225             repaint();
    226         }
    227367    }
    228368}
Note: See TracChangeset for help on using the changeset viewer.