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

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

Added a couple of bits for the new plugins/classifiers stuff when remote building.

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