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

Last change on this file since 6540 was 6389, checked in by jmt12, 20 years ago

Introduced the idea of detail modes - these have an effect on several parts of the gui, such as disabling or hiding all regular expression based controls, simplifying the output from perl scripts and (having been given yet another new last minute feature to implement) displays a completely different create pane. Mode is stored in the config.xml and is changable via the Preferences controls

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