001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.preferences.plugin; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.GridBagConstraints; 007import java.awt.GridBagLayout; 008import java.awt.Insets; 009import java.awt.Rectangle; 010import java.awt.event.MouseAdapter; 011import java.awt.event.MouseEvent; 012import java.util.List; 013 014import javax.swing.JLabel; 015import javax.swing.SwingConstants; 016import javax.swing.SwingUtilities; 017import javax.swing.event.HyperlinkEvent; 018import javax.swing.event.HyperlinkEvent.EventType; 019import javax.swing.event.HyperlinkListener; 020 021import org.openstreetmap.josm.gui.widgets.HtmlPanel; 022import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel; 023import org.openstreetmap.josm.plugins.PluginInformation; 024import org.openstreetmap.josm.tools.OpenBrowser; 025 026/** 027 * A panel displaying the list of known plugins. 028 */ 029public class PluginListPanel extends VerticallyScrollablePanel { 030 private transient PluginPreferencesModel model; 031 032 /** 033 * Constructs a new {@code PluginListPanel} with a default model. 034 */ 035 public PluginListPanel() { 036 this(new PluginPreferencesModel()); 037 } 038 039 /** 040 * Constructs a new {@code PluginListPanel} with a given model. 041 * @param model The plugin model 042 */ 043 public PluginListPanel(PluginPreferencesModel model) { 044 this.model = model; 045 setLayout(new GridBagLayout()); 046 } 047 048 protected static String formatPluginRemoteVersion(PluginInformation pi) { 049 StringBuilder sb = new StringBuilder(); 050 if (pi.version == null || pi.version.trim().isEmpty()) { 051 sb.append(tr("unknown")); 052 } else { 053 sb.append(pi.version); 054 if (pi.oldmode) { 055 sb.append('*'); 056 } 057 } 058 return sb.toString(); 059 } 060 061 protected static String formatPluginLocalVersion(PluginInformation pi) { 062 if (pi == null) 063 return tr("unknown"); 064 if (pi.localversion == null || pi.localversion.trim().isEmpty()) 065 return tr("unknown"); 066 return pi.localversion; 067 } 068 069 protected static String formatCheckboxTooltipText(PluginInformation pi) { 070 if (pi == null) 071 return ""; 072 if (pi.downloadlink == null) 073 return tr("Plugin bundled with JOSM"); 074 else 075 return pi.downloadlink; 076 } 077 078 /** 079 * Displays a message when the plugin list is empty. 080 */ 081 public void displayEmptyPluginListInformation() { 082 GridBagConstraints gbc = new GridBagConstraints(); 083 gbc.gridx = 0; 084 gbc.anchor = GridBagConstraints.CENTER; 085 gbc.fill = GridBagConstraints.BOTH; 086 gbc.insets = new Insets(40, 0, 40, 0); 087 gbc.weightx = 1.0; 088 gbc.weighty = 1.0; 089 090 HtmlPanel hint = new HtmlPanel(); 091 hint.setText( 092 "<html>" 093 + tr("Please click on <strong>Download list</strong> to download and display a list of available plugins.") 094 + "</html>" 095 ); 096 add(hint, gbc); 097 } 098 099 /** 100 * Refreshes the list. 101 */ 102 public void refreshView() { 103 final Rectangle visibleRect = getVisibleRect(); 104 List<PluginInformation> displayedPlugins = model.getDisplayedPlugins(); 105 removeAll(); 106 107 GridBagConstraints gbc = new GridBagConstraints(); 108 gbc.gridx = 0; 109 gbc.anchor = GridBagConstraints.NORTHWEST; 110 gbc.fill = GridBagConstraints.HORIZONTAL; 111 gbc.weightx = 1.0; 112 113 if (displayedPlugins.isEmpty()) { 114 displayEmptyPluginListInformation(); 115 return; 116 } 117 118 int row = -1; 119 for (final PluginInformation pi : displayedPlugins) { 120 boolean selected = model.isSelectedPlugin(pi.getName()); 121 String remoteversion = formatPluginRemoteVersion(pi); 122 String localversion = formatPluginLocalVersion(model.getPluginInformation(pi.getName())); 123 124 final PluginCheckBox cbPlugin = new PluginCheckBox(pi, selected, this, model); 125 String pluginText = tr("{0}: Version {1} (local: {2})", pi.getName(), remoteversion, localversion); 126 if (pi.requires != null && !pi.requires.isEmpty()) { 127 pluginText += tr(" (requires: {0})", pi.requires); 128 } 129 JLabel lblPlugin = new JLabel( 130 pluginText, 131 pi.getScaledIcon(), 132 SwingConstants.LEFT); 133 lblPlugin.addMouseListener(new MouseAdapter() { 134 @Override 135 public void mouseClicked(MouseEvent e) { 136 cbPlugin.doClick(); 137 } 138 }); 139 140 gbc.gridx = 0; 141 gbc.gridy = ++row; 142 gbc.insets = new Insets(5, 5, 0, 5); 143 gbc.weighty = 0.0; 144 gbc.weightx = 0.0; 145 add(cbPlugin, gbc); 146 147 gbc.gridx = 1; 148 gbc.weightx = 1.0; 149 add(lblPlugin, gbc); 150 151 HtmlPanel description = new HtmlPanel(); 152 description.setText(pi.getDescriptionAsHtml()); 153 description.getEditorPane().addHyperlinkListener(new HyperlinkListener() { 154 @Override 155 public void hyperlinkUpdate(HyperlinkEvent e) { 156 if (e.getEventType() == EventType.ACTIVATED) { 157 OpenBrowser.displayUrl(e.getURL().toString()); 158 } 159 } 160 }); 161 lblPlugin.setLabelFor(description); 162 163 gbc.gridx = 1; 164 gbc.gridy = ++row; 165 gbc.insets = new Insets(3, 25, 5, 5); 166 gbc.weighty = 1.0; 167 add(description, gbc); 168 } 169 revalidate(); 170 repaint(); 171 if (visibleRect != null && visibleRect.width > 0 && visibleRect.height > 0) { 172 SwingUtilities.invokeLater(new Runnable() { 173 @Override 174 public void run() { 175 scrollRectToVisible(visibleRect); 176 } 177 }); 178 } 179 } 180}