source: josm/trunk/src/org/openstreetmap/josm/gui/preferences/PluginPreference.java@ 1047

Last change on this file since 1047 was 1028, checked in by stoecker, 16 years ago

allow resizing for plugin prefs

  • Property svn:eol-style set to native
File size: 12.8 KB
Line 
1//License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui.preferences;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.Dimension;
8import java.awt.Graphics;
9import java.awt.GridBagLayout;
10import java.awt.event.ActionEvent;
11import java.awt.event.ActionListener;
12import java.io.File;
13import java.io.FileReader;
14import java.util.Arrays;
15import java.util.Collection;
16import java.util.Collections;
17import java.util.Comparator;
18import java.util.HashMap;
19import java.util.HashSet;
20import java.util.LinkedList;
21import java.util.Map;
22import java.util.Set;
23import java.util.SortedMap;
24import java.util.TreeMap;
25import java.util.Map.Entry;
26
27import javax.swing.AbstractAction;
28import javax.swing.BorderFactory;
29import javax.swing.Box;
30import javax.swing.BoxLayout;
31import javax.swing.DefaultListModel;
32import javax.swing.JButton;
33import javax.swing.JCheckBox;
34import javax.swing.JLabel;
35import javax.swing.JList;
36import javax.swing.JOptionPane;
37import javax.swing.JPanel;
38import javax.swing.JScrollPane;
39
40import org.openstreetmap.josm.Main;
41import org.openstreetmap.josm.plugins.PluginDownloader;
42import org.openstreetmap.josm.plugins.PluginException;
43import org.openstreetmap.josm.plugins.PluginInformation;
44import org.openstreetmap.josm.plugins.PluginProxy;
45import org.openstreetmap.josm.tools.GBC;
46import org.openstreetmap.josm.tools.XmlObjectParser.Uniform;
47
48public class PluginPreference implements PreferenceSetting {
49
50 /**
51 * Only the plugin name, its jar location and the description.
52 * In other words, this is the minimal requirement the plugin preference page
53 * needs to show the plugin as available
54 *
55 * @author imi
56 */
57 public static class PluginDescription implements Comparable<Object> {
58 // Note: All the following need to be public instance variables of
59 // type String. (Plugin description XMLs from the server are parsed
60 // with tools.XmlObjectParser, which uses reflection to access them.)
61 public String name;
62 public String description;
63 public String resource;
64 public String version;
65 public PluginDescription(String name, String description, String resource, String version) {
66 this.name = name;
67 this.description = description;
68 this.resource = resource;
69 this.version = version;
70 }
71 public PluginDescription() {
72 }
73 public int compareTo(Object n) {
74 if(n instanceof PluginDescription)
75 return name.compareToIgnoreCase(((PluginDescription)n).name);
76 else
77 return -1;
78 }
79 }
80
81 private Map<PluginDescription, Boolean> pluginMap;
82 private JPanel plugin;
83 private class MyBox extends Box {
84 int lastwidth;
85 int offset = 40;
86 public MyBox()
87 {
88 super(BoxLayout.Y_AXIS);
89 }
90 public int myGetWidth()
91 {
92 int w = plugin.getWidth()-offset;
93 if(w <= 0) w = 450;
94 lastwidth = w;
95 return w;
96 }
97 public void paint(Graphics g)
98 {
99 if(lastwidth != plugin.getWidth()-offset)
100 refreshPluginPanel(gui);
101 super.paint(g);
102 }
103 }
104 private MyBox pluginPanel = new MyBox();
105 private PreferenceDialog gui;
106
107 public void addGui(final PreferenceDialog gui) {
108 this.gui = gui;
109 plugin = gui.createPreferenceTab("plugin", tr("Plugins"), tr("Configure available plugins."));
110 JScrollPane pluginPane = new JScrollPane(pluginPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
111 pluginPane.setBorder(null);
112 plugin.add(pluginPane, GBC.eol().fill(GBC.BOTH));
113 plugin.add(GBC.glue(0,10), GBC.eol());
114 JButton morePlugins = new JButton(tr("Download List"));
115 morePlugins.addActionListener(new ActionListener(){
116 public void actionPerformed(ActionEvent e) {
117 int count = PluginDownloader.downloadDescription();
118 if (count > 0)
119 JOptionPane.showMessageDialog(Main.parent,
120 trn("Downloaded plugin information from {0} site",
121 "Downloaded plugin information from {0} sites", count, count));
122 else
123 JOptionPane.showMessageDialog(Main.parent, tr("No plugin information found."));
124 refreshPluginPanel(gui);
125 }
126 });
127 plugin.add(morePlugins, GBC.std().insets(0,0,10,0));
128
129 JButton update = new JButton(tr("Update"));
130 update.addActionListener(new ActionListener(){
131 public void actionPerformed(ActionEvent e) {
132 update();
133 refreshPluginPanel(gui);
134 }
135
136 });
137 plugin.add(update, GBC.std().insets(0,0,10,0));
138
139 JButton configureSites = new JButton(tr("Configure Sites ..."));
140 configureSites.addActionListener(new ActionListener(){
141 public void actionPerformed(ActionEvent e) {
142 configureSites();
143 }
144
145 });
146 plugin.add(configureSites, GBC.std());
147
148 refreshPluginPanel(gui);
149 }
150
151 private void configureSites() {
152 JPanel p = new JPanel(new GridBagLayout());
153 p.add(new JLabel(tr("Add either site-josm.xml or Wiki Pages.")), GBC.eol());
154 final DefaultListModel model = new DefaultListModel();
155 for (String s : PluginDownloader.getSites())
156 model.addElement(s);
157 final JList list = new JList(model);
158 p.add(new JScrollPane(list), GBC.std().fill());
159 JPanel buttons = new JPanel(new GridBagLayout());
160 buttons.add(new JButton(new AbstractAction(tr("Add")){
161 public void actionPerformed(ActionEvent e) {
162 String s = JOptionPane.showInputDialog(gui, tr("Add either site-josm.xml or Wiki Pages."));
163 if (s != null)
164 model.addElement(s);
165 }
166 }), GBC.eol().fill(GBC.HORIZONTAL));
167 buttons.add(new JButton(new AbstractAction(tr("Edit")){
168 public void actionPerformed(ActionEvent e) {
169 if (list.getSelectedValue() == null) {
170 JOptionPane.showMessageDialog(gui, tr("Please select an entry."));
171 return;
172 }
173 String s = JOptionPane.showInputDialog(gui, tr("Add either site-josm.xml or Wiki Pages."), list.getSelectedValue());
174 model.setElementAt(s, list.getSelectedIndex());
175 }
176 }), GBC.eol().fill(GBC.HORIZONTAL));
177 buttons.add(new JButton(new AbstractAction(tr("Delete")){
178 public void actionPerformed(ActionEvent event) {
179 if (list.getSelectedValue() == null) {
180 JOptionPane.showMessageDialog(gui, tr("Please select an entry."));
181 return;
182 }
183 model.removeElement(list.getSelectedValue());
184 }
185 }), GBC.eol().fill(GBC.HORIZONTAL));
186 p.add(buttons, GBC.eol());
187 int answer = JOptionPane.showConfirmDialog(gui, p, tr("Configure Plugin Sites"), JOptionPane.OK_CANCEL_OPTION);
188 if (answer != JOptionPane.OK_OPTION)
189 return;
190 StringBuilder b = new StringBuilder();
191 for (int i = 0; i < model.getSize(); ++i) {
192 b.append(model.getElementAt(i));
193 if (i < model.getSize()-1)
194 b.append(" ");
195 }
196 Main.pref.put("pluginmanager.sites", b.toString());
197 }
198
199 private void update() {
200 // refresh description
201 PluginDownloader.downloadDescription();
202 refreshPluginPanel(gui);
203
204 Set<PluginDescription> toUpdate = new HashSet<PluginDescription>();
205 StringBuilder toUpdateStr = new StringBuilder();
206 for (PluginProxy proxy : Main.plugins) {
207 PluginDescription description = findDescription(proxy.info.name);
208 if (description != null && (description.version == null || description.version.equals("")) ? (proxy.info.version != null && proxy.info.version.equals("")) : !description.version.equals(proxy.info.version)) {
209 toUpdate.add(description);
210 toUpdateStr.append(description.name+"\n");
211 }
212 }
213 if (toUpdate.isEmpty()) {
214 JOptionPane.showMessageDialog(Main.parent, tr("All installed plugins are up to date."));
215 return;
216 }
217 int answer = JOptionPane.showConfirmDialog(Main.parent, tr("Update the following plugins:\n\n{0}", toUpdateStr.toString()), tr("Update"), JOptionPane.OK_CANCEL_OPTION);
218 if (answer != JOptionPane.OK_OPTION)
219 return;
220 PluginDownloader.update(toUpdate);
221 }
222
223 private PluginDescription findDescription(String name) {
224 for (PluginDescription d : pluginMap.keySet())
225 if (d.name.equals(name))
226 return d;
227 return null;
228 }
229
230 private void refreshPluginPanel(final PreferenceDialog gui) {
231 Collection<PluginDescription> availablePlugins = getAvailablePlugins();
232 pluginMap = new HashMap<PluginDescription, Boolean>();
233 pluginPanel.removeAll();
234 int width = pluginPanel.myGetWidth();
235
236 // the following could probably be done more elegantly?
237 Collection<String> enabledPlugins = null;
238 String enabledProp = Main.pref.get("plugins");
239 if ((enabledProp == null) || ("".equals(enabledProp))) {
240 enabledPlugins = Collections.emptySet();
241 }
242 else
243 {
244 enabledPlugins = Arrays.asList(enabledProp.split(","));
245 }
246
247 for (final PluginDescription plugin : availablePlugins) {
248 boolean enabled = enabledPlugins.contains(plugin.name);
249 String remoteversion = plugin.version;
250 if(remoteversion == null || remoteversion.equals(""))
251 remoteversion = tr("unknown");
252
253 String localversion;
254 PluginInformation p = PluginInformation.findPlugin(plugin.name);
255 if(p != null)
256 {
257 if(p.version != null && !p.version.equals(""))
258 localversion = p.version;
259 else
260 localversion = tr("unknown");
261 localversion = " (" + localversion + ")";
262 }
263 else
264 localversion = "";
265
266 final JCheckBox pluginCheck = new JCheckBox(tr("{0}: Version {1}{2}", plugin.name, remoteversion, localversion), enabled);
267 pluginPanel.add(pluginCheck);
268
269 pluginCheck.setToolTipText(plugin.resource != null ? ""+plugin.resource : tr("Plugin bundled with JOSM"));
270 JLabel label = new JLabel("<html><i>"+(plugin.description==null?tr("no description available"):plugin.description)+"</i></html>");
271 label.setBorder(BorderFactory.createEmptyBorder(0,20,0,0));
272 label.setMaximumSize(new Dimension(width,1000));
273 pluginPanel.add(label);
274 pluginPanel.add(Box.createVerticalStrut(5));
275
276 pluginMap.put(plugin, enabled);
277 pluginCheck.addActionListener(new ActionListener(){
278 public void actionPerformed(ActionEvent e) {
279 pluginMap.put(plugin, pluginCheck.isSelected());
280 }
281 });
282 }
283 plugin.updateUI();
284 }
285
286 private Collection<PluginDescription> getAvailablePlugins() {
287 SortedMap<String, PluginDescription> availablePlugins = new TreeMap<String, PluginDescription>(new Comparator<String>(){
288 public int compare(String o1, String o2) {
289 return o1.compareToIgnoreCase(o2);
290 }
291 });
292 for (String location : PluginInformation.getPluginLocations()) {
293 File[] pluginFiles = new File(location).listFiles();
294 if (pluginFiles != null) {
295 Arrays.sort(pluginFiles);
296 for (File f : pluginFiles) {
297 if (!f.isFile())
298 continue;
299 if (f.getName().endsWith(".jar")) {
300 try {
301 PluginInformation info = new PluginInformation(f);
302 if (!availablePlugins.containsKey(info.name))
303 availablePlugins.put(info.name, new PluginDescription(
304 info.name,
305 info.description,
306 PluginInformation.fileToURL(f).toString(),
307 info.version));
308 } catch (PluginException x) {
309 }
310 } else if (f.getName().matches("^[0-9]+-site.*\\.xml$")) {
311 try {
312 Uniform<PluginDescription> parser = new Uniform<PluginDescription>(new FileReader(f), "plugin", PluginDescription.class);
313 for (PluginDescription pd : parser)
314 if (!availablePlugins.containsKey(pd.name))
315 availablePlugins.put(pd.name, pd);
316 } catch (Exception e) {
317 e.printStackTrace();
318 JOptionPane.showMessageDialog(Main.parent, tr("Error reading plugin information file: {0}", f.getName()));
319 }
320 }
321 }
322 }
323 }
324 for (PluginProxy proxy : Main.plugins)
325 if (!availablePlugins.containsKey(proxy.info.name))
326 availablePlugins.put(proxy.info.name, new PluginDescription(
327 proxy.info.name,
328 proxy.info.description,
329 proxy.info.file == null ? null :
330 PluginInformation.fileToURL(proxy.info.file).toString(),
331 proxy.info.version));
332 return availablePlugins.values();
333 }
334
335 public void ok() {
336 Collection<PluginDescription> toDownload = new LinkedList<PluginDescription>();
337 String msg = "";
338 for (Entry<PluginDescription, Boolean> entry : pluginMap.entrySet()) {
339 if (entry.getValue() && PluginInformation.findPlugin(entry.getKey().name) == null) {
340 toDownload.add(entry.getKey());
341 msg += entry.getKey().name+"\n";
342 }
343 }
344 if (!toDownload.isEmpty()) {
345 int answer = JOptionPane.showConfirmDialog(Main.parent,
346 tr("Download the following plugins?\n\n{0}", msg),
347 tr("Download missing plugins"),
348 JOptionPane.YES_NO_OPTION);
349 if (answer != JOptionPane.OK_OPTION)
350 for (PluginDescription pd : toDownload)
351 pluginMap.put(pd, false);
352 else
353 for (PluginDescription pd : toDownload)
354 if (!PluginDownloader.downloadPlugin(pd))
355 pluginMap.put(pd, false);
356
357 }
358
359 String plugins = "";
360 Object pd[] = pluginMap.keySet().toArray();
361 Arrays.sort(pd);
362 for (Object d : pd) {
363 if (pluginMap.get(d))
364 plugins += ((PluginDescription)d).name + ",";
365 }
366 if (plugins.endsWith(","))
367 plugins = plugins.substring(0, plugins.length()-1);
368 if (plugins.length() == 0)
369 plugins = null;
370
371 String oldPlugins = Main.pref.get("plugins");
372 if(oldPlugins == null && plugins == null)
373 return;
374 if(plugins == null || oldPlugins == null || !plugins.equals(oldPlugins))
375 {
376 Main.pref.put("plugins", plugins);
377 gui.requiresRestart = true;
378 }
379 }
380}
Note: See TracBrowser for help on using the repository browser.