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

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

When a plugin is unassigned the list of available plugins stays sorted

  • Property svn:keywords set to Author Date Id Revision
File size: 38.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.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 Collections.sort(available);
300 return available.toArray();
301 }
302
303 /** Method to extract just the plugins name from a file object.
304 * @param plugin The <strong>File</strong> which references a certain plugin.
305 * @return A <strong>String</strong> containing just the plugins name, without extension.
306 */
307 private String getPlugInName(File plugin) {
308 String name = plugin.getName();
309 if(name.indexOf(".") != -1) {
310 name = name.substring(0, name.indexOf("."));
311 }
312 return name;
313 }
314
315 /** Method to load the details of a single plug-in.
316 * @param plugin The plugin <strong>File</strong> you wish to load.
317 */
318 private void loadPlugIn(File plugin) {
319 Document document = null;
320 // Run pluginfo on this plugin, and then send the results for parsing.
321 try {
322 String args[] = null;
323 if(Utility.isWindows()) {
324 args = new String[4];
325 if(Gatherer.config.perl_path != null) {
326 args[0] = Gatherer.config.perl_path;
327 }
328 else {
329 args[0] = "Perl.exe";
330 }
331 args[1] = Gatherer.config.gsdl_path + "bin" + File.separator + "script" + File.separator + "pluginfo.pl";
332 args[2] = "-xml";
333 args[3] = getPlugInName(plugin);
334 }
335 else {
336 args = new String[3];
337 args[0] = "pluginfo.pl";
338 args[1] = "-xml";
339 args[2] = getPlugInName(plugin);
340 }
341 // Create the process.
342 Runtime runtime = Runtime.getRuntime();
343 Process process = runtime.exec(args);
344 //InputStream input_stream = process.getErrorStream();
345 BufferedReader error_in = new BufferedReader(new InputStreamReader(process.getErrorStream()));
346 String line = "";
347 StringBuffer xml = new StringBuffer("");
348 boolean xml_content = false;
349 while((line = error_in.readLine()) != null) {
350 if(xml_content) {
351 xml.append(line);
352 xml.append("\n");
353 }
354 else if(line.trim().startsWith("<?xml")) {
355 xml_content = true;
356 xml.append(line);
357 xml.append("\n");
358 }
359 }
360 error_in = null;
361 process = null;
362 runtime = null;
363 args = null;
364 // If something has gone horribly wrong then xml will be empty.
365 if(xml.length() != 0) {
366 // Then read the xml from the piped input stream.
367 InputSource source = new InputSource(new StringReader(xml.toString()));
368 DOMParser parser = new DOMParser();
369 parser.parse(source);
370 document = parser.getDocument();
371 parser = null;
372 source = null;
373 }
374 else {
375 String plugin_name = getPlugInName(plugin);
376 //Gatherer.println("Zero length argument xml detected for: " + plugin_name);
377 String[] margs = new String[1];
378 margs[0] = plugin_name;
379 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.PlugInManager.PlugIn_XML_Parse_Failed", margs), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
380 }
381 }
382 catch (Exception error) {
383 System.err.println("Failed when trying to parse: " + plugin.getName());
384 error.printStackTrace();
385 }
386 if(document != null) {
387 parseXML(document.getDocumentElement());
388 }
389 }
390
391 /** Method to initially load information from the standard plug-ins within the gsdl Perl library.
392 */
393 private void loadPlugIns() {
394 // Attempt to restore the cached file.
395 try {
396 FileInputStream file = new FileInputStream(Utility.BASE_DIR + "plugins.dat");
397 ObjectInputStream input = new ObjectInputStream(file);
398 library = (ArrayList) input.readObject();
399 }
400 catch (Exception error) {
401 }
402 if(library == null) {
403 library = new ArrayList();
404 // Retrieve the gsdl home directory...
405 String directory = Gatherer.config.gsdl_path;
406 directory = directory + "perllib" + File.separator + "plugins" + File.separator;
407 loadPlugIns(new File(directory));
408 }
409 }
410 /** Method to load plug-in information from a specified directory. Of course no plug-ins may be found at this location.
411 * @param directory A <strong>File</strong> indicating the directory to be scanned for plug-ins.
412 */
413 private void loadPlugIns(File directory) {
414 File files[] = directory.listFiles();
415 if(files != null) {
416 // Create a progress indicator.
417 ParsingProgress progress = new ParsingProgress(Dictionary.get("CDM.PlugInManager.Parsing.Title"), Dictionary.get("CDM.PlugInManager.Parsing.Message"), files.length);
418 for(int i = 0; i < files.length; i++) {
419 // We only want to check Perl Modules.
420 if(files[i].getName().endsWith(".pm")) {
421 loadPlugIn(files[i]);
422 }
423 progress.inc();
424 }
425 progress.dispose();
426 }
427 }
428
429 private PlugIn parseXML(Node root) {
430 PlugIn plugin = new PlugIn();
431 String node_name = null;
432 for(Node node = root.getFirstChild(); node != null;
433 node = node.getNextSibling()) {
434 node_name = node.getNodeName();
435 if(node_name.equalsIgnoreCase("Name")) {
436 String name = MSMUtils.getValue(node);
437 // 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.
438 PlugIn existing = getBasePlugIn(name);
439 if(existing != null) {
440 return existing;
441 }
442 plugin.setName(name);
443 }
444 else if(node_name.equalsIgnoreCase("Desc")) {
445 plugin.setDescription(MSMUtils.getValue(node));
446 }
447 else if(node_name.equalsIgnoreCase(CollectionConfiguration.ABSTRACT_ELEMENT)) {
448 System.err.println("Is " + plugin.getName() + " abstract? " + MSMUtils.getValue(node).equalsIgnoreCase(CollectionConfiguration.YES_STR));
449 plugin.setIsAbstract(MSMUtils.getValue(node).equalsIgnoreCase(CollectionConfiguration.YES_STR));
450 }
451 // Parse the multitude of arguments.
452 else if(node_name.equalsIgnoreCase("Arguments")) {
453 for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) {
454 node_name = arg.getNodeName();
455 // An option.
456 if(node_name.equalsIgnoreCase("Option")) {
457 Argument argument = new Argument();
458 // If its an option we parse the multitude of details an options might have.
459 for(Node det = arg.getFirstChild(); det != null; det = det.getNextSibling()) {
460 node_name = det.getNodeName();
461 if(node_name.equalsIgnoreCase("Name")) {
462 argument.setName(MSMUtils.getValue(det));
463 }
464 else if(node_name.equalsIgnoreCase("Desc")) {
465 argument.setDescription(MSMUtils.getValue(det));
466 }
467 else if(node_name.equalsIgnoreCase("Type")) {
468 argument.setType(MSMUtils.getValue(det));
469 }
470 else if(node_name.equalsIgnoreCase("Default")) {
471 argument.setDefaultValue(MSMUtils.getValue(det));
472 }
473 else if(node_name.equalsIgnoreCase("List")) {
474 // Two final loops are required to parse lists.
475 for(Node value = det.getFirstChild(); value != null; value = value.getNextSibling()) {
476 if(value.getNodeName().equalsIgnoreCase("Value")) {
477 String key = null;
478 String desc = "";
479 for(Node subvalue = value.getFirstChild(); subvalue != null; subvalue = subvalue.getNextSibling()) {
480 node_name = subvalue.getNodeName();
481 if(node_name.equalsIgnoreCase("Name")) {
482 key = MSMUtils.getValue(subvalue);
483 }
484 else if(node_name.equalsIgnoreCase("Desc")) {
485 desc = MSMUtils.getValue(subvalue);
486 }
487 }
488 if(key != null) {
489 argument.addOption(key, desc);
490 }
491 }
492 }
493 }
494 else if(node_name.equalsIgnoreCase("Required")) {
495 String v = MSMUtils.getValue(det);
496 if(v != null && v.equalsIgnoreCase("yes")) {
497 argument.setRequired(true);
498 }
499 }
500 }
501 plugin.addArgument(argument);
502 }
503 // A super plugin class.
504 else if(node_name.equalsIgnoreCase("PlugInfo")) {
505 PlugIn super_plugin = parseXML(arg);
506 plugin.setSuper(super_plugin);
507 }
508 }
509 }
510 }
511 if(plugin.getName() != null) {
512 addPlugIn(plugin);
513 return plugin;
514 }
515 return null;
516 }
517
518 /** A class which provodes controls for assigned and editing plugins. */
519 private class PlugInControl
520 extends JPanel
521 implements Control {
522 /** Button for adding plugins. */
523 private JButton add = null;
524 /** Button for configuring the selected plugin. */
525 private JButton configure = null;
526 /** Buttom to move an assinged plugin as low in the order as possible. */
527 private JButton move_bottom_button = null;
528 /** Button to move an assigned plugin one position lower in the order. */
529 private JButton move_down_button = null;
530 /** Button to move an assigned plugin as high in the order as possible. */
531 private JButton move_top_button = null;
532 /** Button to move an assigned plugin one position higher in the order. */
533 private JButton move_up_button = null;
534 /** Button to remove the selected plugin. */
535 private JButton remove = null;
536 /** A combobox containing all of the known plugins, including those that may have already been assigned. */
537 private GComboBox plugin = null;
538 /** The label next to the plugin combobox. */
539 private JLabel plugin_label = null;
540 /** The label above the assigned plugin list. */
541 private JLabel plugin_list_label = null;
542 /** The title of this view. */
543 private JLabel title = null;
544 /** A list of assigned plugins. */
545 private JList plugin_list = null;
546 /** The area where the add, configure and remove buttons are placed. */
547 private JPanel button_pane = null;
548 /** The region which divides the central portion of the view into list and controls */
549 private JPanel central_pane = null;
550 /** The area where title label and instructions sit. */
551 private JPanel header_pane = null;
552 /** The area where movement buttons are placed. */
553 private JPanel movement_pane = null;
554 /** The small region containing the plugin combobox and its label. */
555 private JPanel plugin_pane = null;
556 /** The pane containing the assigned plugin list and its label. */
557 private JPanel plugin_list_pane = null;
558 /** The text area containing instructions on the use of this control. */
559 private JTextArea instructions = null;
560
561 /** Constructor.
562 */
563 public PlugInControl() {
564 // Create
565 add = new JButton();
566 add.setMnemonic(KeyEvent.VK_A);
567 Dictionary.registerBoth(add, "CDM.PlugInManager.Add", "CDM.PlugInManager.Add_Tooltip");
568
569 button_pane = new JPanel();
570 central_pane = new JPanel();
571
572 configure = new JButton();
573 configure.setEnabled(false);
574 configure.setMnemonic(KeyEvent.VK_C);
575 Dictionary.registerBoth(configure, "CDM.PlugInManager.Configure", "CDM.PlugInManager.Configure_Tooltip");
576
577 header_pane = new JPanel();
578
579 instructions = new JTextArea();
580 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
581 instructions.setEditable(false);
582 instructions.setLineWrap(true);
583 instructions.setRows(6);
584 instructions.setWrapStyleWord(true);
585 Dictionary.registerText(instructions, "CDM.PlugInManager.Instructions");
586
587 move_top_button = new DoubleImageButton("", Utility.getImage("arrow-top.gif"), Utility.getImage("arrow-top-disabled.gif"));
588 move_top_button.setEnabled(false);
589 move_top_button.setMnemonic(KeyEvent.VK_T);
590 // move_top_button.setDisplayedMnemonicIndex(8); // !! English-centric hack
591 move_top_button.setPreferredSize(Utility.DOUBLE_IMAGE_BUTTON_SIZE);
592 Dictionary.registerBoth(move_top_button, "CDM.Move.Move_Top", "CDM.Move.Move_Top_Tooltip");
593
594 move_up_button = new DoubleImageButton("", Utility.getImage("arrow-up.gif"), Utility.getImage("arrow-up-disabled.gif"));
595 move_up_button.setEnabled(false);
596 move_up_button.setMnemonic(KeyEvent.VK_U);
597 move_up_button.setPreferredSize(Utility.DOUBLE_IMAGE_BUTTON_SIZE);
598 Dictionary.registerBoth(move_up_button, "CDM.Move.Move_Up", "CDM.Move.Move_Up_Tooltip");
599
600 move_down_button = new DoubleImageButton("", Utility.getImage("arrow-down.gif"), Utility.getImage("arrow-down-disabled.gif"));
601 move_down_button.setEnabled(false);
602 move_down_button.setMnemonic(KeyEvent.VK_D);
603 move_down_button.setPreferredSize(Utility.DOUBLE_IMAGE_BUTTON_SIZE);
604 Dictionary.registerBoth(move_down_button, "CDM.Move.Move_Down", "CDM.Move.Move_Down_Tooltip");
605
606 move_bottom_button = new DoubleImageButton("", Utility.getImage("arrow-bottom.gif"), Utility.getImage("arrow-bottom-disabled.gif"));
607 move_bottom_button.setEnabled(false);
608 move_bottom_button.setMnemonic(KeyEvent.VK_B);
609 move_bottom_button.setPreferredSize(Utility.DOUBLE_IMAGE_BUTTON_SIZE);
610 Dictionary.registerBoth(move_bottom_button, "CDM.Move.Move_Bottom", "CDM.Move.Move_Bottom_Tooltip");
611
612 movement_pane = new JPanel();
613
614 PlugInComboboxListener picl = new PlugInComboboxListener();
615 plugin = new GComboBox(getAvailable());
616 plugin.setBackgroundNonSelectionColor(Gatherer.config.getColor("coloring.editable_background", false));
617 plugin.setBackgroundSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
618 plugin.setEditable(true);
619 plugin.setTextNonSelectionColor(Gatherer.config.getColor("coloring.workspace_tree_foreground", false));
620 plugin.setTextSelectionColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
621 picl.itemStateChanged(new ItemEvent(plugin, 0, null, ItemEvent.SELECTED));
622
623 plugin_label = new JLabel();
624 Dictionary.registerText(plugin_label, "CDM.PlugInManager.PlugIn");
625
626 plugin_list = new JList(model);
627 plugin_list.setCellRenderer(new ListRenderer());
628 plugin_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
629 plugin_list_label = new JLabel();
630 plugin_list_label.setHorizontalAlignment(JLabel.CENTER);
631 plugin_list_label.setOpaque(true);
632 Dictionary.registerText(plugin_list_label, "CDM.PlugInManager.Assigned");
633
634 plugin_list_pane = new JPanel();
635 plugin_pane = new JPanel();
636
637 remove = new JButton();
638 remove.setEnabled(false);
639 remove.setMnemonic(KeyEvent.VK_R);
640 Dictionary.registerBoth(remove, "CDM.PlugInManager.Remove", "CDM.PlugInManager.Remove_Tooltip");
641
642 title = new JLabel();
643 title.setHorizontalAlignment(JLabel.CENTER);
644 title.setOpaque(true);
645 Dictionary.registerText(title, "CDM.PlugInManager.Title");
646
647 // Listeners
648 add.addActionListener(new AddListener());
649 configure.addActionListener(new ConfigureListener());
650 MoveListener ml = new MoveListener();
651 move_bottom_button.addActionListener(ml);
652 move_down_button.addActionListener(ml);
653 move_top_button.addActionListener(ml);
654 move_up_button.addActionListener(ml);
655 plugin.addItemListener(picl);
656 remove.addActionListener(new RemoveListener());
657 plugin_list.addMouseListener(new ClickListener());
658 plugin_list.addListSelectionListener(new ListListener());
659 picl = null;
660
661 // Layout
662 title.setBorder(BorderFactory.createEmptyBorder(0,0,2,0));
663
664 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5));
665
666 header_pane.setLayout(new BorderLayout());
667 header_pane.add(title, BorderLayout.NORTH);
668 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER);
669
670 plugin_list_label.setBorder(BorderFactory.createEmptyBorder(0,2,0,2));
671
672 movement_pane.setBorder(BorderFactory.createEmptyBorder(0,2,0,0));
673 movement_pane.setLayout(new GridLayout(4,1));
674 movement_pane.add(move_top_button);
675 movement_pane.add(move_up_button);
676 movement_pane.add(move_down_button);
677 movement_pane.add(move_bottom_button);
678
679 plugin_list_pane.setLayout(new BorderLayout());
680 plugin_list_pane.add(plugin_list_label, BorderLayout.NORTH);
681 plugin_list_pane.add(new JScrollPane(plugin_list), BorderLayout.CENTER);
682 plugin_list_pane.add(movement_pane, BorderLayout.EAST);
683
684 plugin_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
685
686 plugin_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
687 plugin_pane.setLayout(new GridLayout(1,2));
688 plugin_pane.add(plugin_label);
689 plugin_pane.add(plugin);
690
691 button_pane.setLayout(new GridLayout(1,3));
692 button_pane.add(add);
693 button_pane.add(configure);
694 button_pane.add(remove);
695
696 // Scope these mad bordering skillz.
697 // !! TO DO: Dictionary registration !!
698 JPanel temp = new JPanel(new BorderLayout());
699 temp.setBorder
700 (BorderFactory.createCompoundBorder
701 (BorderFactory.createEmptyBorder(5,0,5,0),
702 BorderFactory.createCompoundBorder
703 (BorderFactory.createTitledBorder(Dictionary.get("CDM.PlugInManager.Controls")),
704 BorderFactory.createEmptyBorder(2,2,2,2))));
705
706 temp.add(plugin_pane, BorderLayout.NORTH);
707 temp.add(button_pane, BorderLayout.SOUTH);
708
709 central_pane.setLayout(new BorderLayout());
710 central_pane.add(plugin_list_pane, BorderLayout.CENTER);
711 central_pane.add(temp, BorderLayout.SOUTH);
712
713 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
714 setLayout(new BorderLayout());
715 add(header_pane, BorderLayout.NORTH);
716 add(central_pane, BorderLayout.CENTER);
717 }
718
719 /** Method which acts like a destructor, tidying up references to persistant objects.
720 */
721 public void destroy() {
722 }
723
724 /** This method is overridden to ensure the instructions are scrolled to top, before the super classes updateUI() is called.
725 */
726 public void gainFocus() {
727 if(instructions != null) {
728 instructions.setCaretPosition(0);
729 }
730 super.updateUI();
731 }
732
733 public void loseFocus() {
734 }
735
736 /** This class listens for actions upon the add button in the controls, and if detected calls the <i>assignPlugIn()</i> method. */
737 private class AddListener
738 implements ActionListener {
739 /** 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.
740 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
741 */
742 public void actionPerformed(ActionEvent event) {
743 Object selected_object = plugin.getSelectedItem();
744 if(selected_object != null) {
745 // Retrieve the base plugin if any
746 PlugIn base_plugin = getBasePlugIn(selected_object.toString());
747
748 // Create a new element in the DOM
749 Element element = CollectionDesignManager.collect_config.document.createElement(CollectionConfiguration.PLUGIN_ELEMENT);
750 // Remember that the plugin supplied might be a custom string rather than a base plugin
751 PlugIn new_plugin = null;
752 if(base_plugin != null) {
753 //Gatherer.println("New PlugIn based on existing PlugIn");
754 element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, base_plugin.getName());
755 new_plugin = new PlugIn(element, base_plugin);
756 }
757 else {
758 //Gatherer.println("New Custom PlugIn");
759 element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, selected_object.toString());
760 new_plugin = new PlugIn(element, null);
761 }
762 if(!model.contains(new_plugin) || new_plugin.getName().equals(StaticStrings.UNKNOWNPLUG_STR)) {
763 // Automatically chain to configuration. This ensures required arguments are filled out.
764 ArgumentConfiguration ac = new ArgumentConfiguration(new_plugin);
765 if(ac.display()) {
766 assignPlugIn(new_plugin);
767 plugin_list.setSelectedValue(new_plugin, true);
768 // 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
769 if(base_plugin != null && !base_plugin.getName().equals(StaticStrings.UNKNOWNPLUG_STR)) {
770 plugin.removeItem(base_plugin);
771 }
772 }
773 ac = null;
774 new_plugin = null;
775 plugin.setSelectedIndex(0);
776 }
777 else {
778 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.PlugInManager.PlugIn_Exists"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
779 }
780 base_plugin = null;
781 }
782 }
783 }
784
785 /** Listens for double clicks apon the list and react as if the configure button was pushed. */
786 private class ClickListener
787 extends MouseAdapter {
788 /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt.
789 * @param event A <strong>MouseEvent</strong> containing information about the mouse click.
790 */
791 public void mouseClicked(MouseEvent event) {
792 if(event.getClickCount() == 2 ) {
793 if(!plugin_list.isSelectionEmpty()) {
794 PlugIn plugin = (PlugIn) plugin_list.getSelectedValue();
795 if(!plugin.isSeparator()) {
796 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
797 if(ac.display()) {
798 refresh(plugin);
799 }
800 ac.destroy();
801 ac = null;
802 }
803 }
804 }
805 }
806 }
807
808 /** 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.
809 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration
810 */
811 private class ConfigureListener
812 implements ActionListener {
813 /** 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.
814 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
815 */
816 public void actionPerformed(ActionEvent event) {
817 if(!plugin_list.isSelectionEmpty()) {
818 PlugIn plugin = (PlugIn) plugin_list.getSelectedValue();
819 if(!plugin.isSeparator()) {
820 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
821 if(ac.display()) {
822 refresh(plugin);
823 }
824 ac.destroy();
825 ac = null;
826 }
827 }
828 }
829 }
830
831 /** 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 */
832 private class ListListener
833 implements ListSelectionListener {
834
835 public void valueChanged(ListSelectionEvent e) {
836 if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one (the second one)
837 if (plugin_list.isSelectionEmpty()) {
838 move_top_button.setEnabled(false);
839 move_up_button.setEnabled(false);
840 move_down_button.setEnabled(false);
841 move_bottom_button.setEnabled(false);
842 configure.setEnabled(false);
843 remove.setEnabled(false);
844 }
845 else {
846 configure.setEnabled(true);
847 // Some buttons are only available for plugins other than ArcPlug and RecPlug
848 PlugIn selected_plugin = (PlugIn) plugin_list.getSelectedValue();
849 String plugin_name = selected_plugin.getName();
850 if(plugin_name.equals(StaticStrings.ARCPLUG_STR) || plugin_name.equals(StaticStrings.RECPLUG_STR)) {
851 move_top_button.setEnabled(false);
852 move_up_button.setEnabled(false);
853 move_down_button.setEnabled(false);
854 move_bottom_button.setEnabled(false);
855 remove.setEnabled(false);
856 }
857 else {
858 // Move ups are only enabled if the selected plugin isn't already at the top
859 PlugIn first_plugin = (PlugIn) getElementAt(0);
860 if(!first_plugin.equals(selected_plugin)) {
861 move_top_button.setEnabled(true);
862 move_up_button.setEnabled(true);
863 }
864 else {
865 move_top_button.setEnabled(false);
866 move_up_button.setEnabled(false);
867 }
868 // And move downs are only allowed when the selected plugin isn't at an index one less than the separator line.
869 int separator_index = findSeparatorIndex();
870 int selected_index = plugin_list.getSelectedIndex();
871 if(selected_index != separator_index - 1) {
872 move_down_button.setEnabled(true);
873 move_bottom_button.setEnabled(true);
874 }
875 else {
876 move_down_button.setEnabled(false);
877 move_bottom_button.setEnabled(false);
878 }
879 remove.setEnabled(true);
880 }
881 selected_plugin = null;
882 plugin_name = null;
883 }
884 }
885 }
886 }
887
888 /** A special list renderer which is able to render separating lines as well. */
889 private class ListRenderer
890 extends DefaultListCellRenderer {
891 /** 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.
892 * @param list - The <strong>JList</strong> we're painting.
893 * @param value - The value returned by list.getModel().getElementAt(index) as an <strong>Object</strong>.
894 * @param index - The cells index as an <i>int</i>.
895 * @param isSelected - <i>true</i> if the specified cell was selected.
896 * @param cellHasFocus - <i>true</i> if the specified cell has the focus.
897 * @return A <strong>Component</strong> whose paint() method will render the specified value.
898 * @see javax.swing.JList
899 * @see javax.swing.JSeparator
900 * @see javax.swing.ListModel
901 * @see javax.swing.ListSelectionModel
902 */
903 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
904 PlugIn plugin = (PlugIn) value;
905 if(plugin.isSeparator()) {
906 return separator;
907 }
908 else {
909 return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
910 }
911 }
912 }
913
914
915 /** 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. */
916 private class MoveListener
917 implements ActionListener {
918 /** 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.
919 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
920 */
921 public void actionPerformed(ActionEvent event) {
922 if (!plugin_list.isSelectionEmpty()) {
923 Object object = plugin_list.getSelectedValue();
924 if (object instanceof PlugIn) {
925 PlugIn plugin = (PlugIn) object;
926 if (event.getSource() == move_top_button) {
927 movePlugIn(plugin, true, true);
928 }
929 else if (event.getSource() == move_up_button) {
930 movePlugIn(plugin, true, false);
931 }
932 else if (event.getSource() == move_down_button) {
933 movePlugIn(plugin, false, false);
934 }
935 else if (event.getSource() == move_bottom_button) {
936 movePlugIn(plugin, false, true);
937 }
938 plugin_list.setSelectedValue(plugin, true);
939 }
940 }
941 }
942 }
943
944 /** This listener reacts to changes in the current selection of the plugin combobox. */
945 private class PlugInComboboxListener
946 implements ItemListener {
947 /** When a user selects a certain plugin, update the tooltip to show the plugin description. */
948 public void itemStateChanged(ItemEvent event) {
949 if(event.getStateChange() == ItemEvent.SELECTED) {
950 // Retrieve the selected plugin
951 Object current_selection = plugin.getSelectedItem();
952 // And reset the tooltip. If the plugin is null or is a string, then go back to the default message
953 if(current_selection == null || current_selection instanceof String) {
954 Dictionary.registerTooltip(plugin, "CDM.PlugInManager.PlugIn_Tooltip");
955 }
956 else {
957 PlugIn current_plugin = (PlugIn) current_selection;
958 Dictionary.registerTooltip(plugin, Utility.formatHTMLWidth(current_plugin.getDescription(), 40), false);
959 current_plugin = null;
960 }
961 current_selection = null;
962 }
963 }
964 }
965
966 /** This class listens for actions upon the remove button in the controls, and if detected calls the <i>removePlugIn()</i> method.
967 */
968 private class RemoveListener
969 implements ActionListener {
970 /** 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.
971 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
972 */
973 public void actionPerformed(ActionEvent event) {
974 if(!plugin_list.isSelectionEmpty()) {
975 Object [] objects = plugin_list.getSelectedValues();
976 for(int i = 0; i < objects.length; i++) {
977 if(objects[i] instanceof PlugIn) {
978 removePlugIn((PlugIn)objects[i]);
979 }
980 }
981 // Refresh the available plugins
982 plugin.setModel(new DefaultComboBoxModel(getAvailable()));
983 }
984 remove.setEnabled(false);
985 }
986 }
987 }
988 /** Creates a list separator.
989 * Code courtesy of Farwell, Paul. Contact <a href="mailto:[email protected]">[email protected]</a>
990 */
991 static private JPanel getSeparator() {
992 // We put the separator inside a panel to control its appearance
993 JPanel _sepPanel = new JPanel();
994 _sepPanel.setOpaque(false);
995 _sepPanel.setBorder(BorderFactory.createEmptyBorder(1, 3, 1, 3));
996 _sepPanel.setLayout(new BoxLayout(_sepPanel, BoxLayout.Y_AXIS));
997 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
998 _sepPanel.add(new JPopupMenu.Separator());
999 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
1000 return _sepPanel;
1001 }
1002}
Note: See TracBrowser for help on using the repository browser.