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