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.Image;
011import java.awt.Insets;
012import java.awt.event.MouseAdapter;
013import java.awt.event.MouseEvent;
014import java.util.Arrays;
015import java.util.LinkedList;
016
017import javax.swing.ImageIcon;
018import javax.swing.JFrame;
019import javax.swing.JLabel;
020import javax.swing.JPanel;
021import javax.swing.JProgressBar;
022import javax.swing.JSeparator;
023import javax.swing.border.Border;
024import javax.swing.border.EmptyBorder;
025import javax.swing.border.EtchedBorder;
026
027import org.openstreetmap.josm.data.Version;
028import org.openstreetmap.josm.gui.progress.ProgressMonitor;
029import org.openstreetmap.josm.gui.progress.ProgressRenderer;
030import org.openstreetmap.josm.gui.progress.SwingRenderingProgressMonitor;
031import org.openstreetmap.josm.gui.util.GuiHelper;
032import org.openstreetmap.josm.tools.ImageProvider;
033import org.openstreetmap.josm.tools.Utils;
034import org.openstreetmap.josm.tools.WindowGeometry;
035
036/**
037 * Show a splash screen so the user knows what is happening during startup.
038 * @since 976
039 */
040public class SplashScreen extends JFrame {
041
042    private final SwingRenderingProgressMonitor progressMonitor;
043
044    /**
045     * Constructs a new {@code SplashScreen}.
046     */
047    public SplashScreen() {
048        setUndecorated(true);
049
050        // Add a nice border to the main splash screen
051        JPanel contentPane = (JPanel)this.getContentPane();
052        Border margin = new EtchedBorder(1, Color.white, Color.gray);
053        contentPane.setBorder(margin);
054
055        // Add a margin from the border to the content
056        JPanel innerContentPane = new JPanel();
057        innerContentPane.setBorder(new EmptyBorder(10, 10, 2, 10));
058        contentPane.add(innerContentPane);
059        innerContentPane.setLayout(new GridBagLayout());
060
061        // Add the logo
062        JLabel logo = new JLabel(new ImageIcon(ImageProvider.get("logo.png").getImage().getScaledInstance(128, 129, Image.SCALE_SMOOTH)));
063        GridBagConstraints gbc = new GridBagConstraints();
064        gbc.gridheight = 2;
065        gbc.insets = new Insets(0, 0, 0, 70);
066        innerContentPane.add(logo, gbc);
067
068        // Add the name of this application
069        JLabel caption = new JLabel("JOSM ? " + tr("Java OpenStreetMap Editor"));
070        caption.setFont(GuiHelper.getTitleFont());
071        gbc.gridheight = 1;
072        gbc.gridx = 1;
073        gbc.insets = new Insets(30, 0, 0, 0);
074        innerContentPane.add(caption, gbc);
075
076        // Add the version number
077        JLabel version = new JLabel(tr("Version {0}", Version.getInstance().getVersionString()));
078        gbc.gridy = 1;
079        gbc.insets = new Insets(0, 0, 0, 0);
080        innerContentPane.add(version, gbc);
081
082        // Add a separator to the status text
083        JSeparator separator = new JSeparator(JSeparator.HORIZONTAL);
084        gbc.gridx = 0;
085        gbc.gridy = 2;
086        gbc.gridwidth = 2;
087        gbc.fill = GridBagConstraints.HORIZONTAL;
088        gbc.insets = new Insets(15, 0, 5, 0);
089        innerContentPane.add(separator, gbc);
090
091        // Add a status message
092        SplashScreenProgressRenderer progressRenderer = new SplashScreenProgressRenderer();
093        gbc.gridy = 3;
094        gbc.insets = new Insets(0, 0, 10, 0);
095        innerContentPane.add(progressRenderer, gbc);
096        progressMonitor = new SwingRenderingProgressMonitor(progressRenderer);
097
098        pack();
099
100        WindowGeometry.centerOnScreen(this.getSize(), "gui.geometry").applySafe(this);
101
102        // Add ability to hide splash screen by clicking it
103        addMouseListener(new MouseAdapter() {
104            @Override
105            public void mousePressed(MouseEvent event) {
106                setVisible(false);
107            }
108        });
109    }
110
111    /**
112     * Returns the progress monitor.
113     * @return The progress monitor
114     */
115    public ProgressMonitor getProgressMonitor() {
116        return progressMonitor;
117    }
118
119    private static class SplashScreenProgressRenderer extends JPanel implements ProgressRenderer {
120        private JLabel lblTaskTitle;
121        private JLabel lblCustomText;
122        private JProgressBar progressBar;
123
124        protected void build() {
125            setLayout(new GridBagLayout());
126            GridBagConstraints gc = new GridBagConstraints();
127            gc.gridx = 0;
128            gc.gridy = 0;
129            gc.fill = GridBagConstraints.HORIZONTAL;
130            gc.weightx = 1.0;
131            gc.weighty = 0.0;
132            gc.insets = new Insets(5,0,0,0);
133            add(lblTaskTitle = new JLabel(" "), gc);
134
135            gc.gridx = 0;
136            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);
157            add(progressBar = new JProgressBar(JProgressBar.HORIZONTAL), gc);
158        }
159
160        public SplashScreenProgressRenderer() {
161            build();
162        }
163
164        @Override
165        public void setCustomText(String message) {
166            if(message.isEmpty())
167                message = " "; // prevent killing of additional line
168            lblCustomText.setText(message);
169            repaint();
170        }
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        }
227    }
228}