source: trunk/gli/src/org/greenstone/gatherer/cdm/PluginManager.java@ 8853

Last change on this file since 8853 was 8853, checked in by mdewsnip, 19 years ago

Initial work on allowing metadata databases to be exploded from within the GLI. These are marked with a different icon in the collection tree and a new item is added to the right-click menu for these files. Clicking this will eventually run the explode_metadata_databases.pl script to explode these files.

  • Property svn:keywords set to Author Date Id Revision
File size: 52.0 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.net.*;
33import java.util.*;
34import javax.swing.*;
35import javax.swing.event.*;
36import javax.swing.plaf.*;
37import javax.swing.plaf.basic.*;
38import org.apache.xerces.parsers.*;
39import org.greenstone.gatherer.Configuration;
40import org.greenstone.gatherer.DebugStream;
41import org.greenstone.gatherer.Dictionary;
42import org.greenstone.gatherer.Gatherer;
43import org.greenstone.gatherer.collection.CollectionContentsChangedListener;
44import org.greenstone.gatherer.gui.GComboBox;
45import org.greenstone.gatherer.gui.GLIButton;
46import org.greenstone.gatherer.gui.ModalDialog;
47import org.greenstone.gatherer.gui.WarningDialog;
48import org.greenstone.gatherer.util.StaticStrings;
49import org.greenstone.gatherer.util.Utility;
50import org.greenstone.gatherer.util.XMLTools;
51import org.w3c.dom.*;
52import org.xml.sax.*;
53
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 implements CollectionContentsChangedListener
58{
59 /** The library 'reserve' of base plugins. */
60 private ArrayList library = null;
61 /** 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. */
62 private boolean modify_row_count = false;
63 /** The controls for editing the contents of this manager. */
64 private Control controls = null;
65 private DOMProxyListModel model;
66 private JPanel separator;
67 private Plugin separator_plugin;
68 /** Constructor.
69 */
70 public PluginManager() {
71 super(CollectionDesignManager.collect_config.getDocumentElement(), CollectionConfiguration.PLUGIN_ELEMENT, new Plugin());
72 DebugStream.println("PluginManager: " + super.getSize() + " plugins parsed.");
73 model = this;
74 // Reload/Create the library
75 loadPlugins(); // adds all the plugins to the library
76 savePlugins();
77 // Create the separator, cause we can reuse it.
78 separator = getSeparator();
79
80 // Listen for CollectionContentsChanged events, so we can give plugin hints when new files are added
81 Gatherer.c_man.addCollectionContentsChangedListener(this);
82 }
83
84 /** Method to add a new plugin to the library
85 * @param plugin the new base Plugin
86 */
87 private void addPlugin(Plugin plugin) {
88 if(!library.contains(plugin)) {
89 library.add(plugin);
90 }
91 }
92
93 /** Method to assign a plugin
94 * @param plugin the Plugin to assign
95 */
96 private void assignPlugin(Plugin plugin) {
97 if(plugin.getName().equals(StaticStrings.RECPLUG_STR) || plugin.getName().equals(StaticStrings.ARCPLUG_STR)) {
98 addAfter(plugin, separator_plugin); // Adds after separator
99 } else {
100 addBefore(plugin, separator_plugin);
101 }
102 Gatherer.c_man.configurationChanged();
103 }
104
105 public static boolean clearPluginCache() {
106
107 DebugStream.println("deleting plugins.dat");
108 File plugin_file = new File(Utility.getGLIUserFolder(), "plugins.dat");
109 if (plugin_file.exists()) {
110 return Utility.delete(plugin_file);
111 }
112 return true;
113 }
114
115
116 /** Destructor. */
117 public void destroy() {
118 if(controls != null) {
119 controls.destroy();
120 controls = null;
121 }
122 library.clear();
123 library = null;
124 }
125
126
127 /** This function listens for new files being added to the collection and hints about suitable plugins. */
128 public void fileAddedToCollection(File file)
129 {
130 System.err.println("New file added to collection: " + file);
131
132 // First check the plugins already assigned in the collection
133 for (int i = 0; i < super.getSize(); i++) {
134 Plugin assigned_plugin = (Plugin) getElementAt(i);
135 if (assigned_plugin.isSeparator() == false && assigned_plugin.doesProcessFile(file) == true) {
136 // This file will be processed by an assigned plugin, so no suggestion is necessary
137 System.err.println("Processed by assigned plugin: " + assigned_plugin);
138 // System.err.println("Explodes metadata databases: " + assigned_plugin.doesExplodeMetadataDatabases());
139 return;
140 }
141 }
142
143 // Next try the plugins NOT already assigned in the collection
144 ArrayList suitable_plugins = new ArrayList();
145 Object[] unassigned_plugins = getAvailable();
146 for (int i = 0; i < unassigned_plugins.length; i++) {
147 Plugin unassigned_plugin = (Plugin) unassigned_plugins[i];
148 if (unassigned_plugin.doesProcessFile(file) == true) {
149 System.err.println("Processed by unassigned plugin: " + unassigned_plugin);
150 suitable_plugins.add(unassigned_plugin);
151 }
152 }
153
154 // If there appear to be no suitable plugins, warn the user about this and be done
155 if (suitable_plugins.size() == 0) {
156 String[] args = new String[1];
157 args[0] = file.getName();
158 WarningDialog warning_dialog = new WarningDialog("warning.NoPluginExpectedToProcessFile", args, null, false);
159 warning_dialog.display();
160 warning_dialog.dispose();
161 return;
162 }
163
164 // Generate a dialog
165 new PluginSuggestionPrompt(file.getName(), suitable_plugins);
166 }
167
168
169 public boolean isFileExplodable(File file)
170 {
171 // Temporary hack to test the rest of this, the correct code is below
172 if (file.isFile() && file.getName().endsWith(".mst")) {
173 return true;
174 }
175
176// for (int i = 0; i < library.getSize(); i++) {
177// Plugin plugin = (Plugin) library.get(i);
178// if (plugin.doesProcessFile(file) == true && plugin.doesExplodeMetadataDatabases() == true) {
179// return true;
180// }
181// }
182
183 return false;
184 }
185
186
187 /** Method to retrieve the control for this manager.
188 * @return the Control
189 */
190 public Control getControls() {
191 if(controls == null) {
192 // Build controls
193 controls = new PluginControl();
194 }
195 return controls;
196 }
197
198 /** Retrieve the base pluging of the given name, or null if no such plugin.
199 * @param name the name of the base plugin to retrieve as a String
200 * @return the Plugin requested or null if no such plugin
201 */
202 public Plugin getBasePlugin(String name) {
203 int library_size = library.size();
204 for(int i = 0; i < library_size; i++) {
205 Plugin plugin = (Plugin) library.get(i);
206 if(plugin.getName().equals(name)) {
207 return plugin;
208 }
209 }
210 // No success.
211 return null;
212 }
213
214 /** Overrides getSize in DOMProxyListModel to take into account the row count modifier used to hide the last three rows in lower detail modes
215 * @return an int indicating the number of rows in the model, or more correctly the desired number of rows to display
216 */
217 public int getSize() {
218 int result = super.getSize();
219 if(modify_row_count) {
220 result = result-3;
221 }
222 return result;
223 }
224
225 /** Called when the detail mode has changed which in turn may cause several design elements to be available/hidden
226 * @param mode the new mode as an int
227 */
228 public void modeChanged(int mode) {
229 if(controls != null) {
230 ((PluginControl)controls).modeChanged(mode);
231 }
232 }
233
234 /** Method to move a plugin in the list order.
235 * @param plugin the Plugin you want to move.
236 * @param direction true to move the plugin up, false to move it down.
237 * @param all true to move to move all the way, false for a single step.
238 */
239 // why are all the error notices there when the buttons are disabled is you cant move???
240 private void movePlugin(Plugin plugin, boolean direction, boolean all) {
241 // Can't ever move RecPlug or ArcPlug
242 if(super.getSize() < 4) {
243 //DebugStream.println("Not enough plugins to allow moving.");
244 return;
245 }
246 if(plugin.getName().equals(StaticStrings.ARCPLUG_STR) || plugin.getName().equals(StaticStrings.RECPLUG_STR)) {
247 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.Fixed"), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
248 return;
249 }
250 if(all) {
251 // Move to top
252 if(direction) {
253 // Remove the moving plugin
254 remove(plugin);
255 // Retrieve the first plugin
256 Plugin first_plugin = (Plugin) getElementAt(0);
257 // Add the moving plugin before the first plugin
258 addBefore(plugin, first_plugin);
259 first_plugin = null;
260 Gatherer.c_man.configurationChanged();
261 }
262 else {
263 // Remove the moving plugin
264 remove(plugin);
265 // Add the moving plugin before the separator
266 addBefore(plugin, separator_plugin);
267 Gatherer.c_man.configurationChanged();
268 }
269 }
270 else {
271 // Try to move the plugin one step in the desired direction.
272 int index = indexOf(plugin);
273 ///ystem.err.println("Index of " + plugin + " = " + index);
274 if(direction) {
275 index--;
276 if(index < 0) {
277 String args[] = new String[2];
278 args[0] = Dictionary.get("CDM.PlugInManager.PlugIn_Str");
279 args[1] = plugin.getName();
280 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.At_Top", args), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
281 return;
282 }
283 remove(plugin);
284 add(index, plugin);
285 Gatherer.c_man.configurationChanged();
286 }
287 else {
288 index++;
289 Plugin next_plugin = (Plugin) getElementAt(index);
290 if(next_plugin.isSeparator()) {
291 String args[] = new String[1];
292 args[0] = plugin.getName();
293 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.Cannot", args), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
294 // Still not going to move RecPlug or ArcPlug.
295 return;
296 }
297 remove(plugin);
298 add(index, plugin);
299 Gatherer.c_man.configurationChanged();
300 }
301 }
302 }
303
304 /** 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.
305 */
306 public void placeSeparator() {
307 ///ystem.err.println("Placing separator.");
308 int separator_index = super.getSize();
309 if(separator_index > 0) {
310 boolean found_fixed = false;
311 int index = separator_index - 1;
312 while(index > 0) {
313 Plugin plugin = (Plugin) getElementAt(index);
314 String name = plugin.getName();
315 if(name.equals(StaticStrings.RECPLUG_STR) || name.equals(StaticStrings.ARCPLUG_STR)) {
316 found_fixed = true;
317 index--;
318 }
319 else {
320 if(found_fixed) {
321 separator_index = index + 1;
322 index = -1;
323 }
324 else {
325 index--;
326 }
327 }
328 name = null;
329 plugin = null;
330 }
331 }
332 Element element = CollectionDesignManager.collect_config.document.createElement(CollectionConfiguration.PLUGIN_ELEMENT);
333 element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, CollectionConfiguration.SEPARATOR_ATTRIBUTE);
334 element.setAttribute(CollectionConfiguration.SEPARATOR_ATTRIBUTE, CollectionConfiguration.TRUE_STR);
335 separator_plugin = new Plugin(element, null);
336 ///atherer.println("Adding plugin separator at: " + separator_index);
337 add(separator_index, separator_plugin);
338 }
339
340 /** 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.
341 * @param plugin The <strong>Plugin</strong> to remove.
342 */
343 private void removePlugin(Plugin plugin) {
344 remove(plugin);
345 Gatherer.c_man.configurationChanged();
346 }
347
348 /** Method to cache the current contents of library (known plugins) to file.
349 */
350 private void savePlugins() {
351 try {
352 File plugins_dat_file = new File(Utility.getGLIUserFolder(), "plugins.dat");
353 FileOutputStream file = new FileOutputStream(plugins_dat_file);
354 ObjectOutputStream out = new ObjectOutputStream(file);
355 out.writeObject(library);
356 out.close();
357 }
358 catch (Exception error) {
359 DebugStream.printStackTrace(error);
360 }
361 }
362
363 /** Inform the model to hide/show the last three lines on the list.
364 * @param modify_row_count true to hide the last three lines, false otherwise
365 */
366 private void setHideLines(boolean modify_row_count) {
367 this.modify_row_count = modify_row_count;
368 int original_size = super.getSize();
369 if(modify_row_count) {
370 fireIntervalRemoved(this, original_size - 4, original_size - 1);
371 }
372 else {
373 fireIntervalAdded(this, original_size - 4, original_size - 1);
374 }
375 }
376
377 /** Determine the current separator index. */
378 private int findSeparatorIndex() {
379 int separator_index = super.getSize() - 1;
380 while(separator_index >= 0) {
381 Plugin search = (Plugin) getElementAt(separator_index);
382 if(search.isSeparator()) {
383 return separator_index;
384 }
385 separator_index--;
386 }
387 return separator_index;
388 }
389
390
391 /** Retrieve a list of those plugins that are in library but not in the assigned plugins. */
392 private Object[] getAvailable() {
393 ArrayList available = new ArrayList();
394 int library_size = library.size();
395 for(int i = 0; i < library_size; i++) {
396 Plugin plugin = (Plugin) library.get(i);
397 if(!plugin.isAbstract()) {
398 available.add(plugin);
399 }
400 plugin = null;
401 }
402 // Now go through the assigned plugins, and remove any that match.
403 available.removeAll(children());
404 //DebugStream.println("There are a total of " + library.size() + " plugins in the library.");
405 //DebugStream.println("However " + children().size() + " are in use,");
406 //DebugStream.println("So only " + available.size() + " remain.");
407 Collections.sort(available);
408 return available.toArray();
409 }
410
411 /** Method to extract just the plugins name from a file object.
412 * @param plugin The <strong>File</strong> which references a certain plugin.
413 * @return A <strong>String</strong> containing just the plugins name, without extension.
414 */
415 private String getPluginName(String filename) {
416 String name = filename;
417 if(name.indexOf(".") != -1) {
418 name = name.substring(0, name.indexOf("."));
419 }
420 return name;
421 }
422
423
424 /** Method to load the details of a single plug-in.
425 * @param plugin The plugin <strong>File</strong> you wish to load.
426 */
427 private void loadPlugin(String plugin, String lang) {
428 Document document = null;
429 InputStream input_stream = null;
430
431 // Run pluginfo on this plugin, and then send the results for parsing.
432 try {
433 if (Gatherer.isGsdlRemote) {
434 String launch = Gatherer.cgiBase + "launch";
435 launch += "?cmd=pluginfo.pl";
436 launch += "&xml=&language="+lang;
437 launch += "&plug=" + getPluginName(plugin);
438
439 System.err.println("*** launch = " + launch);
440
441 URL launch_url = new URL(launch);
442 URLConnection launch_connection = launch_url.openConnection();
443 input_stream = launch_connection.getInputStream();
444 }
445 else {
446 String args[] = null;
447 if(Utility.isWindows()) {
448 args = new String[6];
449 if(Configuration.perl_path != null) {
450 args[0] = Configuration.perl_path;
451 }
452 else {
453 args[0] = "Perl.exe";
454 }
455 args[1] = Configuration.gsdl_path + "bin" + File.separator + "script" + File.separator + "pluginfo.pl";
456 args[2] = "-xml";
457 args[3] = "-language";
458 args[4] = lang;
459 args[5] = getPluginName(plugin);
460 }
461 else {
462 args = new String[5];
463 args[0] = "pluginfo.pl";
464 args[1] = "-xml";
465 args[2] = "-language";
466 args[3] = lang;
467 args[4] = getPluginName(plugin);
468 }
469
470 // Create the process.
471 Runtime runtime = Runtime.getRuntime();
472 Process process = runtime.exec(args);
473 input_stream = process.getErrorStream();
474 }
475
476 StringBuffer xml = Utility.readXMLStream(input_stream);
477 document = CollectionDesignManager.XMLStringToDOM(xml,plugin);
478 }
479 catch (Exception error) {
480 System.err.println("Failed when trying to parse: " + plugin);
481 error.printStackTrace();
482 }
483 if(document != null) {
484 parseXML(document.getDocumentElement());
485 }
486 }
487
488 /** Method to initially load information from the standard plug-ins within the gsdl Perl library.
489 */
490 private void loadPlugins() {
491 // Attempt to restore the cached file.
492 File plugins_dat_file = new File(Utility.getGLIUserFolder(), "plugins.dat");
493 try {
494 FileInputStream file = new FileInputStream(plugins_dat_file);
495 ObjectInputStream input = new ObjectInputStream(file);
496 library = (ArrayList) input.readObject();
497 }
498 catch (Exception error) {
499 DebugStream.println("Unable to open " + plugins_dat_file);
500 }
501
502 if(library == null) {
503 library = new ArrayList();
504 if (Gatherer.isGsdlRemote) {
505
506 String lang = Configuration.getLanguage();
507
508 String launch = Gatherer.cgiBase + "launch";
509 launch += "?cmd=pluginfo.pl";
510 launch += "&xml=&language="+lang;
511 launch += "&listall=";
512
513 System.err.println("*** launch = " + launch);
514
515 try {
516 URL launch_url = new URL(launch);
517 URLConnection launch_connection = launch_url.openConnection();
518 InputStream input_stream = launch_connection.getInputStream();
519 loadPlugins(input_stream);
520 }
521 catch (Exception error) {
522 System.err.println("Failed when trying to connect to : " + launch);
523 error.printStackTrace();
524 }
525
526 }
527 else {
528 // Retrieve the gsdl home directory...
529 String directory = Configuration.gsdl_path;
530 directory = directory + "perllib" + File.separator + "plugins" + File.separator;
531 loadPlugins(new File(directory));
532 }
533 }
534 }
535
536
537
538
539 /** Method to load plug-in information from a specified input stream (could be local or through URL). Of course no plug-ins may be found at this location.
540 * @param input_stream An <strong>InputStream</strong> indicating the where list of plugins -- encoded in XML -- can be read from
541 */
542 private void loadPlugins(InputStream input_stream)
543 {
544 StringBuffer xml = Utility.readXMLStream(input_stream);
545 Document document = CollectionDesignManager.XMLStringToDOM(xml, "-listall");
546
547 // Parse XML to build up list of plugin names
548 Node root = document.getDocumentElement();
549
550 NamedNodeMap attributes = root.getAttributes();
551 Node length_node = attributes.getNamedItem("length");
552 String num_plugins_str = length_node.getNodeValue();
553 int num_plugins = Integer.parseInt(num_plugins_str);
554 String plugin_list[] = new String[num_plugins];
555
556 Node node = root.getFirstChild();
557 int i = 0;
558 while (node != null) {
559 String node_name = node.getNodeName();
560 if (node_name.equalsIgnoreCase("PluginName")) {
561 String name = XMLTools.getValue(node);
562 plugin_list[i] = name;
563 i++;
564 }
565
566 node = node.getNextSibling();
567 }
568
569
570 boolean is_windows = Utility.isWindows();
571 boolean is_mac = Utility.isMac();
572
573 String current_lang = Configuration.getLanguage();
574 if (num_plugins>0) {
575 // Create a progress indicator.
576 ParsingProgress progress = new ParsingProgress(Dictionary.get("CDM.PlugInManager.Parsing.Title"), Dictionary.get("CDM.PlugInManager.Parsing.Message"), num_plugins);
577
578 for (i=0; i<num_plugins; i++) {
579 String plugin = plugin_list[i];
580 if (plugin.equals("GMLPlug.pm") || ((is_windows || is_mac) && plugin.equals("DBPlug.pm"))) {
581 // don't load GMLPlug or DBPlug for windows
582 } else {
583 loadPlugin(plugin, current_lang);
584 }
585
586 progress.inc();
587 }
588 progress.dispose();
589 progress.destroy();
590 progress = null;
591 }
592 }
593
594
595 /** Method to load plug-in information from a specified directory. Of course no plug-ins may be found at this location.
596 * @param directory A <strong>File</strong> indicating the directory to be scanned for plug-ins.
597 */
598 private void loadPlugins(File directory) {
599 File files[] = directory.listFiles();
600 boolean is_windows = Utility.isWindows();
601 boolean is_mac = Utility.isMac();
602 String current_lang = Configuration.getLanguage();
603 if(files != null) {
604 // Create a progress indicator.
605 ParsingProgress progress = new ParsingProgress(Dictionary.get("CDM.PlugInManager.Parsing.Title"), Dictionary.get("CDM.PlugInManager.Parsing.Message"), files.length);
606 for(int i = 0; i < files.length; i++) {
607 // We only want to check Perl Modules.
608 if(files[i].getName().endsWith(".pm")) {
609 if (files[i].getName().equals("GMLPlug.pm") || ((is_windows || is_mac) && files[i].getName().equals("DBPlug.pm"))) {
610 // don't load GMLPlug or DBPlug for windows
611 } else {
612 loadPlugin(files[i].getName(), current_lang);
613 }
614 }
615 progress.inc();
616 }
617 progress.dispose();
618 progress.destroy();
619 progress = null;
620 }
621 }
622
623 private Plugin parseXML(Node root) {
624 Plugin plugin = new Plugin();
625 String node_name = null;
626 for (Node node = root.getFirstChild(); node != null; node = node.getNextSibling()) {
627 node_name = node.getNodeName();
628 if(node_name.equalsIgnoreCase("Name")) {
629 String name = XMLTools.getValue(node);
630 // 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.
631 Plugin existing = getBasePlugin(name);
632 if(existing != null) {
633 return existing;
634 }
635 plugin.setName(name);
636 }
637 else if (node_name.equalsIgnoreCase("Desc")) {
638 plugin.setDescription(XMLTools.getValue(node));
639 }
640 else if (node_name.equalsIgnoreCase("Abstract")) {
641 plugin.setIsAbstract(XMLTools.getValue(node).equalsIgnoreCase(StaticStrings.YES_STR));
642 }
643 else if (node_name.equalsIgnoreCase("Explodes")) {
644 plugin.setDoesExplodeMetadataDatabases(XMLTools.getValue(node).equalsIgnoreCase(StaticStrings.YES_STR));
645 System.err.println("Plugin " + plugin.getName() + " explodes metadata databases: " + plugin.doesExplodeMetadataDatabases());
646 }
647 // Parse the multitude of arguments
648 else if(node_name.equalsIgnoreCase("Arguments")) {
649 for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) {
650 node_name = arg.getNodeName();
651 // An option.
652 if(node_name.equalsIgnoreCase("Option")) {
653 Argument argument = new Argument();
654 // If its an option we parse the multitude of details an options might have.
655 for(Node det = arg.getFirstChild(); det != null; det = det.getNextSibling()) {
656 node_name = det.getNodeName();
657 if(node_name.equalsIgnoreCase("Name")) {
658 argument.setName(XMLTools.getValue(det));
659 }
660 else if(node_name.equalsIgnoreCase("Desc")) {
661 argument.setDescription(XMLTools.getValue(det));
662 }
663 else if(node_name.equalsIgnoreCase("Type")) {
664 argument.setType(XMLTools.getValue(det));
665 }
666 else if(node_name.equalsIgnoreCase("Default")) {
667 argument.setDefaultValue(XMLTools.getValue(det));
668 }
669 else if(node_name.equalsIgnoreCase("List")) {
670 // Two final loops are required to parse lists.
671 for(Node value = det.getFirstChild(); value != null; value = value.getNextSibling()) {
672 if(value.getNodeName().equalsIgnoreCase("Value")) {
673 String key = null;
674 String desc = "";
675 for(Node subvalue = value.getFirstChild(); subvalue != null; subvalue = subvalue.getNextSibling()) {
676 node_name = subvalue.getNodeName();
677 if(node_name.equalsIgnoreCase("Name")) {
678 key = XMLTools.getValue(subvalue);
679 }
680 else if(node_name.equalsIgnoreCase("Desc")) {
681 desc = XMLTools.getValue(subvalue);
682 }
683 }
684 if(key != null) {
685 argument.addOption(key, desc);
686 }
687 }
688 }
689 }
690 else if(node_name.equalsIgnoreCase("Required")) {
691 String v = XMLTools.getValue(det);
692 if(v != null && v.equalsIgnoreCase("yes")) {
693 argument.setRequired(true);
694 }
695 }
696 else if(node_name.equals(StaticStrings.RANGE_ELEMENT)) {
697 String range_raw = XMLTools.getValue(det);
698 int index = -1;
699 if((index = range_raw.indexOf(StaticStrings.COMMA_CHARACTER)) != -1) {
700if(index > 0) {
701 try {
702 String first_number = range_raw.substring(0, index);
703 argument.setMinimum(Integer.parseInt(first_number));
704 first_number = null;
705 }
706 catch(Exception exception) {
707 }
708 }
709
710 if(index + 1 < range_raw.length()) {
711 try {
712 String second_number = range_raw.substring(index + 1);
713 argument.setMaximum(Integer.parseInt(second_number));
714 second_number = null;
715 }
716 catch(Exception exception) {
717 }
718 }
719 }
720 // Else it wasn't a valid range anyway, so ignore it
721 }
722 }
723 plugin.addArgument(argument);
724 }
725 // A super plugin class.
726 else if(node_name.equalsIgnoreCase("PlugInfo")) {
727 Plugin super_plugin = parseXML(arg);
728 plugin.setSuper(super_plugin);
729 }
730 }
731 }
732 }
733 if(plugin.getName() != null) {
734 addPlugin(plugin);
735 return plugin;
736 }
737 return null;
738 }
739
740 /** A class which provodes controls for assigned and editing plugins. */
741 private class PluginControl
742 extends JPanel
743 implements Control {
744 /** Button for adding plugins. */
745 private JButton add = null;
746 /** Button for configuring the selected plugin. */
747 private JButton configure = null;
748 /** Buttom to move an assinged plugin as low in the order as possible. */
749 //private JButton move_bottom_button = null;
750 /** Button to move an assigned plugin one position lower in the order. */
751 private JButton move_down_button = null;
752 /** Button to move an assigned plugin as high in the order as possible. */
753 //private JButton move_top_button = null;
754 /** Button to move an assigned plugin one position higher in the order. */
755 private JButton move_up_button = null;
756 /** Button to remove the selected plugin. */
757 private JButton remove = null;
758 /** A combobox containing all of the known plugins, including those that may have already been assigned. */
759 private GComboBox plugin = null;
760 /** The label next to the plugin combobox. */
761 private JLabel plugin_label = null;
762 /** The label above the assigned plugin list. */
763 private JLabel plugin_list_label = null;
764 /** The title of this view. */
765 private JLabel title = null;
766 /** A list of assigned plugins. */
767 private JList plugin_list = null;
768 /** The area where the add, configure and remove buttons are placed. */
769 private JPanel button_pane = null;
770 /** The region which divides the central portion of the view into list and controls */
771 private JPanel central_pane = null;
772 /** The area where title label and instructions sit. */
773 private JPanel header_pane = null;
774 /** The area where movement buttons are placed. */
775 private JPanel movement_pane = null;
776 /** The small region containing the plugin combobox and its label. */
777 private JPanel plugin_pane = null;
778 /** The pane containing the assigned plugin list and its label. */
779 private JPanel plugin_list_pane = null;
780 /** The text area containing instructions on the use of this control. */
781 private JTextArea instructions = null;
782
783 /** Constructor.
784 */
785 public PluginControl() {
786 // Create
787 add = new GLIButton();
788 add.setMnemonic(KeyEvent.VK_A);
789 Dictionary.registerBoth(add, "CDM.PlugInManager.Add", "CDM.PlugInManager.Add_Tooltip");
790
791 button_pane = new JPanel();
792 central_pane = new JPanel();
793
794 configure = new GLIButton();
795 configure.setEnabled(false);
796 configure.setMnemonic(KeyEvent.VK_C);
797 Dictionary.registerBoth(configure, "CDM.PlugInManager.Configure", "CDM.PlugInManager.Configure_Tooltip");
798
799 header_pane = new JPanel();
800
801 instructions = new JTextArea();
802 instructions.setBackground(Configuration.getColor("coloring.collection_tree_background", false));
803 instructions.setEditable(false);
804 instructions.setLineWrap(true);
805 instructions.setRows(6);
806 instructions.setWrapStyleWord(true);
807 Dictionary.registerText(instructions, "CDM.PlugInManager.Instructions");
808
809 move_up_button = new JButton("", Utility.getImage("arrow-up.gif"));
810 move_up_button.setEnabled(false);
811 move_up_button.setMnemonic(KeyEvent.VK_U);
812 //move_up_button.setPreferredSize(Utility.DOUBLE_IMAGE_BUTTON_SIZE);
813 Dictionary.registerBoth(move_up_button, "CDM.Move.Move_Up", "CDM.Move.Move_Up_Tooltip");
814
815 move_down_button = new JButton("", Utility.getImage("arrow-down.gif"));
816 move_down_button.setEnabled(false);
817 move_down_button.setMnemonic(KeyEvent.VK_D);
818 //move_down_button.setPreferredSize(Utility.DOUBLE_IMAGE_BUTTON_SIZE);
819 Dictionary.registerBoth(move_down_button, "CDM.Move.Move_Down", "CDM.Move.Move_Down_Tooltip");
820
821 movement_pane = new JPanel();
822
823 PluginComboboxListener picl = new PluginComboboxListener();
824 plugin = new GComboBox(getAvailable());
825 plugin.setBackgroundNonSelectionColor(Configuration.getColor("coloring.editable_background", false));
826 plugin.setBackgroundSelectionColor(Configuration.getColor("coloring.collection_selection_background", false));
827 plugin.setEditable(true);
828 plugin.setTextNonSelectionColor(Configuration.getColor("coloring.workspace_tree_foreground", false));
829 plugin.setTextSelectionColor(Configuration.getColor("coloring.collection_selection_foreground", false));
830 picl.itemStateChanged(new ItemEvent(plugin, 0, null, ItemEvent.SELECTED));
831
832 plugin_label = new JLabel();
833 Dictionary.registerText(plugin_label, "CDM.PlugInManager.PlugIn");
834
835 plugin_list = new JList(model);
836 plugin_list.setCellRenderer(new ListRenderer());
837 plugin_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
838 plugin_list_label = new JLabel();
839 plugin_list_label.setHorizontalAlignment(JLabel.CENTER);
840 plugin_list_label.setOpaque(true);
841 Dictionary.registerText(plugin_list_label, "CDM.PlugInManager.Assigned");
842
843 plugin_list_pane = new JPanel();
844 plugin_pane = new JPanel();
845
846 remove = new GLIButton();
847 remove.setEnabled(false);
848 remove.setMnemonic(KeyEvent.VK_R);
849 Dictionary.registerBoth(remove, "CDM.PlugInManager.Remove", "CDM.PlugInManager.Remove_Tooltip");
850
851 title = new JLabel();
852 title.setHorizontalAlignment(JLabel.CENTER);
853 title.setOpaque(true);
854 Dictionary.registerText(title, "CDM.PlugInManager.Title");
855
856 // Listeners
857 add.addActionListener(new AddListener());
858 configure.addActionListener(new ConfigureListener());
859 MoveListener ml = new MoveListener();
860 //move_bottom_button.addActionListener(ml);
861 move_down_button.addActionListener(ml);
862 //move_top_button.addActionListener(ml);
863 move_up_button.addActionListener(ml);
864 plugin.addItemListener(picl);
865 remove.addActionListener(new RemoveListener());
866 plugin_list.addMouseListener(new ClickListener());
867 plugin_list.addListSelectionListener(new ListListener());
868 picl = null;
869
870 // Layout
871 title.setBorder(BorderFactory.createEmptyBorder(0,0,2,0));
872
873 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5));
874
875 header_pane.setLayout(new BorderLayout());
876 header_pane.add(title, BorderLayout.NORTH);
877 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER);
878
879 plugin_list_label.setBorder(BorderFactory.createEmptyBorder(0,2,0,2));
880
881 movement_pane.setBorder(BorderFactory.createEmptyBorder(0,2,0,0));
882 movement_pane.setLayout(new GridLayout(4,1));
883 movement_pane.add(move_up_button);
884 movement_pane.add(new JPanel());
885 movement_pane.add(new JPanel());
886 movement_pane.add(move_down_button);
887
888 plugin_list_pane.setLayout(new BorderLayout());
889 plugin_list_pane.add(plugin_list_label, BorderLayout.NORTH);
890 plugin_list_pane.add(new JScrollPane(plugin_list), BorderLayout.CENTER);
891 modeChanged(Configuration.getMode()); // Whether the movement buttons are visible is mode dependant
892
893 plugin_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
894
895 plugin_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
896 plugin_pane.setLayout(new BorderLayout(5,0));
897 plugin_pane.add(plugin_label, BorderLayout.WEST);
898 plugin_pane.add(plugin, BorderLayout.CENTER);
899
900 button_pane.setLayout(new GridLayout(1,3));
901 button_pane.add(add);
902 button_pane.add(configure);
903 button_pane.add(remove);
904
905 // Scope these mad bordering skillz.
906 // !! TO DO: Dictionary registration !!
907 JPanel temp = new JPanel(new BorderLayout());
908 temp.setBorder
909 (BorderFactory.createCompoundBorder
910 (BorderFactory.createEmptyBorder(5,0,5,0),
911 BorderFactory.createCompoundBorder
912 (BorderFactory.createTitledBorder(Dictionary.get("CDM.PlugInManager.Controls")),
913 BorderFactory.createEmptyBorder(2,2,2,2))));
914
915 temp.add(plugin_pane, BorderLayout.NORTH);
916 temp.add(button_pane, BorderLayout.SOUTH);
917
918 central_pane.setLayout(new BorderLayout());
919 central_pane.add(plugin_list_pane, BorderLayout.CENTER);
920 central_pane.add(temp, BorderLayout.SOUTH);
921
922 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
923 setLayout(new BorderLayout());
924 add(header_pane, BorderLayout.NORTH);
925 add(central_pane, BorderLayout.CENTER);
926 }
927
928 /** Method which acts like a destructor, tidying up references to persistant objects.
929 */
930 public void destroy() {
931 }
932
933 /** This method is overridden to ensure the instructions are scrolled to top, before the super classes updateUI() is called.
934 */
935 public void gainFocus() {
936 if(instructions != null) {
937 instructions.setCaretPosition(0);
938 }
939 super.updateUI();
940 }
941
942 public void loseFocus() {
943 }
944
945 /** 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
946 * @param mode the current mode as an int, which can be matched against static ints in the Configuration class
947 */
948 public void modeChanged(int mode) {
949 // 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
950 plugin_list.clearSelection();
951 // The first change is dependant on whether the user is systems mode or higher
952 if(mode >= Configuration.SYSTEMS_MODE) {
953 // Show movement buttons
954 plugin_list_pane.add(movement_pane, BorderLayout.EAST);
955 // Do we show Arc and RecPlugs or hide them and the separator line
956 setHideLines(!(mode >= Configuration.EXPERT_MODE));
957 }
958 // Otherwise hide the movement buttons and fixed plugins
959 else {
960 plugin_list_pane.remove(movement_pane);
961 setHideLines(true);
962 }
963 plugin_list_pane.updateUI();
964 }
965
966 /** This class listens for actions upon the add button in the controls, and if detected calls the <i>assignPlugin()</i> method. */
967 private class AddListener
968 implements ActionListener {
969 /** Any implementation of <i>ActionListener</i> must include this method so that we can be informed when an action has occured on one of our target controls.
970 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
971 */
972 public void actionPerformed(ActionEvent event) {
973 Object selected_object = plugin.getSelectedItem();
974 if(selected_object != null) {
975 // Retrieve the base plugin if any
976 Plugin base_plugin = getBasePlugin(selected_object.toString());
977
978 // Create a new element in the DOM
979 Element element = CollectionDesignManager.collect_config.document.createElement(CollectionConfiguration.PLUGIN_ELEMENT);
980 // Remember that the plugin supplied might be a custom string rather than a base plugin
981 Plugin new_plugin = null;
982 if(base_plugin != null) {
983 //DebugStream.println("New Plugin based on existing Plugin");
984 element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, base_plugin.getName());
985 new_plugin = new Plugin(element, base_plugin);
986 }
987 else {
988 //DebugStream.println("New Custom Plugin");
989 element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, selected_object.toString());
990 new_plugin = new Plugin(element, null);
991 }
992 if(!model.contains(new_plugin) || new_plugin.getName().equals(StaticStrings.UNKNOWNPLUG_STR)) {
993 // Automatically chain to configuration. This ensures required arguments are filled out.
994 ArgumentConfiguration ac = new ArgumentConfiguration(new_plugin);
995 if(ac.display()) {
996 assignPlugin(new_plugin);
997 plugin_list.setSelectedValue(new_plugin, true);
998 // 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
999 if(base_plugin != null && !base_plugin.getName().equals(StaticStrings.UNKNOWNPLUG_STR)) {
1000 plugin.removeItem(base_plugin);
1001 }
1002 }
1003 ac = null;
1004 new_plugin = null;
1005 plugin.setSelectedIndex(0);
1006 }
1007 else {
1008 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.PlugInManager.PlugIn_Exists"), Dictionary.get("General.Error"), JOptionPane.ERROR_MESSAGE);
1009 }
1010 base_plugin = null;
1011 }
1012 }
1013 }
1014
1015 /** Listens for double clicks apon the list and react as if the configure button was pushed. */
1016 private class ClickListener
1017 extends MouseAdapter {
1018 /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt.
1019 * @param event A <strong>MouseEvent</strong> containing information about the mouse click.
1020 */
1021 public void mouseClicked(MouseEvent event) {
1022 if(event.getClickCount() == 2 ) {
1023 if(!plugin_list.isSelectionEmpty()) {
1024 Plugin plugin = (Plugin) plugin_list.getSelectedValue();
1025 if(!plugin.isSeparator()) {
1026 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
1027 if(ac.display()) {
1028 refresh(plugin);
1029 }
1030 ac.destroy();
1031 ac = null;
1032 }
1033 }
1034 }
1035 }
1036 }
1037
1038 /** 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.
1039 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration
1040 */
1041 private class ConfigureListener
1042 implements ActionListener {
1043 /** 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.
1044 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
1045 */
1046 public void actionPerformed(ActionEvent event) {
1047 if(!plugin_list.isSelectionEmpty()) {
1048 Plugin plugin = (Plugin) plugin_list.getSelectedValue();
1049 if(!plugin.isSeparator()) {
1050 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
1051 if(ac.display()) {
1052 refresh(plugin);
1053 }
1054 ac.destroy();
1055 ac = null;
1056 }
1057 }
1058 }
1059 }
1060
1061 /** 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 */
1062 private class ListListener
1063 implements ListSelectionListener {
1064
1065 public void valueChanged(ListSelectionEvent e) {
1066 if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one (the second one)
1067 if (plugin_list.isSelectionEmpty()) {
1068 //move_top_button.setEnabled(false);
1069 move_up_button.setEnabled(false);
1070 move_down_button.setEnabled(false);
1071 //move_bottom_button.setEnabled(false);
1072 configure.setEnabled(false);
1073 remove.setEnabled(false);
1074 }
1075 else {
1076 Plugin selected_plugin = (Plugin) plugin_list.getSelectedValue();
1077 if(selected_plugin.isSeparator()) {
1078 //move_top_button.setEnabled(false);
1079 move_up_button.setEnabled(false);
1080 move_down_button.setEnabled(false);
1081 //move_bottom_button.setEnabled(false);
1082 configure.setEnabled(false);
1083 remove.setEnabled(false);
1084 }
1085 else {
1086 configure.setEnabled(true);
1087 String plugin_name = selected_plugin.getName();
1088 // Some buttons are only available for plugins other than ArcPlug and RecPlug
1089 if(plugin_name.equals(StaticStrings.ARCPLUG_STR) || plugin_name.equals(StaticStrings.RECPLUG_STR) ) {
1090 //move_top_button.setEnabled(false);
1091 move_up_button.setEnabled(false);
1092 move_down_button.setEnabled(false);
1093 //move_bottom_button.setEnabled(false);
1094 remove.setEnabled(false);
1095 }
1096 else {
1097 // don't let people remove GAPlug
1098 if (plugin_name.equals(StaticStrings.GAPLUG_STR)) {
1099 remove.setEnabled(false);
1100 } else {
1101 remove.setEnabled(true);
1102 }
1103
1104 // Move ups are only enabled if the selected plugin isn't already at the top
1105 Plugin first_plugin = (Plugin) getElementAt(0);
1106 if(!first_plugin.equals(selected_plugin)) {
1107 //move_top_button.setEnabled(true);
1108 move_up_button.setEnabled(true);
1109 }
1110 else {
1111 //move_top_button.setEnabled(false);
1112 move_up_button.setEnabled(false);
1113 }
1114 // And move downs are only allowed when the selected plugin isn't at an index one less than the separator line.
1115 int separator_index = findSeparatorIndex();
1116 int selected_index = plugin_list.getSelectedIndex();
1117 if(selected_index < separator_index - 1) {
1118 move_down_button.setEnabled(true);
1119 //move_bottom_button.setEnabled(true);
1120 }
1121 else {
1122 move_down_button.setEnabled(false);
1123 //move_bottom_button.setEnabled(false);
1124 }
1125 }
1126 selected_plugin = null;
1127 plugin_name = null;
1128 }
1129 }
1130 }
1131 }
1132 }
1133
1134 /** A special list renderer which is able to render separating lines as well. */
1135 private class ListRenderer
1136 extends DefaultListCellRenderer {
1137 /** 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.
1138 * @param list - The <strong>JList</strong> we're painting.
1139 * @param value - The value returned by list.getModel().getElementAt(index) as an <strong>Object</strong>.
1140 * @param index - The cells index as an <i>int</i>.
1141 * @param isSelected - <i>true</i> if the specified cell was selected.
1142 * @param cellHasFocus - <i>true</i> if the specified cell has the focus.
1143 * @return A <strong>Component</strong> whose paint() method will render the specified value.
1144 * @see javax.swing.JList
1145 * @see javax.swing.JSeparator
1146 * @see javax.swing.ListModel
1147 * @see javax.swing.ListSelectionModel
1148 */
1149 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
1150 Plugin plugin = (Plugin) value;
1151 if(plugin.isSeparator()) {
1152 return separator;
1153 }
1154 else {
1155 return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
1156 }
1157 }
1158 }
1159
1160
1161 /** 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. */
1162 private class MoveListener
1163 implements ActionListener {
1164 /** 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.
1165 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
1166 */
1167 public void actionPerformed(ActionEvent event) {
1168 if (!plugin_list.isSelectionEmpty()) {
1169 Object object = plugin_list.getSelectedValue();
1170 if (object instanceof Plugin) {
1171 Plugin plugin = (Plugin) object;
1172 //if (event.getSource() == move_top_button) {
1173 // movePlugin(plugin, true, true);
1174 //}
1175 //else
1176 if (event.getSource() == move_up_button) {
1177 movePlugin(plugin, true, false);
1178 }
1179 else if (event.getSource() == move_down_button) {
1180 movePlugin(plugin, false, false);
1181 }
1182 //else if (event.getSource() == move_bottom_button) {
1183 // movePlugin(plugin, false, true);
1184 //}
1185 plugin_list.setSelectedValue(plugin, true);
1186 }
1187 }
1188 }
1189 }
1190
1191 /** This listener reacts to changes in the current selection of the plugin combobox. */
1192 private class PluginComboboxListener
1193 implements ItemListener {
1194 /** When a user selects a certain plugin, update the tooltip to show the plugin description. */
1195 public void itemStateChanged(ItemEvent event) {
1196 if(event.getStateChange() == ItemEvent.SELECTED) {
1197 // Retrieve the selected plugin
1198 Object current_selection = plugin.getSelectedItem();
1199 // And reset the tooltip. If the plugin is null or is a string, then go back to the default message
1200 if(current_selection == null || current_selection instanceof String) {
1201 Dictionary.registerTooltip(plugin, "CDM.PlugInManager.PlugIn_Tooltip");
1202 }
1203 else {
1204 Plugin current_plugin = (Plugin) current_selection;
1205 Dictionary.registerTooltipText(plugin, Utility.formatHTMLWidth(current_plugin.getDescription(), 40));
1206 current_plugin = null;
1207 }
1208 current_selection = null;
1209 }
1210 }
1211 }
1212
1213 /** This class listens for actions upon the remove button in the controls, and if detected calls the <i>removePlugin()</i> method.
1214 */
1215 private class RemoveListener
1216 implements ActionListener {
1217 /** 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.
1218 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
1219 */
1220 public void actionPerformed(ActionEvent event) {
1221 int selected_index = plugin_list.getSelectedIndex();
1222 if(selected_index != -1) {
1223 Plugin selected_plugin = (Plugin) plugin_list.getSelectedValue();
1224 removePlugin(selected_plugin);
1225 selected_plugin = null;
1226 // Select the next plugin if available
1227 if(selected_index < plugin_list.getModel().getSize()) {
1228 // If the new selection is above the separator we can remove it
1229 if(selected_index < findSeparatorIndex()) {
1230 plugin_list.setSelectedIndex(selected_index);
1231 // don't allow removal of GAPlug
1232 if (((Plugin)plugin_list.getSelectedValue()).getName().equals(StaticStrings.GAPLUG_STR)) {
1233 remove.setEnabled(false);
1234 } else {
1235 remove.setEnabled(true);
1236 }
1237 }
1238 // Otherwise select the first non-removable plugin
1239 else {
1240 plugin_list.setSelectedIndex(selected_index + 1);
1241 remove.setEnabled(false);
1242 }
1243 }
1244 else {
1245 remove.setEnabled(false);
1246 }
1247 // Refresh the available plugins
1248 plugin.setModel(new DefaultComboBoxModel(getAvailable()));
1249 }
1250 else {
1251 remove.setEnabled(false);
1252 }
1253 }
1254 }
1255 }
1256
1257
1258 private class PluginSuggestionPrompt
1259 extends ModalDialog
1260 implements ActionListener
1261 {
1262 private Dimension size = new Dimension(400, 200);
1263 private GComboBox suitable_plugins_combobox = null;
1264 private GLIButton add_button = null;
1265 private GLIButton ignore_button = null;
1266
1267 public PluginSuggestionPrompt(String filename, ArrayList suitable_plugins)
1268 {
1269 super(Gatherer.g_man, true);
1270 setModal(true);
1271 setSize(size);
1272 Dictionary.setText(this, "CDM.PluginManager.SuggestedPluginListTitle");
1273
1274 String[] args = new String[1];
1275 args[0] = filename;
1276
1277 JTextArea instructions_textarea = new JTextArea();
1278 instructions_textarea.setCaretPosition(0);
1279 instructions_textarea.setEditable(false);
1280 instructions_textarea.setLineWrap(true);
1281 instructions_textarea.setRows(5);
1282 instructions_textarea.setWrapStyleWord(true);
1283 Dictionary.setText(instructions_textarea, "CDM.PluginManager.Plugin_Suggestion_Prompt", args);
1284
1285 JLabel suitable_plugins_label = new JLabel();
1286 Dictionary.registerText(suitable_plugins_label, "CDM.PlugInManager.PlugIn");
1287 suitable_plugins_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
1288
1289 suitable_plugins_combobox = new GComboBox(suitable_plugins);
1290 suitable_plugins_combobox.setBackgroundNonSelectionColor(Configuration.getColor("coloring.editable_background", false));
1291 suitable_plugins_combobox.setBackgroundSelectionColor(Configuration.getColor("coloring.collection_selection_background", false));
1292 suitable_plugins_combobox.setTextNonSelectionColor(Configuration.getColor("coloring.workspace_tree_foreground", false));
1293 suitable_plugins_combobox.setTextSelectionColor(Configuration.getColor("coloring.collection_selection_foreground", false));
1294
1295 JPanel suitable_plugins_pane = new JPanel();
1296 //suitable_plugins_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
1297 suitable_plugins_pane.setLayout(new BorderLayout(5,0));
1298 suitable_plugins_pane.add(suitable_plugins_label, BorderLayout.WEST);
1299 suitable_plugins_pane.add(suitable_plugins_combobox, BorderLayout.CENTER);
1300
1301 add_button = new GLIButton();
1302 Dictionary.setBoth(add_button, "CDM.PlugInManager.Add", "CDM.PlugInManager.Add_Tooltip");
1303 ignore_button = new GLIButton();
1304 Dictionary.setBoth(ignore_button, "CDM.PlugInManager.Ignore","CDM.PlugInManager.Ignore_Tooltip" );
1305
1306 add_button.addActionListener(this);
1307 ignore_button.addActionListener(this);
1308
1309 JPanel button_pane = new JPanel();
1310 button_pane.setLayout(new GridLayout(1,2,5,0));
1311 button_pane.add(add_button);
1312 button_pane.add(ignore_button);
1313
1314 JPanel controls_pane = new JPanel();
1315 controls_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
1316 controls_pane.setLayout(new GridLayout(2,1,0,5));
1317 controls_pane.add(suitable_plugins_pane);
1318 controls_pane.add(button_pane);
1319
1320 JPanel content_pane = (JPanel) getContentPane();
1321 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
1322 content_pane.setLayout(new BorderLayout());
1323 content_pane.add(instructions_textarea, BorderLayout.CENTER);
1324 content_pane.add(controls_pane, BorderLayout.SOUTH);
1325
1326 // Show
1327 Dimension screen_size = Configuration.screen_size;
1328 setLocation((screen_size.width - size.width) / 2, (screen_size.height - size.height) / 2);
1329 setVisible(true);
1330 }
1331
1332 public void actionPerformed(ActionEvent event) {
1333
1334 if(event.getSource() == add_button) {
1335 // add the selected plugin to the list
1336 Object selected_object = suitable_plugins_combobox.getSelectedItem();
1337 Plugin base_plugin = getBasePlugin(selected_object.toString());
1338
1339 // Create a new element in the DOM
1340 Element element = CollectionDesignManager.collect_config.document.createElement(CollectionConfiguration.PLUGIN_ELEMENT);
1341 // Remember that the plugin supplied might be a custom string rather than a base plugin
1342 Plugin new_plugin = null;
1343 if(base_plugin != null) {
1344 //DebugStream.println("New Plugin based on existing Plugin");
1345 element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, base_plugin.getName());
1346 new_plugin = new Plugin(element, base_plugin);
1347 }
1348 else {
1349 //DebugStream.println("New Custom Plugin");
1350 element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, selected_object.toString());
1351 new_plugin = new Plugin(element, null);
1352 }
1353 assignPlugin(new_plugin);
1354 } // else do nothing
1355
1356 // close the dialog
1357 setVisible(false);
1358 }
1359
1360 }
1361
1362
1363 /** Creates a list separator.
1364 * Found on Google Groups. Code courtesy of Paul Farwell.
1365 */
1366 private JPanel getSeparator() {
1367 // We put the separator inside a panel to control its appearance
1368 JPanel _sepPanel = new JPanel();
1369 _sepPanel.setOpaque(false);
1370 _sepPanel.setBorder(BorderFactory.createEmptyBorder(1, 3, 1, 3));
1371 _sepPanel.setLayout(new BoxLayout(_sepPanel, BoxLayout.Y_AXIS));
1372 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
1373 // 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
1374 _sepPanel.add(new BasicSeparator());
1375 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
1376 return _sepPanel;
1377 }
1378
1379 /** 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. */
1380 private class BasicSeparator
1381 extends JSeparator {
1382
1383 private ComponentUI basic_ui;
1384
1385 public BasicSeparator() {
1386 super();
1387 basic_ui = new BasicSeparatorUI();
1388 }
1389
1390 public void paintComponent(Graphics g) {
1391 if (basic_ui != null) {
1392 basic_ui.update(g, this);
1393 }
1394 }
1395 }
1396}
Note: See TracBrowser for help on using the repository browser.