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

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

Replaced all Gatherer.print* with DebugStream.print*.

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