001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.actions;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.awt.Color;
007import java.awt.Dimension;
008import java.awt.FlowLayout;
009import java.awt.GridBagLayout;
010import java.awt.event.ActionEvent;
011import java.awt.event.KeyEvent;
012import java.awt.event.MouseAdapter;
013import java.awt.event.MouseEvent;
014import java.io.BufferedReader;
015import java.io.IOException;
016import java.io.InputStream;
017import java.io.InputStreamReader;
018
019import javax.swing.BorderFactory;
020import javax.swing.JLabel;
021import javax.swing.JPanel;
022import javax.swing.JScrollPane;
023import javax.swing.JTabbedPane;
024import javax.swing.JTextArea;
025
026import org.openstreetmap.josm.Main;
027import org.openstreetmap.josm.data.Version;
028import org.openstreetmap.josm.gui.ExtendedDialog;
029import org.openstreetmap.josm.gui.MainApplication;
030import org.openstreetmap.josm.gui.util.GuiHelper;
031import org.openstreetmap.josm.gui.widgets.JMultilineLabel;
032import org.openstreetmap.josm.gui.widgets.JosmTextArea;
033import org.openstreetmap.josm.gui.widgets.UrlLabel;
034import org.openstreetmap.josm.plugins.PluginHandler;
035import org.openstreetmap.josm.tools.GBC;
036import org.openstreetmap.josm.tools.ImageProvider;
037import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
038import org.openstreetmap.josm.tools.Logging;
039import org.openstreetmap.josm.tools.OpenBrowser;
040import org.openstreetmap.josm.tools.Shortcut;
041import org.openstreetmap.josm.tools.Utils;
042
043/**
044 * Nice about screen.
045 *
046 * The REVISION resource is read and if present, it shows the revision information of the jar-file.
047 *
048 * @author imi
049 */
050public final class AboutAction extends JosmAction {
051
052    /**
053     * Constructs a new {@code AboutAction}.
054     */
055    public AboutAction() {
056        super(tr("About"), "logo", tr("Display the about screen."),
057            Shortcut.registerShortcut("system:about", tr("About"),
058            KeyEvent.VK_F1, Shortcut.SHIFT), true);
059    }
060
061    @Override
062    public void actionPerformed(ActionEvent e) {
063        final JTabbedPane about = new JTabbedPane();
064
065        Version version = Version.getInstance();
066
067        JosmTextArea readme = new JosmTextArea();
068        readme.setFont(GuiHelper.getMonospacedFont(readme));
069        readme.setEditable(false);
070        setTextFromResourceFile(readme, "/README");
071        readme.setCaretPosition(0);
072
073        JosmTextArea revision = new JosmTextArea();
074        revision.setFont(GuiHelper.getMonospacedFont(revision));
075        revision.setEditable(false);
076        revision.setText(version.getReleaseAttributes());
077        revision.setCaretPosition(0);
078
079        JosmTextArea contribution = new JosmTextArea();
080        contribution.setEditable(false);
081        setTextFromResourceFile(contribution, "/CONTRIBUTION");
082        contribution.setCaretPosition(0);
083
084        JosmTextArea license = new JosmTextArea();
085        license.setEditable(false);
086        setTextFromResourceFile(license, "/LICENSE");
087        license.setCaretPosition(0);
088
089        JPanel info = new JPanel(new GridBagLayout());
090        final JMultilineLabel label = new JMultilineLabel("<html>" +
091                "<h1>" + "JOSM – " + tr("Java OpenStreetMap Editor") + "</h1>" +
092                "<p style='font-size:75%'></p>" +
093                "<p>" + tr("Version {0}", version.getVersionString()) + "</p>" +
094                "<p style='font-size:50%'></p>" +
095                "<p>" + tr("Last change at {0}", version.getTime()) + "</p>" +
096                "<p style='font-size:50%'></p>" +
097                "<p>" + tr("Java Version {0}", Utils.getSystemProperty("java.version")) + "</p>" +
098                "<p style='font-size:50%'></p>" +
099                "</html>");
100        info.add(label, GBC.eol().fill(GBC.HORIZONTAL).insets(10, 0, 0, 10));
101        info.add(new JLabel(tr("Homepage")), GBC.std().insets(10, 0, 10, 0));
102        info.add(new UrlLabel(Main.getJOSMWebsite(), 2), GBC.eol());
103        info.add(new JLabel(tr("Translations")), GBC.std().insets(10, 0, 10, 0));
104        info.add(new UrlLabel("https://translations.launchpad.net/josm", 2), GBC.eol());
105        info.add(new JLabel(tr("Follow us on")), GBC.std().insets(10, 10, 10, 0));
106        JPanel logos = new JPanel(new FlowLayout());
107        logos.add(createImageLink("OpenStreetMap", /* ICON(dialogs/about/) */ "openstreetmap",
108                "https://www.openstreetmap.org/user/josmeditor/diary"));
109        logos.add(createImageLink("Twitter", /* ICON(dialogs/about/) */ "twitter", "https://twitter.com/josmeditor"));
110        logos.add(createImageLink("Facebook", /* ICON(dialogs/about/) */ "facebook", "https://www.facebook.com/josmeditor"));
111        logos.add(createImageLink("Google+", /* ICON(dialogs/about/) */ "google-plus", "https://plus.google.com/115458051662705872607"));
112        logos.add(createImageLink("Github", /* ICON(dialogs/about/) */ "github", "https://github.com/JOSM"));
113        info.add(logos, GBC.eol().insets(0, 10, 0, 0));
114        info.add(GBC.glue(0, 5), GBC.eol());
115
116        about.addTab(tr("Info"), info);
117        about.addTab(tr("Readme"), createScrollPane(readme));
118        about.addTab(tr("Revision"), createScrollPane(revision));
119        about.addTab(tr("Contribution"), createScrollPane(contribution));
120        about.addTab(tr("License"), createScrollPane(license));
121        about.addTab(tr("Plugins"), new JScrollPane(PluginHandler.getInfoPanel()));
122
123        // Get the list of Launchpad contributors using customary msgid “translator-credits”
124        String translators = tr("translator-credits");
125        if (translators != null && !translators.isEmpty() && !"translator-credits".equals(translators)) {
126            about.addTab(tr("Translators"), createScrollPane(new JosmTextArea(translators)));
127        }
128
129        // Intermediate panel to allow proper optionPane resizing
130        JPanel panel = new JPanel(new GridBagLayout());
131        panel.setPreferredSize(new Dimension(890, 300));
132        panel.add(new JLabel("", ImageProvider.get("logo.svg", ImageProvider.ImageSizes.ABOUT_LOGO),
133                JLabel.CENTER), GBC.std().insets(0, 5, 0, 0));
134        panel.add(about, GBC.std().fill());
135
136        GuiHelper.prepareResizeableOptionPane(panel, panel.getPreferredSize());
137        int ret = new ExtendedDialog(Main.parent, tr("About JOSM..."), tr("OK"), tr("Report bug"))
138            .setButtonIcons("ok", "bug")
139            .setContent(panel, false)
140            .showDialog().getValue();
141        if (2 == ret) {
142            MainApplication.getMenu().reportbug.actionPerformed(null);
143        }
144    }
145
146    private static JLabel createImageLink(String tooltip, String icon, final String link) {
147        JLabel label = new JLabel(ImageProvider.get("dialogs/about", icon, ImageSizes.LARGEICON));
148        label.setToolTipText(tooltip);
149        label.addMouseListener(new MouseAdapter() {
150            @Override
151            public void mouseClicked(MouseEvent e) {
152                OpenBrowser.displayUrl(link);
153            }
154        });
155        return label;
156    }
157
158    /**
159     * Reads the contents of the resource file that is described by the {@code filePath}-attribute and puts that text
160     * into the {@link JTextArea} given by the {@code ta}-attribute.
161     * @param ta the {@link JTextArea} to put the files contents into
162     * @param filePath the path where the resource file to read resides
163     */
164    private void setTextFromResourceFile(JTextArea ta, String filePath) {
165        InputStream is = getClass().getResourceAsStream(filePath);
166        if (is == null) {
167            displayErrorMessage(ta, tr("Failed to locate resource ''{0}''.", filePath));
168        } else {
169            try (InputStreamReader reader = new InputStreamReader(is, "UTF-8");
170                 BufferedReader br = new BufferedReader(reader)) {
171                String line;
172                while ((line = br.readLine()) != null) {
173                    ta.append(line+'\n');
174                }
175            } catch (IOException e) {
176                Logging.warn(e);
177                displayErrorMessage(ta, tr("Failed to load resource ''{0}'', error is {1}.", filePath, e.toString()));
178            }
179        }
180    }
181
182    private static void displayErrorMessage(JTextArea ta, String msg) {
183        Logging.warn(msg);
184        ta.setForeground(new Color(200, 0, 0));
185        ta.setText(msg);
186    }
187
188    private static JScrollPane createScrollPane(JosmTextArea area) {
189        area.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
190        area.setOpaque(false);
191        JScrollPane sp = new JScrollPane(area);
192        sp.setBorder(null);
193        sp.setOpaque(false);
194        return sp;
195    }
196}