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