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

Last change on this file since 7224 was 7151, checked in by kjdon, 20 years ago

fixed some more static label sizes and deleted a lot of commented out stuff

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