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

Last change on this file since 12638 was 12638, checked in by mdewsnip, 18 years ago

Changed debug statements in new code to go to DebugStream.

  • Property svn:keywords set to Author Date Id Revision
File size: 43.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.LocalGreenstone;
44import org.greenstone.gatherer.collection.CollectionContentsChangedListener;
45import org.greenstone.gatherer.gui.DesignPaneHeader;
46import org.greenstone.gatherer.gui.GComboBox;
47import org.greenstone.gatherer.gui.GLIButton;
48import org.greenstone.gatherer.gui.ModalDialog;
49import org.greenstone.gatherer.gui.WarningDialog;
50import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
51import org.greenstone.gatherer.util.JarTools;
52import org.greenstone.gatherer.util.StaticStrings;
53import org.greenstone.gatherer.util.Utility;
54import org.greenstone.gatherer.util.XMLTools;
55import org.w3c.dom.*;
56import org.xml.sax.*;
57
58
59/** This class is for maintaining a list of known plug-ins, and importing new plugins using the parser. */
60public class PluginManager
61 extends DOMProxyListModel
62 implements CollectionContentsChangedListener
63{
64 // A list of all the plugins in the core Greenstone "perllib/plugins" folder (arguments may not be loaded)
65 static private ArrayList core_greenstone_plugins_list = null;
66
67 /** 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. */
68 private boolean modify_row_count = false;
69 /** The controls for editing the contents of this manager. */
70 private Control controls = null;
71 private DOMProxyListModel model;
72 private JPanel separator;
73 private Plugin separator_plugin;
74 /** Constructor.
75 */
76 public PluginManager() {
77 super(CollectionDesignManager.collect_config.getDocumentElement(), CollectionConfiguration.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
89 // Listen for CollectionContentsChanged events, so we can give plugin hints when new files are added
90 Gatherer.c_man.addCollectionContentsChangedListener(this);
91 }
92
93
94 // --------------------------------------------------------------------------------------------------------
95
96
97 /** Retrieve a list of the plugins that are available to be added to the collection. */
98 private Object[] getAvailablePlugins()
99 {
100 ArrayList available = new ArrayList();
101
102 // Add all the non-abstract core Greenstone plugins, except for ArcPlug and RecPlug
103 for (int i = 0; i < core_greenstone_plugins_list.size(); i++) {
104 Plugin plugin = (Plugin) core_greenstone_plugins_list.get(i);
105 if (!plugin.isAbstract()) {
106 String plugin_name = plugin.getName();
107 if (!plugin_name.equals(StaticStrings.ARCPLUG_STR) && !plugin_name.equals(StaticStrings.RECPLUG_STR)) {
108 available.add(plugin);
109 }
110 }
111 }
112
113 // Now remove any assigned plugins
114 if (Configuration.getMode() <= Configuration.SYSTEMS_MODE) {
115 available.removeAll(children());
116 }
117
118 // Sort the available plugins into alphabetical order
119 Collections.sort(available);
120
121 return available.toArray();
122 }
123
124
125 public ArrayList getExploderPlugins(File file)
126 {
127 ArrayList exploder_plugins = new ArrayList();
128 for (int i = 0; i < core_greenstone_plugins_list.size(); i++) {
129 Plugin plugin = (Plugin) core_greenstone_plugins_list.get(i);
130 if (plugin.doesExplodeMetadataDatabases() == true && plugin.doesProcessFile(file)) {
131 exploder_plugins.add(plugin);
132 }
133 }
134
135 return exploder_plugins;
136 }
137
138
139 static public Plugin getPlugin(String plugin_name, boolean arguments_required)
140 {
141 for (int i = 0; i < core_greenstone_plugins_list.size(); i++) {
142 Plugin plugin = (Plugin) core_greenstone_plugins_list.get(i);
143 if (plugin.getName().equals(plugin_name)) {
144 if (arguments_required) {
145 if (plugin.getArguments().size() == 0) {
146 loadPluginInfo(plugin);
147 }
148 else {
149 DebugStream.println("Already loaded arguments for " + plugin_name + "!");
150 }
151 }
152 return plugin;
153 }
154 }
155
156 return null;
157 }
158
159
160 public boolean isFileExplodable(File file)
161 {
162 for (int i = 0; i < core_greenstone_plugins_list.size(); i++) {
163 Plugin plugin = (Plugin) core_greenstone_plugins_list.get(i);
164 if (plugin.doesExplodeMetadataDatabases() == true && plugin.doesProcessFile(file) == true) {
165 return true;
166 }
167 }
168
169 return false;
170 }
171
172
173 static private void loadPluginInfo(Plugin plugin)
174 {
175 DebugStream.println("Loading arguments for " + plugin.getName() + "...");
176
177 // Run pluginfo.pl to get the list of plugins
178 try {
179 StringBuffer xml = null;
180 if (Gatherer.isGsdlRemote) {
181 String pluginfo_output = RemoteGreenstoneServer.getScriptOptions("pluginfo.pl", "&plugin=" + plugin);
182 xml = new StringBuffer(pluginfo_output);
183 }
184 else {
185 ArrayList args = new ArrayList();
186 if (Utility.isWindows()) {
187 args.add(Configuration.perl_path);
188 args.add("-S");
189 }
190 args.add(LocalGreenstone.getBinScriptDirectoryPath() + "pluginfo.pl");
191 args.add("-xml");
192 args.add("-language");
193 args.add(Configuration.getLanguage());
194 args.add(plugin.getName());
195
196 // Run the pluginfo.pl process
197 Runtime runtime = Runtime.getRuntime();
198 Process process = runtime.exec((String[]) args.toArray(new String[] { }));
199 InputStream input_stream = process.getErrorStream();
200 xml = XMLTools.readXMLStream(input_stream);
201 }
202
203 if (xml.length() > 0) {
204 parsePluginInfoXML(plugin, xml.toString());
205 }
206 else {
207 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.PlugInManager.PlugIn_XML_Parse_Failed", plugin.getName()), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
208 }
209 }
210 catch (Exception exception) {
211 DebugStream.printStackTrace(exception);
212 }
213 }
214
215
216 static public void loadPluginsList()
217 {
218 DebugStream.println("In loadPluginsList()...");
219
220 // Run pluginfo.pl to get the list of plugins
221 try {
222 StringBuffer xml = null;
223 if (Gatherer.isGsdlRemote) {
224 String pluginfo_output = RemoteGreenstoneServer.getScriptOptions("pluginfo.pl", "&listall");
225 xml = new StringBuffer(pluginfo_output);
226 }
227 else {
228 ArrayList args = new ArrayList();
229 if (Utility.isWindows()) {
230 args.add(Configuration.perl_path);
231 args.add("-S");
232 }
233 args.add(LocalGreenstone.getBinScriptDirectoryPath() + "pluginfo.pl");
234 args.add("-listall");
235 args.add("-xml");
236
237 // Run the pluginfo.pl process
238 Runtime runtime = Runtime.getRuntime();
239 Process process = runtime.exec((String[]) args.toArray(new String[] { }));
240 InputStream input_stream = process.getErrorStream();
241 xml = XMLTools.readXMLStream(input_stream);
242 }
243
244 if (xml.length() > 0) {
245 core_greenstone_plugins_list = parsePluginsListXML(xml.toString());
246 }
247 else {
248 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.PluginManager.Plugin_List_XML_Parse_Failed"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
249 }
250 }
251 catch (Exception exception) {
252 DebugStream.println("Failed when trying to list plugins.");
253 DebugStream.printStackTrace(exception);
254 }
255 }
256
257
258 static private void parsePluginInfoXML(Plugin plugin, String xml)
259 {
260 Document document = XMLTools.parseXML(new StringReader(xml));
261 parsePluginInfoXMLNode(plugin, document.getDocumentElement());
262 }
263
264
265 static private void parsePluginInfoXMLNode(Plugin plugin, Node root_node)
266 {
267 for (Node node = root_node.getFirstChild(); node != null; node = node.getNextSibling()) {
268 String node_name = node.getNodeName();
269
270 if (node_name.equalsIgnoreCase("Name")) {
271 plugin.setName(XMLTools.getValue(node));
272 }
273 else if (node_name.equals("Desc")) {
274 plugin.setDescription(XMLTools.getValue(node));
275 }
276 else if (node_name.equals("Abstract")) {
277 plugin.setIsAbstract(XMLTools.getValue(node).equalsIgnoreCase(CollectionConfiguration.YES_STR));
278 }
279 else if (node_name.equalsIgnoreCase("Explodes")) {
280 plugin.setDoesExplodeMetadataDatabases(XMLTools.getValue(node).equalsIgnoreCase(StaticStrings.YES_STR));
281 }
282 else if (node_name.equalsIgnoreCase("Processes")) {
283 plugin.setDefaultProcessExpression(XMLTools.getValue(node));
284 }
285 else if (node_name.equalsIgnoreCase("Blocks")) {
286 plugin.setDefaultBlockExpression(XMLTools.getValue(node));
287 }
288 // Parse the plugin arguments
289 else if (node_name.equalsIgnoreCase("Arguments")) {
290 for (Node argument_node = node.getFirstChild(); argument_node != null; argument_node = argument_node.getNextSibling()) {
291 // An option
292 if (argument_node.getNodeName().equalsIgnoreCase("Option")) {
293 Argument argument = new Argument();
294 argument.parseXML((Element) argument_node);
295 plugin.addArgument(argument);
296 }
297 }
298 }
299 // A super plugin class
300 else if (node_name.equalsIgnoreCase("PlugInfo")) {
301 Plugin super_plugin = new Plugin();
302 parsePluginInfoXMLNode(super_plugin, node);
303 plugin.setSuper(super_plugin);
304 }
305 }
306 }
307
308
309 static private ArrayList parsePluginsListXML(String xml)
310 {
311 ArrayList plugins_list = new ArrayList();
312
313 Document document = XMLTools.parseXML(new StringReader(xml));
314 Node root = document.getDocumentElement();
315 for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) {
316 String node_name = node.getNodeName();
317
318 if (node_name.equals("PlugInfo")) {
319 Plugin plugin = new Plugin();
320 parsePluginInfoXMLNode(plugin, node);
321 plugins_list.add(plugin);
322 }
323 }
324
325 return plugins_list;
326 }
327
328
329 // --------------------------------------------------------------------------------------------------------
330
331
332 /** Method to assign a plugin
333 * @param plugin the Plugin to assign
334 */
335 private void assignPlugin(Plugin plugin) {
336 if(plugin.getName().equals(StaticStrings.RECPLUG_STR) || plugin.getName().equals(StaticStrings.ARCPLUG_STR)) {
337 addAfter(plugin, separator_plugin); // Adds after separator
338 } else {
339 addBefore(plugin, separator_plugin);
340 }
341 Gatherer.c_man.configurationChanged();
342 }
343
344
345 /** Destructor. */
346 public void destroy()
347 {
348 Gatherer.c_man.removeCollectionContentsChangedListener(this);
349
350 if (controls != null) {
351 controls.destroy();
352 controls = null;
353 }
354 }
355
356
357 /** This function listens for new files being added to the collection and hints about suitable plugins. */
358 public void fileAddedToCollection(File file)
359 {
360 // First check the plugins already assigned in the collection
361 for (int i = 0; i < super.getSize(); i++) {
362 Plugin assigned_plugin = (Plugin) getElementAt(i);
363 if (assigned_plugin.isSeparator() == false && (assigned_plugin.doesProcessFile(file) == true || assigned_plugin.doesBlockFile(file) == true)) {
364 // This file will be processed by an assigned plugin, so no suggestion is necessary
365 DebugStream.println("Processed by assigned plugin: " + assigned_plugin);
366 return;
367 }
368 }
369
370 // Next try the plugins NOT already assigned in the collection
371 ArrayList suitable_plugins = new ArrayList();
372 Object[] unassigned_plugins = getAvailablePlugins();
373 for (int i = 0; i < unassigned_plugins.length; i++) {
374 Plugin unassigned_plugin = (Plugin) unassigned_plugins[i];
375 if (unassigned_plugin.doesProcessFile(file) == true) {
376 DebugStream.println("Processed by unassigned plugin: " + unassigned_plugin);
377 suitable_plugins.add(unassigned_plugin);
378 }
379 }
380
381 // If there appear to be no suitable plugins, warn the user about this and be done
382 if (suitable_plugins.size() == 0) {
383 String[] args = new String[1];
384 args[0] = file.getName();
385 WarningDialog warning_dialog = new WarningDialog("warning.NoPluginExpectedToProcessFile", "NoPluginExpectedToProcessFile.Title", Dictionary.get("NoPluginExpectedToProcessFile.Message", args), null, false);
386 warning_dialog.display();
387 warning_dialog.dispose();
388 return;
389 }
390
391 // Generate a dialog
392 new PluginSuggestionPrompt(file.getName(), suitable_plugins);
393 }
394
395
396 /** Method to retrieve the control for this manager.
397 * @return the Control
398 */
399 public Control getControls() {
400 if(controls == null) {
401 // Build controls
402 controls = new PluginControl();
403 }
404 return controls;
405 }
406
407
408 /** Overrides getSize in DOMProxyListModel to take into account the row count modifier used to hide the last three rows in lower detail modes
409 * @return an int indicating the number of rows in the model, or more correctly the desired number of rows to display
410 */
411 public int getSize() {
412 int result = super.getSize();
413 if(modify_row_count) {
414 result = result-3;
415 }
416 return result;
417 }
418
419 /** Called when the detail mode has changed which in turn may cause several design elements to be available/hidden
420 * @param mode the new mode as an int
421 */
422 public void modeChanged(int mode) {
423 if(controls != null) {
424 ((PluginControl)controls).modeChanged(mode);
425 }
426 }
427
428 /** Method to move a plugin in the list order.
429 * @param plugin the Plugin you want to move.
430 * @param direction true to move the plugin up, false to move it down.
431 * @param all true to move to move all the way, false for a single step.
432 */
433 // why are all the error notices there when the buttons are disabled is you cant move???
434 private void movePlugin(Plugin plugin, boolean direction, boolean all) {
435 // Can't ever move RecPlug or ArcPlug
436 if(super.getSize() < 4) {
437 //DebugStream.println("Not enough plugins to allow moving.");
438 return;
439 }
440 if(plugin.getName().equals(StaticStrings.ARCPLUG_STR) || plugin.getName().equals(StaticStrings.RECPLUG_STR)) {
441 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.Fixed"), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
442 return;
443 }
444 if(all) {
445 // Move to top
446 if(direction) {
447 // Remove the moving plugin
448 remove(plugin);
449 // Retrieve the first plugin
450 Plugin first_plugin = (Plugin) getElementAt(0);
451 // Add the moving plugin before the first plugin
452 addBefore(plugin, first_plugin);
453 first_plugin = null;
454 Gatherer.c_man.configurationChanged();
455 }
456 else {
457 // Remove the moving plugin
458 remove(plugin);
459 // Add the moving plugin before the separator
460 addBefore(plugin, separator_plugin);
461 Gatherer.c_man.configurationChanged();
462 }
463 }
464 else {
465 // Try to move the plugin one step in the desired direction.
466 int index = indexOf(plugin);
467 ///ystem.err.println("Index of " + plugin + " = " + index);
468 if(direction) {
469 index--;
470 if(index < 0) {
471 String args[] = new String[2];
472 args[0] = Dictionary.get("CDM.PlugInManager.PlugIn_Str");
473 args[1] = plugin.getName();
474 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.At_Top", args), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
475 return;
476 }
477 remove(plugin);
478 add(index, plugin);
479 Gatherer.c_man.configurationChanged();
480 }
481 else {
482 index++;
483 Plugin next_plugin = (Plugin) getElementAt(index);
484 if(next_plugin.isSeparator()) {
485 String args[] = new String[1];
486 args[0] = plugin.getName();
487 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.Cannot", args), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
488 // Still not going to move RecPlug or ArcPlug.
489 return;
490 }
491 remove(plugin);
492 add(index, plugin);
493 Gatherer.c_man.configurationChanged();
494 }
495 }
496 }
497
498 /** 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.
499 */
500 public void placeSeparator() {
501 ///ystem.err.println("Placing separator.");
502 int separator_index = super.getSize();
503 if(separator_index > 0) {
504 boolean found_fixed = false;
505 int index = separator_index - 1;
506 while(index > 0) {
507 Plugin plugin = (Plugin) getElementAt(index);
508 String name = plugin.getName();
509 if(name.equals(StaticStrings.RECPLUG_STR) || name.equals(StaticStrings.ARCPLUG_STR)) {
510 found_fixed = true;
511 index--;
512 }
513 else {
514 if(found_fixed) {
515 separator_index = index + 1;
516 index = -1;
517 }
518 else {
519 index--;
520 }
521 }
522 name = null;
523 plugin = null;
524 }
525 }
526 Element element = CollectionDesignManager.collect_config.document.createElement(CollectionConfiguration.PLUGIN_ELEMENT);
527 element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, CollectionConfiguration.SEPARATOR_ATTRIBUTE);
528 element.setAttribute(CollectionConfiguration.SEPARATOR_ATTRIBUTE, CollectionConfiguration.TRUE_STR);
529 separator_plugin = new Plugin(element, null);
530 ///atherer.println("Adding plugin separator at: " + separator_index);
531 add(separator_index, separator_plugin);
532 }
533
534 /** 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.
535 * @param plugin The <strong>Plugin</strong> to remove.
536 */
537 private void removePlugin(Plugin plugin) {
538 remove(plugin);
539 Gatherer.c_man.configurationChanged();
540 }
541
542
543 /** Inform the model to hide/show the last three lines on the list.
544 * @param modify_row_count true to hide the last three lines, false otherwise
545 */
546 private void setHideLines(boolean modify_row_count) {
547 this.modify_row_count = modify_row_count;
548 int original_size = super.getSize();
549 if(modify_row_count) {
550 fireIntervalRemoved(this, original_size - 4, original_size - 1);
551 }
552 else {
553 fireIntervalAdded(this, original_size - 4, original_size - 1);
554 }
555 }
556
557 /** Determine the current separator index. */
558 private int findSeparatorIndex() {
559 int separator_index = super.getSize() - 1;
560 while(separator_index >= 0) {
561 Plugin search = (Plugin) getElementAt(separator_index);
562 if(search.isSeparator()) {
563 return separator_index;
564 }
565 separator_index--;
566 }
567 return separator_index;
568 }
569
570
571 /** A class which provodes controls for assigned and editing plugins. */
572 private class PluginControl
573 extends JPanel
574 implements Control {
575 /** Button for adding plugins. */
576 private JButton add = null;
577 /** Button for configuring the selected plugin. */
578 private JButton configure = null;
579 /** Button to move an assigned plugin one position lower in the order. */
580 private JButton move_down_button = null;
581 /** Button to move an assigned plugin one position higher in the order. */
582 private JButton move_up_button = null;
583 /** Button to remove the selected plugin. */
584 private JButton remove = null;
585 /** A combobox containing all of the known plugins, including those that may have already been assigned. */
586 private JComboBox plugin_combobox = null;
587 /** The label next to the plugin combobox. */
588 private JLabel plugin_label = null;
589 /** The label above the assigned plugin list. */
590 private JLabel plugin_list_label = null;
591 /** A list of assigned plugins. */
592 private JList plugin_list = null;
593 /** The area where the add, configure and remove buttons are placed. */
594 private JPanel button_pane = null;
595 /** The region which divides the central portion of the view into list and controls */
596 private JPanel central_pane = null;
597 /** The area where movement buttons are placed. */
598 private JPanel movement_pane = null;
599 /** The small region containing the plugin combobox and its label. */
600 private JPanel plugin_pane = null;
601 /** The pane containing the assigned plugin list and its label. */
602 private JPanel plugin_list_pane = null;
603
604 /** Constructor.
605 */
606 public PluginControl()
607 {
608 // Create
609 add = new GLIButton(Dictionary.get("CDM.PlugInManager.Add"), Dictionary.get("CDM.PlugInManager.Add_Tooltip"));
610
611 button_pane = new JPanel();
612 central_pane = new JPanel();
613
614 configure = new GLIButton(Dictionary.get("CDM.PlugInManager.Configure"), Dictionary.get("CDM.PlugInManager.Configure_Tooltip"));
615 configure.setEnabled(false);
616
617 JPanel header_pane = new DesignPaneHeader("CDM.GUI.Plugins", "plugins");
618 move_up_button = new GLIButton(Dictionary.get("CDM.Move.Move_Up"), JarTools.getImage("arrow-up.gif"), Dictionary.get("CDM.Move.Move_Up_Tooltip"));
619 move_up_button.setEnabled(false);
620
621 move_down_button = new GLIButton(Dictionary.get("CDM.Move.Move_Down"), JarTools.getImage("arrow-down.gif"), Dictionary.get("CDM.Move.Move_Down_Tooltip"));
622 move_down_button.setEnabled(false);
623
624 movement_pane = new JPanel();
625
626 PluginComboboxListener picl = new PluginComboboxListener();
627 plugin_combobox = new JComboBox(getAvailablePlugins());
628 plugin_combobox.setEditable(false);
629 picl.itemStateChanged(new ItemEvent(plugin_combobox, 0, null, ItemEvent.SELECTED));
630
631 plugin_label = new JLabel(Dictionary.get("CDM.PlugInManager.PlugIn"));
632
633 plugin_list = new JList(model);
634 plugin_list.setCellRenderer(new ListRenderer());
635 plugin_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
636 plugin_list_label = new JLabel(Dictionary.get("CDM.PlugInManager.Assigned"));
637 //plugin_list_label.setHorizontalAlignment(JLabel.CENTER);
638 plugin_list_label.setOpaque(true);
639
640 plugin_list_pane = new JPanel();
641 plugin_pane = new JPanel();
642
643 remove = new GLIButton(Dictionary.get("CDM.PlugInManager.Remove"), Dictionary.get("CDM.PlugInManager.Remove_Tooltip"));
644 remove.setEnabled(false);
645
646 // Listeners
647 add.addActionListener(new AddListener()); //all_change_listener is listening to the ArgumentConfiguration
648 configure.addActionListener(new ConfigureListener());
649 MoveListener ml = new MoveListener();
650 move_down_button.addActionListener(ml);
651 move_down_button.addActionListener(CollectionDesignManager.all_change_listener);
652 move_up_button.addActionListener(ml);
653 move_up_button.addActionListener(CollectionDesignManager.all_change_listener);
654 plugin_combobox.addItemListener(picl);
655 remove.addActionListener(new RemoveListener());
656 remove.addActionListener(CollectionDesignManager.all_change_listener);
657 plugin_list.addMouseListener(new ClickListener());
658 plugin_list.addListSelectionListener(new ListListener());
659 picl = null;
660
661 // Layout
662 movement_pane.setBorder(BorderFactory.createEmptyBorder(0,2,0,0));
663 movement_pane.setLayout(new GridLayout(4,1));
664 movement_pane.add(move_up_button);
665 movement_pane.add(new JPanel());
666 movement_pane.add(new JPanel());
667 movement_pane.add(move_down_button);
668
669 plugin_list_pane.setLayout(new BorderLayout());
670 plugin_list_pane.add(plugin_list_label, BorderLayout.NORTH);
671 plugin_list_pane.add(new JScrollPane(plugin_list), BorderLayout.CENTER);
672 modeChanged(Configuration.getMode()); // Whether the movement buttons are visible is mode dependant
673
674 plugin_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
675
676 plugin_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
677 plugin_pane.setLayout(new BorderLayout(5,0));
678 plugin_pane.add(plugin_label, BorderLayout.WEST);
679 plugin_pane.add(plugin_combobox, BorderLayout.CENTER);
680
681 button_pane.setLayout(new GridLayout(1,3));
682 button_pane.add(add);
683 button_pane.add(configure);
684 button_pane.add(remove);
685
686 // Scope these mad bordering skillz.
687 JPanel temp = new JPanel(new BorderLayout());
688 temp.add(plugin_pane, BorderLayout.NORTH);
689 temp.add(button_pane, BorderLayout.SOUTH);
690
691 central_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
692 central_pane.setLayout(new BorderLayout());
693 central_pane.add(plugin_list_pane, BorderLayout.CENTER);
694 central_pane.add(temp, BorderLayout.SOUTH);
695
696 setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
697 setLayout(new BorderLayout());
698 add(header_pane, BorderLayout.NORTH);
699 add(central_pane, BorderLayout.CENTER);
700 }
701
702 /** Method which acts like a destructor, tidying up references to persistant objects.
703 */
704 public void destroy() {
705 }
706
707 /** This method is overridden to ensure the instructions are scrolled to top, before the super classes updateUI() is called.
708 no longer have instructions, do we still need updateUI???
709 */
710 public void gainFocus() {
711 super.updateUI();
712 }
713
714 public void loseFocus() {
715 }
716
717 /** 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
718 * @param mode the current mode as an int, which can be matched against static ints in the Configuration class
719 */
720 public void modeChanged(int mode) {
721 // 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
722 plugin_list.clearSelection();
723 // The first change is dependant on whether the user is systems mode or higher
724 if(mode >= Configuration.SYSTEMS_MODE) {
725 // Show movement buttons
726 plugin_list_pane.add(movement_pane, BorderLayout.EAST);
727 // Do we show Arc and RecPlugs or hide them and the separator line
728 setHideLines(!(mode >= Configuration.EXPERT_MODE));
729 }
730 // Otherwise hide the movement buttons and fixed plugins
731 else {
732 plugin_list_pane.remove(movement_pane);
733 setHideLines(true);
734 }
735 plugin_list_pane.updateUI();
736 }
737
738
739 private class AddListener
740 implements ActionListener
741 {
742 public void actionPerformed(ActionEvent event)
743 {
744 if (plugin_combobox.getSelectedItem() != null) {
745 // This must be done on a new thread for the remote building code
746 new AddPluginTask(plugin_combobox.getSelectedItem().toString()).start();
747 }
748 }
749 }
750
751
752 private class AddPluginTask
753 extends Thread
754 {
755 private String plugin_name;
756
757 public AddPluginTask(String plugin_name)
758 {
759 this.plugin_name = plugin_name;
760 }
761
762 public void run()
763 {
764 // Retrieve the plugin
765 Plugin plugin = getPlugin(plugin_name, true);
766 if (plugin == null) {
767 System.err.println("Error: getPlugin() returned null.");
768 return;
769 }
770
771 // Create a new element in the DOM
772 Element new_plugin_element = CollectionDesignManager.collect_config.document.createElement(CollectionConfiguration.PLUGIN_ELEMENT);
773 new_plugin_element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, plugin.getName());
774 Plugin new_plugin = new Plugin(new_plugin_element, plugin);
775
776 ArgumentConfiguration ac = new ArgumentConfiguration(new_plugin);
777 if (ac.display()) {
778 assignPlugin(new_plugin);
779 plugin_list.setSelectedValue(new_plugin, true);
780
781 // Remove the plugin from the available list (unless we're in a high mode, or it's UnknownPlug)
782 if (Configuration.getMode() <= Configuration.SYSTEMS_MODE && !plugin_name.equals(StaticStrings.UNKNOWNPLUG_STR)) {
783 plugin_combobox.removeItem(plugin);
784 plugin_combobox.setSelectedIndex(0);
785 }
786 }
787 }
788 }
789
790
791 /** Listens for double clicks apon the list and react as if the configure button was pushed. */
792 private class ClickListener
793 extends MouseAdapter {
794 /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt.
795 * @param event A <strong>MouseEvent</strong> containing information about the mouse click.
796 */
797 public void mouseClicked(MouseEvent event) {
798 if(event.getClickCount() == 2 ) {
799 if(!plugin_list.isSelectionEmpty()) {
800 Plugin plugin = (Plugin) plugin_list.getSelectedValue();
801 if(!plugin.isSeparator()) {
802 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
803 if(ac.display()) {
804 refresh(plugin);
805 }
806 ac.destroy();
807 ac = null;
808 // cos I can't be bothered checking every argument to see if it has changed or not, we'll asasume that the configuration has changed if someone has clicked configure
809 Gatherer.c_man.configurationChanged();
810 }
811 }
812 }
813 }
814 }
815
816 /** 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.
817 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration
818 */
819 private class ConfigureListener
820 implements ActionListener {
821 /** 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.
822 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
823 */
824 public void actionPerformed(ActionEvent event) {
825 if(!plugin_list.isSelectionEmpty()) {
826 Plugin plugin = (Plugin) plugin_list.getSelectedValue();
827 if(!plugin.isSeparator()) {
828 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
829 if(ac.display()) {
830 refresh(plugin);
831 }
832 ac.destroy();
833 ac = null;
834 // cos I can't be bothered checking every argument to see if it has changed or not, we'll asasume that the configuration has changed if someone has clicked configure
835 Gatherer.c_man.configurationChanged();
836 }
837 }
838 }
839 }
840
841 /** 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 */
842 private class ListListener
843 implements ListSelectionListener {
844
845 public void valueChanged(ListSelectionEvent e) {
846 if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one (the second one)
847 if (plugin_list.isSelectionEmpty()) {
848 move_up_button.setEnabled(false);
849 move_down_button.setEnabled(false);
850 configure.setEnabled(false);
851 remove.setEnabled(false);
852 }
853 else {
854 Plugin selected_plugin = (Plugin) plugin_list.getSelectedValue();
855 if(selected_plugin.isSeparator()) {
856 move_up_button.setEnabled(false);
857 move_down_button.setEnabled(false);
858 configure.setEnabled(false);
859 remove.setEnabled(false);
860 }
861 else {
862 configure.setEnabled(true);
863 String plugin_name = selected_plugin.getName();
864 // Some buttons are only available for plugins other than ArcPlug and RecPlug
865 if(plugin_name.equals(StaticStrings.ARCPLUG_STR) || plugin_name.equals(StaticStrings.RECPLUG_STR) ) {
866 move_up_button.setEnabled(false);
867 move_down_button.setEnabled(false);
868 remove.setEnabled(false);
869 }
870 else {
871 // don't let people remove special plugins such GAPlug an METSPlug,
872 // unless they are in systems mode or above
873 int mode = Configuration.getMode();
874 for (int i=0; i<StaticStrings.KEEP_PLUG.length; i++) {
875 if ((plugin_name.equals(StaticStrings.KEEP_PLUG[i])) &&
876 (mode < Configuration.SYSTEMS_MODE)) {
877 remove.setEnabled(false);
878 break;
879 } else {
880 remove.setEnabled(true);
881 }
882 }
883
884 // Move ups are only enabled if the selected plugin isn't already at the top
885 Plugin first_plugin = (Plugin) getElementAt(0);
886 if(!first_plugin.equals(selected_plugin)) {
887 move_up_button.setEnabled(true);
888 }
889 else {
890 move_up_button.setEnabled(false);
891 }
892 // And move downs are only allowed when the selected plugin isn't at an index one less than the separator line.
893 int separator_index = findSeparatorIndex();
894 int selected_index = plugin_list.getSelectedIndex();
895 if(selected_index < separator_index - 1) {
896 move_down_button.setEnabled(true);
897 }
898 else {
899 move_down_button.setEnabled(false);
900 }
901 }
902 selected_plugin = null;
903 plugin_name = null;
904 }
905 }
906 }
907 }
908 }
909
910 /** A special list renderer which is able to render separating lines as well. */
911 private class ListRenderer
912 extends DefaultListCellRenderer {
913 /** 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.
914 * @param list - The <strong>JList</strong> we're painting.
915 * @param value - The value returned by list.getModel().getElementAt(index) as an <strong>Object</strong>.
916 * @param index - The cells index as an <i>int</i>.
917 * @param isSelected - <i>true</i> if the specified cell was selected.
918 * @param cellHasFocus - <i>true</i> if the specified cell has the focus.
919 * @return A <strong>Component</strong> whose paint() method will render the specified value.
920 * @see javax.swing.JList
921 * @see javax.swing.JSeparator
922 * @see javax.swing.ListModel
923 * @see javax.swing.ListSelectionModel
924 */
925 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
926 Plugin plugin = (Plugin) value;
927 if(plugin.isSeparator()) {
928 return separator;
929 }
930 else {
931 return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
932 }
933 }
934 }
935
936
937 /** 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. */
938 private class MoveListener
939 implements ActionListener {
940 /** 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.
941 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
942 */
943 public void actionPerformed(ActionEvent event) {
944 if (!plugin_list.isSelectionEmpty()) {
945 Object object = plugin_list.getSelectedValue();
946 if (object instanceof Plugin) {
947 Plugin plugin = (Plugin) object;
948 if (event.getSource() == move_up_button) {
949 movePlugin(plugin, true, false);
950 }
951 else if (event.getSource() == move_down_button) {
952 movePlugin(plugin, false, false);
953 }
954 plugin_list.setSelectedValue(plugin, true);
955 }
956 }
957 }
958 }
959
960 /** This listener reacts to changes in the current selection of the plugin combobox. */
961 private class PluginComboboxListener
962 implements ItemListener {
963 /** When a user selects a certain plugin, update the tooltip to show the plugin description. */
964 public void itemStateChanged(ItemEvent event) {
965 if(event.getStateChange() == ItemEvent.SELECTED) {
966 // Retrieve the selected plugin
967 Object current_selection = plugin_combobox.getSelectedItem();
968 // And reset the tooltip. If the plugin is null or is a string, then go back to the default message
969 if(current_selection == null || current_selection instanceof String) {
970 plugin_combobox.setToolTipText(Dictionary.get("CDM.PlugInManager.PlugIn_Tooltip"));
971 }
972 else {
973 Plugin current_plugin = (Plugin) current_selection;
974 plugin_combobox.setToolTipText(Utility.formatHTMLWidth(current_plugin.getDescription(), 40));
975 current_plugin = null;
976 }
977 current_selection = null;
978 }
979 }
980 }
981
982 /** This class listens for actions upon the remove button in the controls, and if detected calls the <i>removePlugin()</i> method.
983 */
984 private class RemoveListener
985 implements ActionListener {
986 /** 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.
987 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
988 */
989 public void actionPerformed(ActionEvent event) {
990 int selected_index = plugin_list.getSelectedIndex();
991 if(selected_index != -1) {
992 Plugin selected_plugin = (Plugin) plugin_list.getSelectedValue();
993 removePlugin(selected_plugin);
994 selected_plugin = null;
995 // Select the next plugin if available
996 if(selected_index < plugin_list.getModel().getSize()) {
997 // If the new selection is above the separator we can remove it
998 if(selected_index < findSeparatorIndex()) {
999 plugin_list.setSelectedIndex(selected_index);
1000
1001 // don't let people remove special plugins such GAPlug an METSPlug,
1002 // unless they are in systems mode or above
1003 int mode = Configuration.getMode();
1004 for (int i=0; i<StaticStrings.KEEP_PLUG.length; i++) {
1005 String selected_plugin_name
1006 = ((Plugin)plugin_list.getSelectedValue()).getName();
1007 if ((selected_plugin_name.equals(StaticStrings.KEEP_PLUG[i])) &&
1008 (mode < Configuration.SYSTEMS_MODE)) {
1009 remove.setEnabled(false);
1010 break;
1011 } else {
1012 remove.setEnabled(true);
1013 }
1014 }
1015 }
1016 // Otherwise select the first non-removable plugin
1017 else {
1018 plugin_list.setSelectedIndex(selected_index + 1);
1019 remove.setEnabled(false);
1020 }
1021 }
1022 else {
1023 remove.setEnabled(false);
1024 }
1025 // Refresh the available plugins
1026 plugin_combobox.setModel(new DefaultComboBoxModel(getAvailablePlugins()));
1027 }
1028 else {
1029 remove.setEnabled(false);
1030 }
1031 }
1032 }
1033 }
1034
1035
1036 private class PluginSuggestionPrompt
1037 extends ModalDialog
1038 implements ActionListener
1039 {
1040 private Dimension size = new Dimension(480, 240);
1041 private GComboBox suitable_plugins_combobox = null;
1042 private GLIButton add_button = null;
1043 private GLIButton ignore_button = null;
1044
1045 public PluginSuggestionPrompt(String filename, ArrayList suitable_plugins)
1046 {
1047 super(Gatherer.g_man, true);
1048 setModal(true);
1049 setSize(size);
1050 setTitle(Dictionary.get("CDM.PluginManager.SuggestedPluginListTitle"));
1051
1052 String[] args = new String[1];
1053 args[0] = filename;
1054
1055 JTextArea instructions_textarea = new JTextArea(Dictionary.get("CDM.PluginManager.Plugin_Suggestion_Prompt", args));
1056 instructions_textarea.setCaretPosition(0);
1057 instructions_textarea.setEditable(false);
1058 instructions_textarea.setLineWrap(true);
1059 instructions_textarea.setRows(5);
1060 instructions_textarea.setWrapStyleWord(true);
1061
1062 JLabel suitable_plugins_label = new JLabel(Dictionary.get("CDM.PlugInManager.PlugIn"));
1063 suitable_plugins_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
1064
1065 suitable_plugins_combobox = new GComboBox(suitable_plugins);
1066 suitable_plugins_combobox.setBackgroundNonSelectionColor(Configuration.getColor("coloring.editable_background", false));
1067 suitable_plugins_combobox.setBackgroundSelectionColor(Configuration.getColor("coloring.collection_selection_background", false));
1068 suitable_plugins_combobox.setTextNonSelectionColor(Configuration.getColor("coloring.workspace_tree_foreground", false));
1069 suitable_plugins_combobox.setTextSelectionColor(Configuration.getColor("coloring.collection_selection_foreground", false));
1070
1071 JPanel suitable_plugins_pane = new JPanel();
1072 //suitable_plugins_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
1073 suitable_plugins_pane.setLayout(new BorderLayout(5,0));
1074 suitable_plugins_pane.add(suitable_plugins_label, BorderLayout.WEST);
1075 suitable_plugins_pane.add(suitable_plugins_combobox, BorderLayout.CENTER);
1076
1077 add_button = new GLIButton(Dictionary.get("CDM.PlugInManager.QuickAdd"), Dictionary.get("CDM.PlugInManager.Add_Tooltip"));
1078 ignore_button = new GLIButton(Dictionary.get("CDM.PlugInManager.Ignore"), Dictionary.get("CDM.PlugInManager.Ignore_Tooltip"));
1079
1080 add_button.addActionListener(this);
1081 ignore_button.addActionListener(this);
1082
1083 JPanel button_pane = new JPanel();
1084 button_pane.setLayout(new GridLayout(1,2,5,0));
1085 button_pane.add(add_button);
1086 button_pane.add(ignore_button);
1087
1088 JPanel controls_pane = new JPanel();
1089 controls_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
1090 controls_pane.setLayout(new GridLayout(2,1,0,5));
1091 controls_pane.add(suitable_plugins_pane);
1092 controls_pane.add(button_pane);
1093
1094 JPanel content_pane = (JPanel) getContentPane();
1095 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
1096 content_pane.setLayout(new BorderLayout());
1097 content_pane.add(instructions_textarea, BorderLayout.CENTER);
1098 content_pane.add(controls_pane, BorderLayout.SOUTH);
1099
1100 // Show
1101 Dimension screen_size = Configuration.screen_size;
1102 setLocation((screen_size.width - size.width) / 2, (screen_size.height - size.height) / 2);
1103 setVisible(true);
1104 }
1105
1106
1107 public void actionPerformed(ActionEvent event)
1108 {
1109 // Close the dialog
1110 setVisible(false);
1111
1112 if (event.getSource() == add_button) {
1113 new AddPluginTask(suitable_plugins_combobox.getSelectedItem().toString()).start();
1114 }
1115 }
1116 }
1117
1118
1119 private class AddPluginTask
1120 extends Thread
1121 {
1122 private String plugin_name;
1123
1124 public AddPluginTask(String plugin_name)
1125 {
1126 this.plugin_name = plugin_name;
1127 }
1128
1129 public void run()
1130 {
1131 // Retrieve the plugin
1132 Plugin plugin = getPlugin(plugin_name, true);
1133 if (plugin == null) {
1134 System.err.println("Error: getPlugin() returned null.");
1135 return;
1136 }
1137
1138 // Create a new element in the DOM
1139 Element new_plugin_element = CollectionDesignManager.collect_config.document.createElement(CollectionConfiguration.PLUGIN_ELEMENT);
1140 new_plugin_element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, plugin.getName());
1141 Plugin new_plugin = new Plugin(new_plugin_element, plugin);
1142
1143 // Just assign the plugin with no argument configuration
1144 assignPlugin(new_plugin);
1145 }
1146 }
1147
1148
1149 /** Creates a list separator.
1150 * Found on Google Groups. Code courtesy of Paul Farwell.
1151 */
1152 private JPanel getSeparator() {
1153 // We put the separator inside a panel to control its appearance
1154 JPanel _sepPanel = new JPanel();
1155 _sepPanel.setOpaque(false);
1156 _sepPanel.setBorder(BorderFactory.createEmptyBorder(1, 3, 1, 3));
1157 _sepPanel.setLayout(new BoxLayout(_sepPanel, BoxLayout.Y_AXIS));
1158 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
1159 // 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
1160 _sepPanel.add(new BasicSeparator());
1161 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
1162 return _sepPanel;
1163 }
1164
1165 /** 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. */
1166 private static class BasicSeparator
1167 extends JSeparator {
1168
1169 private ComponentUI basic_ui;
1170
1171 public BasicSeparator() {
1172 super();
1173 basic_ui = new BasicSeparatorUI();
1174 }
1175
1176 public void paintComponent(Graphics g) {
1177 if (basic_ui != null) {
1178 basic_ui.update(g, this);
1179 }
1180 }
1181 }
1182}
Note: See TracBrowser for help on using the repository browser.