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

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

The lists for selecting PlugIns and Classifiers now show the description of the aforementioned items if you hover over the combobox. If no description is a available it defaults to the appropriate message from the dictionary.

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