source: gli/trunk/src/org/greenstone/gatherer/cdm/PluginManager.java@ 17035

Last change on this file since 17035 was 17035, checked in by kjdon, 16 years ago

moved MetadataXMLPlugin 'below the line' in plugin list. you always need it in gli, and so Ian thought it should go with the other two that you can't remove or move

  • Property svn:keywords set to Author Date Id Revision
File size: 40.4 KB
Line 
1/**
2 *#########################################################################
3 *
4 * A component of the Gatherer application, part of the Greenstone digital
5 * library suite from the New Zealand Digital Library Project at the
6 * University of Waikato, New Zealand.
7 *
8 * Author: John Thompson, Greenstone Digital Library, University of Waikato
9 *
10 * Copyright (C) 1999 New Zealand Digital Library Project
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *########################################################################
26 */
27package org.greenstone.gatherer.cdm;
28
29import java.awt.*;
30import java.awt.event.*;
31import java.io.*;
32import java.net.*;
33import java.util.*;
34import javax.swing.*;
35import javax.swing.event.*;
36import javax.swing.plaf.*;
37import javax.swing.plaf.basic.*;
38import org.apache.xerces.parsers.*;
39import org.greenstone.gatherer.Configuration;
40import org.greenstone.gatherer.DebugStream;
41import org.greenstone.gatherer.Dictionary;
42import org.greenstone.gatherer.Gatherer;
43import org.greenstone.gatherer.collection.CollectionContentsChangedListener;
44import org.greenstone.gatherer.collection.CollectionManager;
45import org.greenstone.gatherer.greenstone.Plugins;
46import org.greenstone.gatherer.gui.DesignPaneHeader;
47import org.greenstone.gatherer.gui.GComboBox;
48import org.greenstone.gatherer.gui.GLIButton;
49import org.greenstone.gatherer.gui.ModalDialog;
50import org.greenstone.gatherer.gui.WarningDialog;
51import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
52import org.greenstone.gatherer.util.JarTools;
53import org.greenstone.gatherer.util.StaticStrings;
54import org.greenstone.gatherer.util.Utility;
55import org.greenstone.gatherer.util.XMLTools;
56import org.w3c.dom.*;
57import org.xml.sax.*;
58
59
60/** This class is for maintaining a list of known plug-ins, and importing new plugins using the parser. */
61public class PluginManager
62 extends DOMProxyListModel
63 implements CollectionContentsChangedListener
64{
65 /** When asking how many rows are in the model, and if this variables value is true, then this modifier alters the number returned. This funtionality is used to hide the last three rows of the list in low detail modes. */
66 private boolean modify_row_count = false;
67 /** The controls for editing the contents of this manager. */
68 private Control controls = null;
69 private DOMProxyListModel model;
70 private JPanel separator;
71 private Plugin separator_plugin;
72
73 /** The number of plugins that are fixed 'below the line' */
74 private static int NUM_FIXED_PLUGINS = 3;
75 /** Constructor.
76 */
77 public PluginManager() { super(CollectionDesignManager.collect_config.getDocumentElement(), StaticStrings.PLUGIN_ELEMENT, new Plugin());
78 DebugStream.println("PluginManager: " + super.getSize() + " plugins parsed.");
79 model = this;
80
81 // Force the assigned plugins to be loaded and cached now
82 for (int i = 0; i < getSize(); i++) {
83 getElementAt(i);
84 }
85
86 // Create the separator, cause we can reuse it.
87 separator = getSeparator();
88 placeSeparator();
89 // check for MetadataXMLPlug
90 ensureMetadataXMLPlugIsLoaded();
91 // Listen for CollectionContentsChanged events, so we can give plugin hints when new files are added
92 CollectionManager.addCollectionContentsChangedListener(this);
93
94 }
95
96 private void ensureMetadataXMLPlugIsLoaded() {
97
98 Plugin metaxmlplug = Plugins.getPlugin(StaticStrings.METADATAXMLPLUG_STR, false);
99 if (metaxmlplug!= null) {
100 if (!contains(metaxmlplug)) {
101 // Create a new element in the DOM
102 Element new_plugin_element = CollectionConfiguration.createElement(StaticStrings.PLUGIN_ELEMENT);
103 new_plugin_element.setAttribute(StaticStrings.TYPE_ATTRIBUTE, metaxmlplug.getName());
104 Plugin new_plugin = new Plugin(new_plugin_element, metaxmlplug);
105 assignPlugin(new_plugin);
106 }
107 }
108 }
109
110
111 /** Retrieve a list of the plugins that are available to be added to the collection. */
112 private Object[] getAvailablePlugins()
113 {
114 ArrayList available = new ArrayList();
115
116 // Add all the non-abstract core Greenstone plugins, except for ArcPlug and RecPlug
117 ArrayList plugins_list = Plugins.getPluginsList();
118 for (int i = 0; i < plugins_list.size(); i++) {
119 Plugin plugin = (Plugin) plugins_list.get(i);
120 if (!plugin.isAbstract()) {
121 String plugin_name = plugin.getName();
122 if (!plugin_name.equals(StaticStrings.ARCPLUG_STR) && !plugin_name.equals(StaticStrings.RECPLUG_STR) && !plugin_name.equals(StaticStrings.METADATAXMLPLUG_STR)) {
123 available.add(plugin);
124 }
125 }
126 }
127
128 // Now remove any assigned plugins
129 if (Configuration.getMode() < Configuration.SYSTEMS_MODE) {
130 available.removeAll(children());
131 }
132
133 // Sort the available plugins into alphabetical order
134 Collections.sort(available);
135
136 return available.toArray();
137 }
138
139
140 public ArrayList getExploderPlugins(File file)
141 {
142 ArrayList exploder_plugins = new ArrayList();
143 ArrayList plugins_list = Plugins.getPluginsList();
144 for (int i = 0; i < plugins_list.size(); i++) {
145 Plugin plugin = (Plugin) plugins_list.get(i);
146 if (plugin.doesExplodeMetadataDatabases() == true && plugin.doesProcessFile(file)) {
147 exploder_plugins.add(plugin);
148 }
149 }
150
151 return exploder_plugins;
152 }
153
154 public boolean isFileExplodable(File file)
155 {
156 ArrayList plugins_list = Plugins.getPluginsList();
157 for (int i = 0; i < plugins_list.size(); i++) {
158 Plugin plugin = (Plugin) plugins_list.get(i);
159 if (plugin.doesExplodeMetadataDatabases() == true && plugin.doesProcessFile(file) == true) {
160 return true;
161 }
162 }
163
164 return false;
165 }
166
167 // Works with replace_srcdoc_with_html.pl
168 public ArrayList getSrcReplacerPlugins(File file)
169 {
170 ArrayList srcreplacer_plugins = new ArrayList();
171 ArrayList plugins_list = Plugins.getPluginsList();
172 for (int i = 0; i < plugins_list.size(); i++) {
173 Plugin plugin = (Plugin) plugins_list.get(i);
174 if (plugin.doesReplaceSrcDocsWithHtml() == true && plugin.doesProcessFile(file)) {
175 srcreplacer_plugins.add(plugin);
176 }
177 }
178
179 return srcreplacer_plugins;
180 }
181
182 // Works with replace_srcdoc_with_html.pl
183 public boolean isFileSrcReplaceable(File file)
184 {
185 ArrayList plugins_list = Plugins.getPluginsList();
186 for (int i = 0; i < plugins_list.size(); i++) {
187 Plugin plugin = (Plugin) plugins_list.get(i);
188
189 if (plugin.doesReplaceSrcDocsWithHtml() == true && plugin.doesProcessFile(file) == true) {
190 return true;
191 }
192 }
193
194 return false;
195 }
196
197 /** Method to assign a plugin
198 * @param plugin the Plugin to assign
199 */
200 private void assignPlugin(Plugin plugin) {
201 if(plugin.getName().equals(StaticStrings.RECPLUG_STR) || plugin.getName().equals(StaticStrings.ARCPLUG_STR) || plugin.getName().equals(StaticStrings.METADATAXMLPLUG_STR)) {
202 addAfter(plugin, separator_plugin); // Adds after separator
203 } else {
204 addBefore(plugin, separator_plugin);
205 }
206 }
207
208
209 /** Destructor. */
210 public void destroy()
211 {
212 CollectionManager.removeCollectionContentsChangedListener(this);
213
214 if (controls != null) {
215 controls.destroy();
216 controls = null;
217 }
218 }
219
220
221 /** This function listens for new files being added to the collection and hints about suitable plugins. */
222 public void fileAddedToCollection(File file)
223 {
224 // First check the plugins already assigned in the collection
225 for (int i = 0; i < super.getSize(); i++) {
226 Plugin assigned_plugin = (Plugin) getElementAt(i);
227 if (assigned_plugin.isSeparator() == false && (assigned_plugin.doesProcessFile(file) == true || assigned_plugin.doesBlockFile(file) == true)) {
228 // This file will be processed by an assigned plugin, so no suggestion is necessary
229 DebugStream.println("Processed by assigned plugin: " + assigned_plugin);
230 return;
231 }
232 }
233
234 // Next try the plugins NOT already assigned in the collection
235 ArrayList suitable_plugins = new ArrayList();
236 Object[] unassigned_plugins = getAvailablePlugins();
237 for (int i = 0; i < unassigned_plugins.length; i++) {
238 Plugin unassigned_plugin = (Plugin) unassigned_plugins[i];
239 if (unassigned_plugin.doesProcessFile(file) == true) {
240 DebugStream.println("Processed by unassigned plugin: " + unassigned_plugin);
241 suitable_plugins.add(unassigned_plugin);
242 }
243 }
244
245 // If there appear to be no suitable plugins, warn the user about this and be done
246 if (suitable_plugins.size() == 0) {
247 String[] args = new String[1];
248 args[0] = file.getName();
249 WarningDialog warning_dialog = new WarningDialog("warning.NoPluginExpectedToProcessFile", Dictionary.get("NoPluginExpectedToProcessFile.Title"), Dictionary.get("NoPluginExpectedToProcessFile.Message", args), null, false);
250 warning_dialog.display();
251 warning_dialog.dispose();
252 return;
253 }
254
255 // Generate a dialog
256 new PluginSuggestionPrompt(file.getName(), suitable_plugins);
257 }
258
259
260 /** Method to retrieve the control for this manager.
261 * @return the Control
262 */
263 public Control getControls() {
264 if(controls == null) {
265 // Build controls
266 controls = new PluginControl();
267 }
268 return controls;
269 }
270
271
272 /** Overrides getSize in DOMProxyListModel to take into account the row count modifier used to hide the last three rows in lower detail modes
273 * @return an int indicating the number of rows in the model, or more correctly the desired number of rows to display
274 */
275 public int getSize() {
276 int result = super.getSize();
277 if(modify_row_count) {
278 result = result-(NUM_FIXED_PLUGINS+1);
279 }
280 return result;
281 }
282
283 /** Called when the detail mode has changed which in turn may cause several design elements to be available/hidden
284 * @param mode the new mode as an int
285 */
286 public void modeChanged(int mode) {
287 if(controls != null) {
288 ((PluginControl)controls).modeChanged(mode);
289 }
290 }
291
292 /** Method to move a plugin in the list order.
293 * @param plugin the Plugin you want to move.
294 * @param direction true to move the plugin up, false to move it down.
295 * @param all true to move to move all the way, false for a single step.
296 */
297 // why are all the error notices there when the buttons are disabled is you cant move???
298 private void movePlugin(Plugin plugin, boolean direction, boolean all) {
299 // Can't ever move RecPlug or ArcPlug
300 if(super.getSize() < (NUM_FIXED_PLUGINS+2)) {
301 //DebugStream.println("Not enough plugins to allow moving.");
302 return;
303 }
304 if(plugin.getName().equals(StaticStrings.ARCPLUG_STR) || plugin.getName().equals(StaticStrings.RECPLUG_STR) || plugin.getName().equals(StaticStrings.METADATAXMLPLUG_STR)) {
305 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.Fixed"), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
306 return;
307 }
308 if(all) {
309 // Move to top
310 if(direction) {
311 // Remove the moving plugin
312 remove(plugin);
313 // Retrieve the first plugin
314 Plugin first_plugin = (Plugin) getElementAt(0);
315 // Add the moving plugin before the first plugin
316 addBefore(plugin, first_plugin);
317 first_plugin = null;
318 }
319 else {
320 // Remove the moving plugin
321 remove(plugin);
322 // Add the moving plugin before the separator
323 addBefore(plugin, separator_plugin);
324 }
325 }
326 else {
327 // Try to move the plugin one step in the desired direction.
328 int index = indexOf(plugin);
329 ///ystem.err.println("Index of " + plugin + " = " + index);
330 if(direction) {
331 index--;
332 if(index < 0) {
333 String args[] = new String[2];
334 args[0] = Dictionary.get("CDM.PlugInManager.PlugIn_Str");
335 args[1] = plugin.getName();
336 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.At_Top", args), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
337 return;
338 }
339 remove(plugin);
340 add(index, plugin);
341 }
342 else {
343 index++;
344 Plugin next_plugin = (Plugin) getElementAt(index);
345 if(next_plugin.isSeparator()) {
346 String args[] = new String[1];
347 args[0] = plugin.getName();
348 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.Cannot", args), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
349 // Still not going to move RecPlug or ArcPlug.
350 return;
351 }
352 remove(plugin);
353 add(index, plugin);
354 }
355 }
356 }
357
358 /** We attempt to place the separator between the unfixed and the fixed plugins. Since we only know of two fixed plugins, we search for either of them, and place the separator before them.
359 */
360 private void placeSeparator() {
361 ///ystem.err.println("Placing separator.");
362 int separator_index = super.getSize();
363 if(separator_index > 0) {
364 boolean found_fixed = false;
365 int index = separator_index - 1;
366 while(index > 0) {
367 Plugin plugin = (Plugin) getElementAt(index);
368 String name = plugin.getName();
369 if(name.equals(StaticStrings.RECPLUG_STR) || name.equals(StaticStrings.ARCPLUG_STR) || name.equals(StaticStrings.METADATAXMLPLUG_STR)) {
370 found_fixed = true;
371 index--;
372 }
373 else {
374 if(found_fixed) {
375 separator_index = index + 1;
376 index = -1;
377 }
378 else {
379 index--;
380 }
381 }
382 name = null;
383 plugin = null;
384 }
385 }
386 Element element = CollectionConfiguration.createElement(StaticStrings.PLUGIN_ELEMENT);
387 element.setAttribute(StaticStrings.TYPE_ATTRIBUTE, StaticStrings.SEPARATOR_ATTRIBUTE);
388 element.setAttribute(StaticStrings.SEPARATOR_ATTRIBUTE, StaticStrings.TRUE_STR);
389 separator_plugin = new Plugin(element, null);
390 ///atherer.println("Adding plugin separator at: " + separator_index);
391 add(separator_index, separator_plugin);
392 }
393
394 /** This method removes an assigned plugin. I was tempted to call it unassign, but remove is more consistant. Note that there is no way to remove a plugin from the library.
395 * @param plugin The <strong>Plugin</strong> to remove.
396 */
397 private void removePlugin(Plugin plugin) {
398 remove(plugin);
399
400 }
401
402
403 /** Inform the model to hide/show the last four lines on the list.
404 * @param modify_row_count true to hide the last four lines, false otherwise
405 */
406 private void setHideLines(boolean modify_row_count) {
407 this.modify_row_count = modify_row_count;
408 int original_size = super.getSize();
409 if(modify_row_count) {
410 fireIntervalRemoved(this, original_size - (NUM_FIXED_PLUGINS+2), original_size - 1);
411 }
412 else {
413 fireIntervalAdded(this, original_size - (NUM_FIXED_PLUGINS+2), original_size - 1);
414 }
415 }
416
417 /** Determine the current separator index. */
418 private int findSeparatorIndex() {
419 int separator_index = super.getSize() - 1;
420 while(separator_index >= 0) {
421 Plugin search = (Plugin) getElementAt(separator_index);
422 if(search.isSeparator()) {
423 return separator_index;
424 }
425 separator_index--;
426 }
427 return separator_index;
428 }
429
430
431 /** A class which provodes controls for assigned and editing plugins. */
432 private class PluginControl
433 extends JPanel
434 implements Control {
435 /** Button for adding plugins. */
436 private JButton add = null;
437 /** Button for configuring the selected plugin. */
438 private JButton configure = null;
439 /** Button to move an assigned plugin one position lower in the order. */
440 private JButton move_down_button = null;
441 /** Button to move an assigned plugin one position higher in the order. */
442 private JButton move_up_button = null;
443 /** Button to remove the selected plugin. */
444 private JButton remove = null;
445 /** A combobox containing all of the known plugins, including those that may have already been assigned. */
446 private JComboBox plugin_combobox = null;
447 /** The label next to the plugin combobox. */
448 private JLabel plugin_label = null;
449 /** The label above the assigned plugin list. */
450 private JLabel plugin_list_label = null;
451 /** A list of assigned plugins. */
452 private JList plugin_list = null;
453 /** The area where the add, configure and remove buttons are placed. */
454 private JPanel button_pane = null;
455 /** The region which divides the central portion of the view into list and controls */
456 private JPanel central_pane = null;
457 /** The area where movement buttons are placed. */
458 private JPanel movement_pane = null;
459 /** The small region containing the plugin combobox and its label. */
460 private JPanel plugin_pane = null;
461 /** The pane containing the assigned plugin list and its label. */
462 private JPanel plugin_list_pane = null;
463
464 /** Constructor.
465 */
466 public PluginControl()
467 {
468 // Create
469 add = new GLIButton(Dictionary.get("CDM.PlugInManager.Add"), Dictionary.get("CDM.PlugInManager.Add_Tooltip"));
470
471 button_pane = new JPanel();
472 central_pane = new JPanel();
473
474 configure = new GLIButton(Dictionary.get("CDM.PlugInManager.Configure"), Dictionary.get("CDM.PlugInManager.Configure_Tooltip"));
475 configure.setEnabled(false);
476
477 JPanel header_pane = new DesignPaneHeader("CDM.GUI.Plugins", "plugins");
478 move_up_button = new GLIButton(Dictionary.get("CDM.Move.Move_Up"), JarTools.getImage("arrow-up.gif"), Dictionary.get("CDM.Move.Move_Up_Tooltip"));
479 move_up_button.setEnabled(false);
480
481 move_down_button = new GLIButton(Dictionary.get("CDM.Move.Move_Down"), JarTools.getImage("arrow-down.gif"), Dictionary.get("CDM.Move.Move_Down_Tooltip"));
482 move_down_button.setEnabled(false);
483
484 movement_pane = new JPanel();
485
486 PluginComboboxListener picl = new PluginComboboxListener();
487 plugin_combobox = new JComboBox(getAvailablePlugins());
488 plugin_combobox.setOpaque(!Utility.isMac());
489 plugin_combobox.setEditable(false);
490 picl.itemStateChanged(new ItemEvent(plugin_combobox, 0, null, ItemEvent.SELECTED));
491
492 plugin_label = new JLabel(Dictionary.get("CDM.PlugInManager.PlugIn"));
493
494 plugin_list = new JList(model);
495 plugin_list.setOpaque(true);
496 plugin_list.setCellRenderer(new ListRenderer());
497 plugin_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
498 plugin_list_label = new JLabel(Dictionary.get("CDM.PlugInManager.Assigned"));
499 //plugin_list_label.setHorizontalAlignment(JLabel.CENTER);
500 plugin_list_label.setOpaque(true);
501
502 plugin_list_pane = new JPanel();
503 plugin_pane = new JPanel();
504 plugin_pane.setOpaque(true);
505
506 remove = new GLIButton(Dictionary.get("CDM.PlugInManager.Remove"), Dictionary.get("CDM.PlugInManager.Remove_Tooltip"));
507 remove.setEnabled(false);
508
509 // Listeners
510 add.addActionListener(new AddListener());
511 configure.addActionListener(new ConfigureListener());
512 MoveListener ml = new MoveListener();
513 move_down_button.addActionListener(ml);
514 move_down_button.addActionListener(CollectionDesignManager.all_change_listener);
515 move_up_button.addActionListener(ml);
516 move_up_button.addActionListener(CollectionDesignManager.all_change_listener);
517 plugin_combobox.addItemListener(picl);
518 remove.addActionListener(new RemoveListener());
519 remove.addActionListener(CollectionDesignManager.all_change_listener);
520 plugin_list.addMouseListener(new ClickListener());
521 plugin_list.addListSelectionListener(new ListListener());
522 picl = null;
523
524 // Layout
525 movement_pane.setBorder(BorderFactory.createEmptyBorder(0,2,0,0));
526 movement_pane.setLayout(new GridLayout(4,1));
527 movement_pane.add(move_up_button);
528 movement_pane.add(new JPanel());
529 movement_pane.add(new JPanel());
530 movement_pane.add(move_down_button);
531
532 plugin_list_pane.setLayout(new BorderLayout());
533 plugin_list_pane.add(plugin_list_label, BorderLayout.NORTH);
534 plugin_list_pane.add(new JScrollPane(plugin_list), BorderLayout.CENTER);
535 modeChanged(Configuration.getMode()); // Whether the movement buttons are visible is mode dependant
536
537 plugin_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
538
539 plugin_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
540 plugin_pane.setLayout(new BorderLayout(5,0));
541 plugin_pane.add(plugin_label, BorderLayout.WEST);
542 plugin_pane.add(plugin_combobox, BorderLayout.CENTER);
543
544 button_pane.setLayout(new GridLayout(1,3));
545 button_pane.add(add);
546 button_pane.add(configure);
547 button_pane.add(remove);
548
549 // Scope these mad bordering skillz.
550 JPanel temp = new JPanel(new BorderLayout());
551 temp.add(plugin_pane, BorderLayout.NORTH);
552 temp.add(button_pane, BorderLayout.SOUTH);
553
554 central_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
555 central_pane.setLayout(new BorderLayout());
556 central_pane.add(plugin_list_pane, BorderLayout.CENTER);
557 central_pane.add(temp, BorderLayout.SOUTH);
558
559 setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
560 setLayout(new BorderLayout());
561 add(header_pane, BorderLayout.NORTH);
562 add(central_pane, BorderLayout.CENTER);
563 }
564
565 /** Method which acts like a destructor, tidying up references to persistant objects.
566 */
567 public void destroy() {
568 }
569
570 /** This method is overridden to ensure the instructions are scrolled to top, before the super classes updateUI() is called.
571 no longer have instructions, do we still need updateUI???
572 */
573 public void gainFocus() {
574 super.updateUI();
575 }
576
577 public void loseFocus() {
578 }
579
580 /** The current detail mode controls two aspects of plugin manager: whether the movement buttons are visible and whether the fixed position plugins Arc and RecPlug are in the list
581 * @param mode the current mode as an int, which can be matched against static ints in the Configuration class
582 */
583 public void modeChanged(int mode) {
584 // First of all we clear the current selection, as there can be some serious problems if the user selects the plugins we're hiding, or had the last plugin selected before we unhid the last three
585 plugin_list.clearSelection();
586 // The first change is dependant on whether the user is systems mode or higher
587
588 plugin_combobox.setModel(new DefaultComboBoxModel(getAvailablePlugins()));
589
590 if(mode >= Configuration.SYSTEMS_MODE) {
591 // Show movement buttons
592
593 plugin_list_pane.add(movement_pane, BorderLayout.EAST);
594 // Do we show Arc and RecPlugs or hide them and the separator line
595 setHideLines(!(mode >= Configuration.EXPERT_MODE));
596 }
597 // Otherwise hide the movement buttons and fixed plugins
598 else {
599 plugin_list_pane.remove(movement_pane);
600 setHideLines(true);
601 }
602 plugin_list_pane.updateUI();
603 }
604
605
606 private class AddListener
607 implements ActionListener
608 {
609 public void actionPerformed(ActionEvent event)
610 {
611 if (plugin_combobox.getSelectedItem() != null) {
612 // This must be done on a new thread for the remote building code
613 new AddPluginTask(plugin_combobox.getSelectedItem().toString()).start();
614 }
615 }
616 }
617
618
619 private class AddPluginTask
620 extends Thread
621 {
622 private String plugin_name;
623
624 public AddPluginTask(String plugin_name)
625 {
626 this.plugin_name = plugin_name;
627 }
628
629 public void run()
630 {
631 // Retrieve the plugin
632 Plugin plugin = Plugins.getPlugin(plugin_name, true);
633 if (plugin == null) {
634 System.err.println("Error: getPlugin() returned null.");
635 return;
636 }
637
638 // Create a new element in the DOM
639 Element new_plugin_element = CollectionConfiguration.createElement(StaticStrings.PLUGIN_ELEMENT);
640 new_plugin_element.setAttribute(StaticStrings.TYPE_ATTRIBUTE, plugin.getName());
641 Plugin new_plugin = new Plugin(new_plugin_element, plugin);
642
643 ArgumentConfiguration ac = new ArgumentConfiguration(new_plugin);
644 ac.addOKButtonActionListener(CollectionDesignManager.all_change_listener);
645 if (ac.display()) {
646 assignPlugin(new_plugin);
647 plugin_list.setSelectedValue(new_plugin, true);
648
649 // Remove the plugin from the available list (unless we're in a high mode, or it's UnknownPlug)
650 if (Configuration.getMode() < Configuration.SYSTEMS_MODE && !plugin_name.equals(StaticStrings.UNKNOWNPLUG_STR)) {
651 plugin_combobox.removeItem(plugin);
652 plugin_combobox.setSelectedIndex(0);
653 }
654 }
655 }
656 }
657
658
659 /** Listens for double clicks apon the list and react as if the configure button was pushed. */
660 private class ClickListener
661 extends MouseAdapter {
662 /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt.
663 * @param event A <strong>MouseEvent</strong> containing information about the mouse click.
664 */
665 public void mouseClicked(MouseEvent event) {
666 if(event.getClickCount() == 2 ) {
667 if(!plugin_list.isSelectionEmpty()) {
668 Plugin plugin = (Plugin) plugin_list.getSelectedValue();
669 if(!plugin.isSeparator()) {
670 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
671 ac.addOKButtonActionListener(CollectionDesignManager.all_change_listener);
672 if (ac.display()) {
673 refresh(plugin);
674 }
675 ac.destroy();
676 ac = null;
677 }
678 }
679 }
680 }
681 }
682
683 /** This class listens for actions upon the configure button in the controls, and if detected creates a new <strong>ArgumentConfiguration</strong> dialog box to allow for configuration.
684 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration
685 */
686 private class ConfigureListener
687 implements ActionListener {
688 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured on one of our target controls.
689 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
690 */
691 public void actionPerformed(ActionEvent event) {
692 if(!plugin_list.isSelectionEmpty()) {
693 Plugin plugin = (Plugin) plugin_list.getSelectedValue();
694 if(!plugin.isSeparator()) {
695 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
696 ac.addOKButtonActionListener(CollectionDesignManager.all_change_listener);
697 if (ac.display()) {
698 refresh(plugin);
699 }
700 ac.destroy();
701 ac = null;
702 }
703 }
704 }
705 }
706
707 /** listens for changes in the list selection and enables the configure and remove buttons if there is a selection, disables them if there is no selection */
708 private class ListListener
709 implements ListSelectionListener {
710
711 public void valueChanged(ListSelectionEvent e) {
712 if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one (the second one)
713 if (plugin_list.isSelectionEmpty()) {
714 move_up_button.setEnabled(false);
715 move_down_button.setEnabled(false);
716 configure.setEnabled(false);
717 remove.setEnabled(false);
718 } else {
719 Plugin selected_plugin = (Plugin) plugin_list.getSelectedValue();
720 if(selected_plugin.isSeparator()) {
721 move_up_button.setEnabled(false);
722 move_down_button.setEnabled(false);
723 configure.setEnabled(false);
724 remove.setEnabled(false);
725 } else {
726 configure.setEnabled(true);
727 String plugin_name = selected_plugin.getName();
728 // Some buttons are only available for plugins other than ArcPlug and RecPlug
729 if(plugin_name.equals(StaticStrings.ARCPLUG_STR) || plugin_name.equals(StaticStrings.RECPLUG_STR) || plugin_name.equals(StaticStrings.METADATAXMLPLUG_STR)) {
730 move_up_button.setEnabled(false);
731 move_down_button.setEnabled(false);
732 remove.setEnabled(false);
733 }
734
735 else {
736 // don't let people remove special plugins such GAPlug an METSPlug,
737 // unless they are in systems mode or above
738 int mode = Configuration.getMode();
739 for (int i=0; i<StaticStrings.KEEP_PLUG.length; i++) {
740 if ((plugin_name.equals(StaticStrings.KEEP_PLUG[i])) &&
741 (mode < Configuration.SYSTEMS_MODE)) {
742 remove.setEnabled(false);
743 break;
744 } else {
745 remove.setEnabled(true);
746 }
747 }
748
749 // Move ups are only enabled if the selected plugin isn't already at the top
750 Plugin first_plugin = (Plugin) getElementAt(0);
751 if(!first_plugin.equals(selected_plugin)) {
752 move_up_button.setEnabled(true);
753 } else {
754 move_up_button.setEnabled(false);
755 }
756 // And move downs are only allowed when the selected plugin isn't at an index one less than the separator line.
757 int separator_index = findSeparatorIndex();
758 int selected_index = plugin_list.getSelectedIndex();
759 if(selected_index < separator_index - 1) {
760 move_down_button.setEnabled(true);
761 } else {
762 move_down_button.setEnabled(false);
763 }
764 }
765 selected_plugin = null;
766 plugin_name = null;
767 }
768 }
769 }
770 }
771 }
772
773 /** A special list renderer which is able to render separating lines as well. */
774 private class ListRenderer
775 extends DefaultListCellRenderer {
776 /** Return a component that has been configured to display the specified value. That component's paint method is then called to "render" the cell. If it is necessary to compute the dimensions of a list because the list cells do not have a fixed size, this method is called to generate a component on which getPreferredSize can be invoked.
777 * @param list - The <strong>JList</strong> we're painting.
778 * @param value - The value returned by list.getModel().getElementAt(index) as an <strong>Object</strong>.
779 * @param index - The cells index as an <i>int</i>.
780 * @param isSelected - <i>true</i> if the specified cell was selected.
781 * @param cellHasFocus - <i>true</i> if the specified cell has the focus.
782 * @return A <strong>Component</strong> whose paint() method will render the specified value.
783 * @see javax.swing.JList
784 * @see javax.swing.JSeparator
785 * @see javax.swing.ListModel
786 * @see javax.swing.ListSelectionModel
787 */
788 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
789 Plugin plugin = (Plugin) value;
790 if(plugin.isSeparator()) {
791 return separator;
792 }
793 else {
794 return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
795 }
796 }
797 }
798
799
800 /** Listens for actions apon the move buttons in the manager controls, and if detected calls the <i>movePlugin()</i> method of the manager with the appropriate details. */
801 private class MoveListener
802 implements ActionListener {
803 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured on one of our target controls.
804 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
805 */
806 public void actionPerformed(ActionEvent event) {
807 if (!plugin_list.isSelectionEmpty()) {
808 Object object = plugin_list.getSelectedValue();
809 if (object instanceof Plugin) {
810 Plugin plugin = (Plugin) object;
811 if (event.getSource() == move_up_button) {
812 movePlugin(plugin, true, false);
813 }
814 else if (event.getSource() == move_down_button) {
815 movePlugin(plugin, false, false);
816 }
817 plugin_list.setSelectedValue(plugin, true);
818 }
819 }
820 }
821 }
822
823 /** This listener reacts to changes in the current selection of the plugin combobox. */
824 private class PluginComboboxListener
825 implements ItemListener {
826 /** When a user selects a certain plugin, update the tooltip to show the plugin description. */
827 public void itemStateChanged(ItemEvent event) {
828 if(event.getStateChange() == ItemEvent.SELECTED) {
829 // Retrieve the selected plugin
830 Object current_selection = plugin_combobox.getSelectedItem();
831 // And reset the tooltip. If the plugin is null or is a string, then go back to the default message
832 if(current_selection == null || current_selection instanceof String) {
833 plugin_combobox.setToolTipText(Dictionary.get("CDM.PlugInManager.PlugIn_Tooltip"));
834 }
835 else {
836 Plugin current_plugin = (Plugin) current_selection;
837 plugin_combobox.setToolTipText(Utility.formatHTMLWidth(current_plugin.getDescription(), 40));
838 current_plugin = null;
839 }
840 current_selection = null;
841 }
842 }
843 }
844
845 /** This class listens for actions upon the remove button in the controls, and if detected calls the <i>removePlugin()</i> method.
846 */
847 private class RemoveListener
848 implements ActionListener {
849 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured on one of our target controls.
850 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
851 */
852 public void actionPerformed(ActionEvent event) {
853 int selected_index = plugin_list.getSelectedIndex();
854 if(selected_index != -1) {
855 Plugin selected_plugin = (Plugin) plugin_list.getSelectedValue();
856 removePlugin(selected_plugin);
857 selected_plugin = null;
858 // Refresh the available plugins
859 plugin_combobox.setModel(new DefaultComboBoxModel(getAvailablePlugins()));
860
861 // Select the next plugin if available
862 if(selected_index < plugin_list.getModel().getSize()) {
863 // If the new selection is above the separator we can remove it
864 if(selected_index < findSeparatorIndex()) {
865 plugin_list.setSelectedIndex(selected_index);
866
867 // don't let people remove special plugins such GAPlug an METSPlug,
868 // unless they are in systems mode or above
869 int mode = Configuration.getMode();
870 for (int i=0; i<StaticStrings.KEEP_PLUG.length; i++) {
871 String selected_plugin_name
872 = ((Plugin)plugin_list.getSelectedValue()).getName();
873
874 if (selected_plugin_name.equals(StaticStrings.METADATAXMLPLUG_STR)) {
875 //this plug cannot be removed under any conditions.
876 remove.setEnabled(false);
877 }else if ((selected_plugin_name.equals(StaticStrings.KEEP_PLUG[i])) &&
878 (mode < Configuration.SYSTEMS_MODE)) {
879 remove.setEnabled(false);
880 break;
881 } else {
882 remove.setEnabled(true);
883 }
884 }
885 }
886 // Otherwise select the first non-removable plugin
887 else {
888 plugin_list.setSelectedIndex(selected_index + 1);
889 remove.setEnabled(false);
890 }
891 } else {
892 remove.setEnabled(false);
893 }
894 } else {
895 remove.setEnabled(false);
896 }
897 }
898 }
899 }
900
901
902 private class PluginSuggestionPrompt
903 extends ModalDialog
904 implements ActionListener
905 {
906 private Dimension size = new Dimension(480, 240);
907 private GComboBox suitable_plugins_combobox = null;
908 private GLIButton add_button = null;
909 private GLIButton ignore_button = null;
910
911 public PluginSuggestionPrompt(String filename, ArrayList suitable_plugins)
912 {
913 super(Gatherer.g_man, true);
914 setModal(true);
915 setSize(size);
916 setTitle(Dictionary.get("CDM.PluginManager.SuggestedPluginListTitle"));
917
918 String[] args = new String[1];
919 args[0] = filename;
920
921 JTextArea instructions_textarea = new JTextArea(Dictionary.get("CDM.PluginManager.Plugin_Suggestion_Prompt", args));
922 instructions_textarea.setCaretPosition(0);
923 instructions_textarea.setEditable(false);
924 instructions_textarea.setLineWrap(true);
925 instructions_textarea.setRows(5);
926 instructions_textarea.setWrapStyleWord(true);
927
928 JLabel suitable_plugins_label = new JLabel(Dictionary.get("CDM.PlugInManager.PlugIn"));
929 suitable_plugins_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
930
931 suitable_plugins_combobox = new GComboBox(suitable_plugins);
932 suitable_plugins_combobox.setBackgroundNonSelectionColor(Configuration.getColor("coloring.editable_background", false));
933 suitable_plugins_combobox.setBackgroundSelectionColor(Configuration.getColor("coloring.collection_selection_background", false));
934 suitable_plugins_combobox.setTextNonSelectionColor(Configuration.getColor("coloring.workspace_tree_foreground", false));
935 suitable_plugins_combobox.setTextSelectionColor(Configuration.getColor("coloring.collection_selection_foreground", false));
936
937 JPanel suitable_plugins_pane = new JPanel();
938 //suitable_plugins_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
939 suitable_plugins_pane.setLayout(new BorderLayout(5,0));
940 suitable_plugins_pane.add(suitable_plugins_label, BorderLayout.WEST);
941 suitable_plugins_pane.add(suitable_plugins_combobox, BorderLayout.CENTER);
942
943 add_button = new GLIButton(Dictionary.get("CDM.PlugInManager.QuickAdd"), Dictionary.get("CDM.PlugInManager.Add_Tooltip"));
944 ignore_button = new GLIButton(Dictionary.get("CDM.PlugInManager.Ignore"), Dictionary.get("CDM.PlugInManager.Ignore_Tooltip"));
945
946 add_button.addActionListener(this);
947 ignore_button.addActionListener(this);
948
949 JPanel button_pane = new JPanel();
950 button_pane.setLayout(new GridLayout(1,2,5,0));
951 button_pane.add(add_button);
952 button_pane.add(ignore_button);
953
954 JPanel controls_pane = new JPanel();
955 controls_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
956 controls_pane.setLayout(new GridLayout(2,1,0,5));
957 controls_pane.add(suitable_plugins_pane);
958 controls_pane.add(button_pane);
959
960 JPanel content_pane = (JPanel) getContentPane();
961 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
962 content_pane.setLayout(new BorderLayout());
963 content_pane.add(instructions_textarea, BorderLayout.CENTER);
964 content_pane.add(controls_pane, BorderLayout.SOUTH);
965
966 // Show
967 Dimension screen_size = Configuration.screen_size;
968 setLocation((screen_size.width - size.width) / 2, (screen_size.height - size.height) / 2);
969 setVisible(true);
970 }
971
972
973 public void actionPerformed(ActionEvent event)
974 {
975 // Close the dialog
976 setVisible(false);
977
978 if (event.getSource() == add_button) {
979 AddPluginWithoutConfigurationTask add_plugin_task = new AddPluginWithoutConfigurationTask(suitable_plugins_combobox.getSelectedItem().toString());
980 add_plugin_task.start();
981
982 // We have to wait for this task to complete before continuing, otherwise
983 // we might offer to add the same plugin again before the task has completed
984 try {
985 add_plugin_task.join();
986 }
987 catch (Exception exception) {
988 DebugStream.printStackTrace(exception);
989 }
990 }
991 }
992
993
994 private class AddPluginWithoutConfigurationTask
995 extends Thread
996 {
997 private String plugin_name;
998
999 public AddPluginWithoutConfigurationTask(String plugin_name)
1000 {
1001 this.plugin_name = plugin_name;
1002 }
1003
1004 public void run()
1005 {
1006 // Retrieve the plugin
1007 Plugin plugin = Plugins.getPlugin(plugin_name, true);
1008 if (plugin == null) {
1009 System.err.println("Error: getPlugin() returned null.");
1010 return;
1011 }
1012
1013 // Create a new element in the DOM
1014 Element new_plugin_element = CollectionConfiguration.createElement(StaticStrings.PLUGIN_ELEMENT);
1015 new_plugin_element.setAttribute(StaticStrings.TYPE_ATTRIBUTE, plugin.getName());
1016 Plugin new_plugin = new Plugin(new_plugin_element, plugin);
1017
1018 // Just assign the plugin with no argument configuration
1019 assignPlugin(new_plugin);
1020 }
1021 }
1022 }
1023
1024
1025 /** Creates a list separator.
1026 * Found on Google Groups. Code courtesy of Paul Farwell.
1027 */
1028 private JPanel getSeparator() {
1029 // We put the separator inside a panel to control its appearance
1030 JPanel _sepPanel = new JPanel();
1031 _sepPanel.setOpaque(false);
1032 _sepPanel.setBorder(BorderFactory.createEmptyBorder(1, 3, 1, 3));
1033 _sepPanel.setLayout(new BoxLayout(_sepPanel, BoxLayout.Y_AXIS));
1034 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
1035 // We have to be a little careful here, as the default UI for separators under MacOS is a blank box. Instead we force a BasicUI look
1036 _sepPanel.add(new BasicSeparator());
1037 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
1038 return _sepPanel;
1039 }
1040
1041 /** This class behaves just like a normal JSeparator except that, no matter what the current settings in the UIManager are, it always paints itself with BasicSeparatorUI. */
1042 private static class BasicSeparator
1043 extends JSeparator {
1044
1045 private ComponentUI basic_ui;
1046
1047 public BasicSeparator() {
1048 super();
1049 basic_ui = new BasicSeparatorUI();
1050 }
1051
1052 public void paintComponent(Graphics g) {
1053 if (basic_ui != null) {
1054 basic_ui.update(g, this);
1055 }
1056 }
1057 }
1058}
Note: See TracBrowser for help on using the repository browser.