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

Last change on this file since 6529 was 6529, checked in by Don-vip, 10 years ago

Various stuff:

  • see #9414: remove old DeprecatedTags test
  • refactor some classes in gui.preferences package
  • improve javadoc
  • Property svn:eol-style set to native
File size: 21.7 KB
Line 
1//License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences.plugin;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.awt.BorderLayout;
8import java.awt.GridBagConstraints;
9import java.awt.GridBagLayout;
10import java.awt.GridLayout;
11import java.awt.Insets;
12import java.awt.event.ActionEvent;
13import java.awt.event.ComponentAdapter;
14import java.awt.event.ComponentEvent;
15import java.util.ArrayList;
16import java.util.Collection;
17import java.util.Collections;
18import java.util.Iterator;
19import java.util.LinkedList;
20import java.util.List;
21
22import javax.swing.AbstractAction;
23import javax.swing.BorderFactory;
24import javax.swing.DefaultListModel;
25import javax.swing.JButton;
26import javax.swing.JLabel;
27import javax.swing.JList;
28import javax.swing.JOptionPane;
29import javax.swing.JPanel;
30import javax.swing.JScrollPane;
31import javax.swing.JTabbedPane;
32import javax.swing.SwingUtilities;
33import javax.swing.UIManager;
34import javax.swing.event.DocumentEvent;
35import javax.swing.event.DocumentListener;
36
37import org.openstreetmap.josm.Main;
38import org.openstreetmap.josm.data.Version;
39import org.openstreetmap.josm.gui.HelpAwareOptionPane;
40import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
41import org.openstreetmap.josm.gui.help.HelpUtil;
42import org.openstreetmap.josm.gui.preferences.DefaultTabPreferenceSetting;
43import org.openstreetmap.josm.gui.preferences.PreferenceSetting;
44import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory;
45import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
46import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.PreferencePanel;
47import org.openstreetmap.josm.gui.util.GuiHelper;
48import org.openstreetmap.josm.gui.widgets.JosmTextField;
49import org.openstreetmap.josm.gui.widgets.SelectAllOnFocusGainedDecorator;
50import org.openstreetmap.josm.plugins.PluginDownloadTask;
51import org.openstreetmap.josm.plugins.PluginInformation;
52import org.openstreetmap.josm.plugins.ReadLocalPluginInformationTask;
53import org.openstreetmap.josm.plugins.ReadRemotePluginInformationTask;
54import org.openstreetmap.josm.tools.GBC;
55import org.openstreetmap.josm.tools.ImageProvider;
56
57public final class PluginPreference extends DefaultTabPreferenceSetting {
58
59 /**
60 * Factory used to create a new {@code PluginPreference}.
61 */
62 public static class Factory implements PreferenceSettingFactory {
63 @Override
64 public PreferenceSetting createPreferenceSetting() {
65 return new PluginPreference();
66 }
67 }
68
69 private PluginPreference() {
70 super("plugin", tr("Plugins"), tr("Configure available plugins."), false, new JTabbedPane());
71 }
72
73 public static String buildDownloadSummary(PluginDownloadTask task) {
74 Collection<PluginInformation> downloaded = task.getDownloadedPlugins();
75 Collection<PluginInformation> failed = task.getFailedPlugins();
76 StringBuilder sb = new StringBuilder();
77 if (! downloaded.isEmpty()) {
78 sb.append(trn(
79 "The following plugin has been downloaded <strong>successfully</strong>:",
80 "The following {0} plugins have been downloaded <strong>successfully</strong>:",
81 downloaded.size(),
82 downloaded.size()
83 ));
84 sb.append("<ul>");
85 for(PluginInformation pi: downloaded) {
86 sb.append("<li>").append(pi.name).append(" (").append(pi.version).append(")").append("</li>");
87 }
88 sb.append("</ul>");
89 }
90 if (! failed.isEmpty()) {
91 sb.append(trn(
92 "Downloading the following plugin has <strong>failed</strong>:",
93 "Downloading the following {0} plugins has <strong>failed</strong>:",
94 failed.size(),
95 failed.size()
96 ));
97 sb.append("<ul>");
98 for(PluginInformation pi: failed) {
99 sb.append("<li>").append(pi.name).append("</li>");
100 }
101 sb.append("</ul>");
102 }
103 return sb.toString();
104 }
105
106 private JosmTextField tfFilter;
107 private PluginListPanel pnlPluginPreferences;
108 private PluginPreferencesModel model;
109 private JScrollPane spPluginPreferences;
110 private PluginUpdatePolicyPanel pnlPluginUpdatePolicy;
111
112 /**
113 * is set to true if this preference pane has been selected
114 * by the user
115 */
116 private boolean pluginPreferencesActivated = false;
117
118 protected JPanel buildSearchFieldPanel() {
119 JPanel pnl = new JPanel(new GridBagLayout());
120 pnl.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
121 GridBagConstraints gc = new GridBagConstraints();
122
123 gc.anchor = GridBagConstraints.NORTHWEST;
124 gc.fill = GridBagConstraints.HORIZONTAL;
125 gc.weightx = 0.0;
126 gc.insets = new Insets(0,0,0,3);
127 pnl.add(new JLabel(tr("Search:")), gc);
128
129 gc.gridx = 1;
130 gc.weightx = 1.0;
131 pnl.add(tfFilter = new JosmTextField(), gc);
132 tfFilter.setToolTipText(tr("Enter a search expression"));
133 SelectAllOnFocusGainedDecorator.decorate(tfFilter);
134 tfFilter.getDocument().addDocumentListener(new SearchFieldAdapter());
135 return pnl;
136 }
137
138 protected JPanel buildActionPanel() {
139 JPanel pnl = new JPanel(new GridLayout(1,3));
140
141 pnl.add(new JButton(new DownloadAvailablePluginsAction()));
142 pnl.add(new JButton(new UpdateSelectedPluginsAction()));
143 pnl.add(new JButton(new ConfigureSitesAction()));
144 return pnl;
145 }
146
147 protected JPanel buildPluginListPanel() {
148 JPanel pnl = new JPanel(new BorderLayout());
149 pnl.add(buildSearchFieldPanel(), BorderLayout.NORTH);
150 model = new PluginPreferencesModel();
151 spPluginPreferences = new JScrollPane(pnlPluginPreferences = new PluginListPanel(model));
152 spPluginPreferences.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
153 spPluginPreferences.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
154 spPluginPreferences.getVerticalScrollBar().addComponentListener(
155 new ComponentAdapter(){
156 @Override
157 public void componentShown(ComponentEvent e) {
158 spPluginPreferences.setBorder(UIManager.getBorder("ScrollPane.border"));
159 }
160 @Override
161 public void componentHidden(ComponentEvent e) {
162 spPluginPreferences.setBorder(null);
163 }
164 }
165 );
166
167 pnl.add(spPluginPreferences, BorderLayout.CENTER);
168 pnl.add(buildActionPanel(), BorderLayout.SOUTH);
169 return pnl;
170 }
171
172 protected JTabbedPane buildContentPane() {
173 JTabbedPane pane = getTabPane();
174 pane.addTab(tr("Plugins"), buildPluginListPanel());
175 pane.addTab(tr("Plugin update policy"), pnlPluginUpdatePolicy = new PluginUpdatePolicyPanel());
176 return pane;
177 }
178
179 @Override
180 public void addGui(final PreferenceTabbedPane gui) {
181 GridBagConstraints gc = new GridBagConstraints();
182 gc.weightx = 1.0;
183 gc.weighty = 1.0;
184 gc.anchor = GridBagConstraints.NORTHWEST;
185 gc.fill = GridBagConstraints.BOTH;
186 PreferencePanel plugins = gui.createPreferenceTab(this);
187 plugins.add(buildContentPane(), gc);
188 readLocalPluginInformation();
189 pluginPreferencesActivated = true;
190 }
191
192 private void configureSites() {
193 ButtonSpec[] options = new ButtonSpec[] {
194 new ButtonSpec(
195 tr("OK"),
196 ImageProvider.get("ok"),
197 tr("Accept the new plugin sites and close the dialog"),
198 null /* no special help topic */
199 ),
200 new ButtonSpec(
201 tr("Cancel"),
202 ImageProvider.get("cancel"),
203 tr("Close the dialog"),
204 null /* no special help topic */
205 )
206 };
207 PluginConfigurationSitesPanel pnl = new PluginConfigurationSitesPanel();
208
209 int answer = HelpAwareOptionPane.showOptionDialog(
210 pnlPluginPreferences,
211 pnl,
212 tr("Configure Plugin Sites"),
213 JOptionPane.QUESTION_MESSAGE,
214 null,
215 options,
216 options[0],
217 null /* no help topic */
218 );
219 if (answer != 0 /* OK */)
220 return;
221 List<String> sites = pnl.getUpdateSites();
222 Main.pref.setPluginSites(sites);
223 }
224
225 /**
226 * Replies the list of plugins waiting for update or download
227 *
228 * @return the list of plugins waiting for update or download
229 */
230 public List<PluginInformation> getPluginsScheduledForUpdateOrDownload() {
231 return model != null ? model.getPluginsScheduledForUpdateOrDownload() : null;
232 }
233
234 @Override
235 public boolean ok() {
236 if (! pluginPreferencesActivated)
237 return false;
238 pnlPluginUpdatePolicy.rememberInPreferences();
239 if (model.isActivePluginsChanged()) {
240 LinkedList<String> l = new LinkedList<String>(model.getSelectedPluginNames());
241 Collections.sort(l);
242 Main.pref.putCollection("plugins", l);
243 return true;
244 }
245 return false;
246 }
247
248 /**
249 * Reads locally available information about plugins from the local file system.
250 * Scans cached plugin lists from plugin download sites and locally available
251 * plugin jar files.
252 *
253 */
254 public void readLocalPluginInformation() {
255 final ReadLocalPluginInformationTask task = new ReadLocalPluginInformationTask();
256 Runnable r = new Runnable() {
257 @Override
258 public void run() {
259 if (task.isCanceled()) return;
260 SwingUtilities.invokeLater(new Runnable() {
261 @Override
262 public void run() {
263 model.setAvailablePlugins(task.getAvailablePlugins());
264 pnlPluginPreferences.refreshView();
265 }
266 });
267 }
268 };
269 Main.worker.submit(task);
270 Main.worker.submit(r);
271 }
272
273 /**
274 * The action for downloading the list of available plugins
275 *
276 */
277 class DownloadAvailablePluginsAction extends AbstractAction {
278
279 public DownloadAvailablePluginsAction() {
280 putValue(NAME,tr("Download list"));
281 putValue(SHORT_DESCRIPTION, tr("Download the list of available plugins"));
282 putValue(SMALL_ICON, ImageProvider.get("download"));
283 }
284
285 @Override
286 public void actionPerformed(ActionEvent e) {
287 final ReadRemotePluginInformationTask task = new ReadRemotePluginInformationTask(Main.pref.getPluginSites());
288 Runnable continuation = new Runnable() {
289 @Override
290 public void run() {
291 if (task.isCanceled()) return;
292 SwingUtilities.invokeLater(new Runnable() {
293 @Override
294 public void run() {
295 model.updateAvailablePlugins(task.getAvailablePlugins());
296 pnlPluginPreferences.refreshView();
297 Main.pref.putInteger("pluginmanager.version", Version.getInstance().getVersion()); // fix #7030
298 }
299 });
300 }
301 };
302 Main.worker.submit(task);
303 Main.worker.submit(continuation);
304 }
305 }
306
307 /**
308 * The action for downloading the list of available plugins
309 *
310 */
311 class UpdateSelectedPluginsAction extends AbstractAction {
312 public UpdateSelectedPluginsAction() {
313 putValue(NAME,tr("Update plugins"));
314 putValue(SHORT_DESCRIPTION, tr("Update the selected plugins"));
315 putValue(SMALL_ICON, ImageProvider.get("dialogs", "refresh"));
316 }
317
318 protected void notifyDownloadResults(PluginDownloadTask task) {
319 final Collection<PluginInformation> downloaded = task.getDownloadedPlugins();
320 final Collection<PluginInformation> failed = task.getFailedPlugins();
321 final StringBuilder sb = new StringBuilder();
322 sb.append("<html>");
323 sb.append(buildDownloadSummary(task));
324 if (!downloaded.isEmpty()) {
325 sb.append(tr("Please restart JOSM to activate the downloaded plugins."));
326 }
327 sb.append("</html>");
328 GuiHelper.runInEDTAndWait(new Runnable() {
329 @Override
330 public void run() {
331 HelpAwareOptionPane.showOptionDialog(
332 pnlPluginPreferences,
333 sb.toString(),
334 tr("Update plugins"),
335 !failed.isEmpty() ? JOptionPane.WARNING_MESSAGE : JOptionPane.INFORMATION_MESSAGE,
336 HelpUtil.ht("/Preferences/Plugins")
337 );
338 }
339 });
340 }
341
342 protected void alertNothingToUpdate() {
343 try {
344 SwingUtilities.invokeAndWait(new Runnable() {
345 @Override
346 public void run() {
347 HelpAwareOptionPane.showOptionDialog(
348 pnlPluginPreferences,
349 tr("All installed plugins are up to date. JOSM does not have to download newer versions."),
350 tr("Plugins up to date"),
351 JOptionPane.INFORMATION_MESSAGE,
352 null // FIXME: provide help context
353 );
354 }
355 });
356 } catch (Exception e) {
357 e.printStackTrace();
358 }
359 }
360
361 @Override
362 public void actionPerformed(ActionEvent e) {
363 final List<PluginInformation> toUpdate = model.getSelectedPlugins();
364 // the async task for downloading plugins
365 final PluginDownloadTask pluginDownloadTask = new PluginDownloadTask(
366 pnlPluginPreferences,
367 toUpdate,
368 tr("Update plugins")
369 );
370 // the async task for downloading plugin information
371 final ReadRemotePluginInformationTask pluginInfoDownloadTask = new ReadRemotePluginInformationTask(Main.pref.getPluginSites());
372
373 // to be run asynchronously after the plugin download
374 //
375 final Runnable pluginDownloadContinuation = new Runnable() {
376 @Override
377 public void run() {
378 if (pluginDownloadTask.isCanceled())
379 return;
380 notifyDownloadResults(pluginDownloadTask);
381 model.refreshLocalPluginVersion(pluginDownloadTask.getDownloadedPlugins());
382 model.clearPendingPlugins(pluginDownloadTask.getDownloadedPlugins());
383 GuiHelper.runInEDT(new Runnable() {
384 @Override
385 public void run() {
386 pnlPluginPreferences.refreshView(); }
387 });
388 }
389 };
390
391 // to be run asynchronously after the plugin list download
392 //
393 final Runnable pluginInfoDownloadContinuation = new Runnable() {
394 @Override
395 public void run() {
396 if (pluginInfoDownloadTask.isCanceled())
397 return;
398 model.updateAvailablePlugins(pluginInfoDownloadTask.getAvailablePlugins());
399 // select plugins which actually have to be updated
400 //
401 Iterator<PluginInformation> it = toUpdate.iterator();
402 while(it.hasNext()) {
403 PluginInformation pi = it.next();
404 if (!pi.isUpdateRequired()) {
405 it.remove();
406 }
407 }
408 if (toUpdate.isEmpty()) {
409 alertNothingToUpdate();
410 return;
411 }
412 pluginDownloadTask.setPluginsToDownload(toUpdate);
413 Main.worker.submit(pluginDownloadTask);
414 Main.worker.submit(pluginDownloadContinuation);
415 }
416 };
417
418 Main.worker.submit(pluginInfoDownloadTask);
419 Main.worker.submit(pluginInfoDownloadContinuation);
420 }
421 }
422
423
424 /**
425 * The action for configuring the plugin download sites
426 *
427 */
428 class ConfigureSitesAction extends AbstractAction {
429 public ConfigureSitesAction() {
430 putValue(NAME,tr("Configure sites..."));
431 putValue(SHORT_DESCRIPTION, tr("Configure the list of sites where plugins are downloaded from"));
432 putValue(SMALL_ICON, ImageProvider.get("dialogs", "settings"));
433 }
434
435 @Override
436 public void actionPerformed(ActionEvent e) {
437 configureSites();
438 }
439 }
440
441 /**
442 * Applies the current filter condition in the filter text field to the
443 * model
444 */
445 class SearchFieldAdapter implements DocumentListener {
446 public void filter() {
447 String expr = tfFilter.getText().trim();
448 if (expr.isEmpty()) {
449 expr = null;
450 }
451 model.filterDisplayedPlugins(expr);
452 pnlPluginPreferences.refreshView();
453 }
454
455 @Override
456 public void changedUpdate(DocumentEvent arg0) {
457 filter();
458 }
459
460 @Override
461 public void insertUpdate(DocumentEvent arg0) {
462 filter();
463 }
464
465 @Override
466 public void removeUpdate(DocumentEvent arg0) {
467 filter();
468 }
469 }
470
471 static private class PluginConfigurationSitesPanel extends JPanel {
472
473 private DefaultListModel model;
474
475 protected void build() {
476 setLayout(new GridBagLayout());
477 add(new JLabel(tr("Add JOSM Plugin description URL.")), GBC.eol());
478 model = new DefaultListModel();
479 for (String s : Main.pref.getPluginSites()) {
480 model.addElement(s);
481 }
482 final JList list = new JList(model);
483 add(new JScrollPane(list), GBC.std().fill());
484 JPanel buttons = new JPanel(new GridBagLayout());
485 buttons.add(new JButton(new AbstractAction(tr("Add")){
486 @Override
487 public void actionPerformed(ActionEvent e) {
488 String s = JOptionPane.showInputDialog(
489 JOptionPane.getFrameForComponent(PluginConfigurationSitesPanel.this),
490 tr("Add JOSM Plugin description URL."),
491 tr("Enter URL"),
492 JOptionPane.QUESTION_MESSAGE
493 );
494 if (s != null) {
495 model.addElement(s);
496 }
497 }
498 }), GBC.eol().fill(GBC.HORIZONTAL));
499 buttons.add(new JButton(new AbstractAction(tr("Edit")){
500 @Override
501 public void actionPerformed(ActionEvent e) {
502 if (list.getSelectedValue() == null) {
503 JOptionPane.showMessageDialog(
504 JOptionPane.getFrameForComponent(PluginConfigurationSitesPanel.this),
505 tr("Please select an entry."),
506 tr("Warning"),
507 JOptionPane.WARNING_MESSAGE
508 );
509 return;
510 }
511 String s = (String)JOptionPane.showInputDialog(
512 Main.parent,
513 tr("Edit JOSM Plugin description URL."),
514 tr("JOSM Plugin description URL"),
515 JOptionPane.QUESTION_MESSAGE,
516 null,
517 null,
518 list.getSelectedValue()
519 );
520 if (s != null) {
521 model.setElementAt(s, list.getSelectedIndex());
522 }
523 }
524 }), GBC.eol().fill(GBC.HORIZONTAL));
525 buttons.add(new JButton(new AbstractAction(tr("Delete")){
526 @Override
527 public void actionPerformed(ActionEvent event) {
528 if (list.getSelectedValue() == null) {
529 JOptionPane.showMessageDialog(
530 JOptionPane.getFrameForComponent(PluginConfigurationSitesPanel.this),
531 tr("Please select an entry."),
532 tr("Warning"),
533 JOptionPane.WARNING_MESSAGE
534 );
535 return;
536 }
537 model.removeElement(list.getSelectedValue());
538 }
539 }), GBC.eol().fill(GBC.HORIZONTAL));
540 add(buttons, GBC.eol());
541 }
542
543 public PluginConfigurationSitesPanel() {
544 build();
545 }
546
547 public List<String> getUpdateSites() {
548 if (model.getSize() == 0) return Collections.emptyList();
549 List<String> ret = new ArrayList<String>(model.getSize());
550 for (int i=0; i< model.getSize();i++){
551 ret.add((String)model.get(i));
552 }
553 return ret;
554 }
555 }
556}
Note: See TracBrowser for help on using the repository browser.