001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Color;
007import java.awt.Dimension;
008import java.awt.GridBagConstraints;
009import java.awt.GridBagLayout;
010import java.awt.Insets;
011import java.awt.event.MouseAdapter;
012import java.awt.event.MouseEvent;
013import java.util.Arrays;
014import java.util.LinkedList;
015
016import javax.swing.JFrame;
017import javax.swing.JLabel;
018import javax.swing.JPanel;
019import javax.swing.JProgressBar;
020import javax.swing.JSeparator;
021import javax.swing.border.Border;
022import javax.swing.border.EmptyBorder;
023import javax.swing.border.EtchedBorder;
024
025import org.openstreetmap.josm.data.Version;
026import org.openstreetmap.josm.gui.progress.ProgressMonitor;
027import org.openstreetmap.josm.gui.progress.ProgressRenderer;
028import org.openstreetmap.josm.gui.progress.SwingRenderingProgressMonitor;
029import org.openstreetmap.josm.gui.util.GuiHelper;
030import org.openstreetmap.josm.tools.ImageProvider;
031import org.openstreetmap.josm.tools.Utils;
032import org.openstreetmap.josm.tools.WindowGeometry;
033
034/**
035 * Show a splash screen so the user knows what is happening during startup.
036 *
037 */
038public class SplashScreen extends JFrame {
039
040    private final SwingRenderingProgressMonitor progressMonitor;
041
042    /**
043     * Constructs a new {@code SplashScreen}.
044     */
045    public SplashScreen() {
046        super();
047        setUndecorated(true);
048
049        // Add a nice border to the main splash screen
050        JPanel contentPane = (JPanel)this.getContentPane();
051        Border margin = new EtchedBorder(1, Color.white, Color.gray);
052        contentPane.setBorder(margin);
053
054        // Add a margin from the border to the content
055        JPanel innerContentPane = new JPanel();
056        innerContentPane.setBorder(new EmptyBorder(10, 10, 2, 10));
057        contentPane.add(innerContentPane);
058        innerContentPane.setLayout(new GridBagLayout());
059
060        // Add the logo
061        JLabel logo = new JLabel(ImageProvider.get("logo.png"));
062        GridBagConstraints gbc = new GridBagConstraints();
063        gbc.gridheight = 2;
064        gbc.insets = new Insets(0, 0, 0, 70);
065        innerContentPane.add(logo, gbc);
066
067        // Add the name of this application
068        JLabel caption = new JLabel("JOSM – " + tr("Java OpenStreetMap Editor"));
069        caption.setFont(GuiHelper.getTitleFont());
070        gbc.gridheight = 1;
071        gbc.gridx = 1;
072        gbc.insets = new Insets(30, 0, 0, 0);
073        innerContentPane.add(caption, gbc);
074
075        // Add the version number
076        JLabel version = new JLabel(tr("Version {0}", Version.getInstance().getVersionString()));
077        gbc.gridy = 1;
078        gbc.insets = new Insets(0, 0, 0, 0);
079        innerContentPane.add(version, gbc);
080
081        // Add a separator to the status text
082        JSeparator separator = new JSeparator(JSeparator.HORIZONTAL);
083        gbc.gridx = 0;
084        gbc.gridy = 2;
085        gbc.gridwidth = 2;
086        gbc.fill = GridBagConstraints.HORIZONTAL;
087        gbc.insets = new Insets(15, 0, 5, 0);
088        innerContentPane.add(separator, gbc);
089
090        // Add a status message
091        SplashScreenProgressRenderer progressRenderer = new SplashScreenProgressRenderer();
092        gbc.gridy = 3;
093        gbc.insets = new Insets(0, 0, 10, 0);
094        innerContentPane.add(progressRenderer, gbc);
095        progressMonitor = new SwingRenderingProgressMonitor(progressRenderer);
096
097        pack();
098
099        WindowGeometry.centerOnScreen(this.getSize(), "gui.geometry").applySafe(this);
100
101        // Add ability to hide splash screen by clicking it
102        addMouseListener(new MouseAdapter() {
103            @Override
104            public void mousePressed(MouseEvent event) {
105                setVisible(false);
106            }
107        });
108    }
109
110    /**
111     * Returns the progress monitor.
112     * @return The progress monitor
113     */
114    public ProgressMonitor getProgressMonitor() {
115        return progressMonitor;
116    }
117
118    private static class SplashScreenProgressRenderer extends JPanel implements ProgressRenderer {
119        private JLabel lblTaskTitle;
120        private JLabel lblCustomText;
121        private JProgressBar progressBar;
122
123        protected void build() {
124            setLayout(new GridBagLayout());
125            GridBagConstraints gc = new GridBagConstraints();
126            gc.gridx = 0;
127            gc.gridy = 0;
128            gc.fill = GridBagConstraints.HORIZONTAL;
129            gc.weightx = 1.0;
130            gc.weighty = 0.0;
131            gc.insets = new Insets(5,0,0,0);
132            add(lblTaskTitle = new JLabel(" "), gc);
133
134            gc.gridx = 0;
135            gc.gridy = 1;
136            gc.fill = GridBagConstraints.HORIZONTAL;
137            gc.weightx = 1.0;
138            gc.weighty = 0.0;
139            gc.insets = new Insets(5,0,0,0);
140            add(lblCustomText = new JLabel(" ") {
141                @Override
142                public Dimension getPreferredSize() {
143                    Dimension d = super.getPreferredSize();
144                    if(d.width < 600) d.width = 600;
145                    d.height *= MAX_NUMBER_OF_MESSAGES;
146                    return d;
147                }
148            }, gc);
149
150            gc.gridx = 0;
151            gc.gridy = 2;
152            gc.fill = GridBagConstraints.HORIZONTAL;
153            gc.weightx = 1.0;
154            gc.weighty = 0.0;
155            gc.insets = new Insets(5,0,0,0);
156            add(progressBar = new JProgressBar(JProgressBar.HORIZONTAL), gc);
157        }
158
159        public SplashScreenProgressRenderer() {
160            build();
161        }
162
163        @Override
164        public void setCustomText(String message) {
165            if(message.isEmpty())
166                message = " "; // prevent killing of additional line
167            lblCustomText.setText(message);
168            repaint();
169        }
170
171        @Override
172        public void setIndeterminate(boolean indeterminate) {
173            progressBar.setIndeterminate(indeterminate);
174            repaint();
175        }
176
177        @Override
178        public void setMaximum(int maximum) {
179            progressBar.setMaximum(maximum);
180            repaint();
181        }
182
183        private static final int MAX_NUMBER_OF_MESSAGES = 3;
184        private LinkedList<String> messages = new LinkedList<>(Arrays.asList("", "", "")); //update when changing MAX_NUMBER_OF_MESSAGES
185        private long time = System.currentTimeMillis();
186
187        /**
188         * Stores and displays the {@code MAX_NUMBER_OF_MESSAGES} most recent
189         * task titles together with their execution time.
190         */
191        @Override
192        public void setTaskTitle(String taskTitle) {
193
194            while (messages.size() >= MAX_NUMBER_OF_MESSAGES) {
195                messages.removeFirst();
196            }
197            long now = System.currentTimeMillis();
198            String prevMessageTitle = messages.getLast();
199            // now should always be >= time but if can be inferior sometimes, see #10287
200            if (!prevMessageTitle.isEmpty() && now >= time) {
201                messages.removeLast();
202                messages.add(tr("{0} ({1})", prevMessageTitle, Utils.getDurationString(now - time)));
203            }
204            time = now;
205            if (!taskTitle.isEmpty()) {
206                messages.add(taskTitle);
207            }
208            StringBuilder html = new StringBuilder();
209            int i = 0;
210            for (String m : messages) {
211                html.append("<p class=\"entry").append(++i).append("\">").append(m).append("</p>");
212            }
213
214            lblTaskTitle.setText("<html><style>"
215                    + ".entry1{color:#CCCCCC;}"
216                    + ".entry2{color:#999999;}"
217                    + ".entry3{color:#000000;}</style>" + html + "</html>");  //update when changing MAX_NUMBER_OF_MESSAGES
218            repaint();
219        }
220
221        @Override
222        public void setValue(int value) {
223            progressBar.setValue(value);
224            repaint();
225        }
226    }
227}