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

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

Moved the XMLStringToDOM() function from Utility into CollectionDesignManager, as it depends on the Gatherer class.

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