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

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

fix some Sonar issues

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