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

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

see #4429 - Right click menu "undo, cut, copy, paste, delete, select all" for each text component (originally based on patch by NooN)

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