source: trunk/gli/src/org/greenstone/gatherer/cdm/PlugInManager.java@ 8231

Last change on this file since 8231 was 8231, checked in by mdewsnip, 20 years ago

Replaced all "Gatherer.config" with "Configuration".

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