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}