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

Last change on this file since 8802 was 8768, checked in by mdewsnip, 20 years ago

A bit of work in preparation for exploding metadata databases.

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