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

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

Kissed the horrible old plugins.dat and classifiers.dat files goodbye...

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