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

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

Allow to access directly to validator preferences from validator dialog with a small preferences button left to pin button.
Generic approach in order to be reused by plugins using dialogs.
This leads to some changes in preferences settings system, but without any break in compatibility.
The only impact is the deprecation of some public JTabbedPane fields, that will be removed mid-2013.

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