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

Last change on this file since 7578 was 7270, checked in by kjdon, 20 years ago

undid most of what I previously commited. GAPlug is now treated like other plugins except that you can't delete it. there is no top separator.

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