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

Last change on this file since 4932 was 4932, checked in by jmt12, 21 years ago

Major CDM rewrite so it uses DOM.

  • Property svn:keywords set to Author Date Id Revision
File size: 35.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.util.*;
33import javax.swing.*;
34import javax.swing.event.*;
35import javax.swing.plaf.basic.*;
36import org.apache.xerces.parsers.*;
37import org.greenstone.gatherer.Gatherer;
38import org.greenstone.gatherer.cdm.Argument;
39import org.greenstone.gatherer.cdm.ArgumentConfiguration;
40import org.greenstone.gatherer.cdm.CollectionDesignManager;
41import org.greenstone.gatherer.cdm.CommandTokenizer;
42import org.greenstone.gatherer.cdm.Control;
43import org.greenstone.gatherer.cdm.DynamicListModel;
44import org.greenstone.gatherer.cdm.PlugIn;
45import org.greenstone.gatherer.gui.GComboBox;
46import org.greenstone.gatherer.msm.MSMUtils;
47import org.greenstone.gatherer.util.StaticStrings;
48import org.greenstone.gatherer.util.Utility;
49import org.w3c.dom.*;
50import org.xml.sax.*;
51/** This class is resposible for maintaining a list of known plug-ins, and importing new plugins using the parser. */
52public class PlugInManager
53 extends DOMProxyListModel {
54 /** The library 'reserve' of base plugins. */
55 private ArrayList library = null;
56 /** The controls for editing the contents of this manager. */
57 private Control controls = null;
58 private DOMProxyListModel model;
59 private JPanel separator;
60 private PlugIn separator_plugin;
61 /** The default size for a label. */
62 static final private Dimension LABEL_SIZE = new Dimension(140, 20);
63 /** Constructor.
64 */
65 public PlugInManager() {
66 super(CollectionDesignManager.collect_config.getDocumentElement(), CollectionConfiguration.PLUGIN_ELEMENT, new PlugIn());
67 Gatherer.println("PlugInManager: " + getSize() + " plugins parsed.");
68 model = this;
69 // Reload/Create the library
70 loadPlugIns();
71 savePlugIns();
72 // Create the separator, cause we can reuse it.
73 separator = getSeparator();
74 }
75 /** Method to add a new plugin to the library
76 * @param plugin the new base PlugIn
77 */
78 public void addPlugIn(PlugIn plugin) {
79 if(!library.contains(plugin)) {
80 library.add(plugin);
81 }
82 }
83
84 /** Method to assign a plugin
85 * @param plugin the PlugIn to assign
86 */
87 public void assignPlugIn(PlugIn plugin) {
88 if(plugin.getName().equals(StaticStrings.RECPLUG_STR) || plugin.getName().equals(StaticStrings.ARCPLUG_STR)) {
89 addAfter(plugin, separator_plugin); // Adds after separator
90 }
91 else {
92 addBefore(plugin, separator_plugin);
93 }
94 Gatherer.c_man.configurationChanged();
95 }
96
97 /** Destructor. */
98 public void destroy() {
99 if(controls != null) {
100 controls.destroy();
101 controls = null;
102 }
103 library.clear();
104 library = null;
105 }
106
107 /** Method to retrieve the control for this manager.
108 * @return the Control
109 */
110 public Control getControls() {
111 if(controls == null) {
112 // Build controls
113 controls = new PlugInControl();
114 }
115 return controls;
116 }
117
118 /** Retrieve the base pluging of the given name, or null if no such plugin.
119 * @param name the name of the base plugin to retrieve as a String
120 * @return the PlugIn requested or null if no such plugin
121 */
122 public PlugIn getBasePlugIn(String name) {
123 int library_size = library.size();
124 for(int i = 0; i < library_size; i++) {
125 PlugIn plugin = (PlugIn) library.get(i);
126 if(plugin.getName().equals(name)) {
127 return plugin;
128 }
129 }
130 // No success.
131 return null;
132 }
133
134 /** Method to move a plugin in the list order.
135 * @param plugin the PlugIn you want to move.
136 * @param direction true to move the plugin up, false to move it down.
137 * @param all true to move to move all the way, false for a single step.
138 */
139 public void movePlugIn(PlugIn plugin, boolean direction, boolean all) {
140 // Can't ever move RecPlug or ArcPlug.
141 if(getSize() < 3) {
142 Gatherer.println("Not enough plugins to allow moving.");
143 return;
144 }
145 if(plugin.getName().equals(StaticStrings.ARCPLUG_STR) || plugin.getName().equals(StaticStrings.RECPLUG_STR)) {
146 JOptionPane.showMessageDialog(Gatherer.g_man, get("CDM.Move.Fixed"), get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
147 return;
148 }
149 if(all) {
150 // Move to top
151 if(direction) {
152 // Remove the moving plugin
153 remove(plugin);
154 // Retrieve the first plugin
155 PlugIn first_plugin = (PlugIn) getElementAt(0);
156 // Add the moving plugin before the first plugin
157 addBefore(plugin, first_plugin);
158 first_plugin = null;
159 Gatherer.c_man.configurationChanged();
160 }
161 else {
162 // Remove the moving plugin
163 remove(plugin);
164 // Locate the plugin immediately before the separator. We have to ensure the separator index is up to date, given we've just removed a plugin.
165 int separator_index = -1;
166 if((separator_index = findSeparatorIndex()) != -1) {
167 PlugIn separator_plugin = (PlugIn) getElementAt(separator_index);
168 // Add the moving plugin before the separator
169 addBefore(plugin, separator_plugin);
170 Gatherer.c_man.configurationChanged();
171 }
172 // Otherwise we aren't moving anywhere!
173 }
174 }
175 else {
176 // Try to move the plugin one step in the desired direction.
177 int index = indexOf(plugin);
178 ///ystem.err.println("Index of " + plugin + " = " + index);
179 if(direction) {
180 index--;
181 if(index < 0) {
182 String args[] = new String[2];
183 args[0] = get("CDM.PlugInManager.PlugIn_Str");
184 args[1] = plugin.getName();
185 JOptionPane.showMessageDialog(Gatherer.g_man, get("CDM.Move.At_Top", args), get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
186 return;
187 }
188 remove(plugin);
189 add(index, plugin);
190 Gatherer.c_man.configurationChanged();
191 }
192 else {
193 index++;
194 PlugIn next_plugin = (PlugIn) getElementAt(index);
195 if(next_plugin.isSeparator()) {
196 String args[] = new String[1];
197 args[0] = plugin.getName();
198 JOptionPane.showMessageDialog(Gatherer.g_man, get("CDM.Move.Cannot", args), get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
199 // Still not going to move RecPlug or ArcPlug.
200 return;
201 }
202 remove(plugin);
203 add(index, plugin);
204 Gatherer.c_man.configurationChanged();
205 }
206 }
207 }
208
209 /** 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.
210 */
211 public void placeSeparator() {
212 ///ystem.err.println("Placing separator.");
213 int separator_index = getSize();
214 if(separator_index > 0) {
215 boolean found_fixed = false;
216 int index = separator_index - 1;
217 while(index > 0) {
218 PlugIn plugin = (PlugIn) getElementAt(index);
219 String name = plugin.getName();
220 if(name.equals(StaticStrings.RECPLUG_STR) || name.equals(StaticStrings.ARCPLUG_STR)) {
221 found_fixed = true;
222 index--;
223 }
224 else {
225 if(found_fixed) {
226 separator_index = index + 1;
227 index = -1;
228 }
229 else {
230 index--;
231 }
232 }
233 name = null;
234 plugin = null;
235 }
236 }
237 Element element = CollectionDesignManager.collect_config.document.createElement(CollectionConfiguration.PLUGIN_ELEMENT);
238 element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, CollectionConfiguration.SEPARATOR_ATTRIBUTE);
239 element.setAttribute(CollectionConfiguration.SEPARATOR_ATTRIBUTE, CollectionConfiguration.TRUE_STR);
240 separator_plugin = new PlugIn(element, null);
241 ///atherer.println("Adding plugin separator at: " + separator_index);
242 add(separator_index, separator_plugin);
243 }
244
245 /** 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.
246 * @param plugin The <strong>PlugIn</strong> to remove.
247 */
248 public void removePlugIn(PlugIn plugin) {
249 remove(plugin);
250 Gatherer.c_man.configurationChanged();
251 }
252
253 /** Method to cache the current contents of library (known plugins) to file.
254 */
255 public void savePlugIns() {
256 try {
257 FileOutputStream file = new FileOutputStream(Utility.BASE_DIR + "plugins.dat");
258 ObjectOutputStream out = new ObjectOutputStream(file);
259 out.writeObject(library);
260 out.close();
261 }
262 catch (Exception error) {
263 Gatherer.printStackTrace(error);
264 }
265 }
266
267 /** Determine the current separator index. */
268 private int findSeparatorIndex() {
269 int separator_index = getSize() - 1;
270 while(separator_index >= 0) {
271 PlugIn search = (PlugIn) getElementAt(separator_index);
272 if(search.isSeparator()) {
273 return separator_index;
274 }
275 separator_index--;
276 }
277 return separator_index;
278 }
279
280 /* Retrieve a phrase from the dictionary based on a certain key.
281 * @param key The search <strong>String</strong>.
282 * @return The matching phrase from the Dictionary.
283 */
284 private String get(String key) {
285 return get(key, (String[])null);
286 }
287
288 private String get(String key, String arg) {
289 String args[] = new String[1];
290 args[0] = arg;
291 return get(key, args);
292 }
293
294 /* Retrieve a phrase from the dictionary based on a certain key and arguments.
295 * @param key The search <strong>String</strong>.
296 * @param args A <strong>String[]</strong> of arguments used to complete and format the choosen phrase.
297 * @return The matching phrase from the Dictionary.
298 */
299 private String get(String key, String args[]) {
300 if(key.indexOf(".") == -1) {
301 key = "CDM.PlugInManager." + key;
302 }
303 return Gatherer.dictionary.get(key, args);
304 }
305
306 /** Retrieve a list of those plugins that are in library but not in the assigned plugins. */
307 private Object[] getAvailable() {
308 ArrayList available = new ArrayList();
309 available.addAll(library);
310 // Now go through the assigned plugins, and remove any that match.
311 available.removeAll(children());
312 return available.toArray();
313 }
314
315 /** Method to extract just the plugins name from a file object.
316 * @param plugin The <strong>File</strong> which references a certain plugin.
317 * @return A <strong>String</strong> containing just the plugins name, without extension.
318 */
319 private String getPlugInName(File plugin) {
320 String name = plugin.getName();
321 if(name.indexOf(".") != -1) {
322 name = name.substring(0, name.indexOf("."));
323 }
324 return name;
325 }
326
327 /** Method to load the details of a single plug-in.
328 * @param plugin The plugin <strong>File</strong> you wish to load.
329 */
330 private void loadPlugIn(File plugin) {
331 Document document = null;
332 // Run pluginfo on this plugin, and then send the results for parsing.
333 try {
334 String args[] = null;
335 if(Utility.isWindows()) {
336 args = new String[4];
337 if(Gatherer.config.perl_path != null) {
338 args[0] = Gatherer.config.perl_path;
339 }
340 else {
341 args[0] = "Perl.exe";
342 }
343 args[1] = Gatherer.config.gsdl_path + "bin" + File.separator + "script" + File.separator + "pluginfo.pl";
344 args[2] = "-xml";
345 args[3] = getPlugInName(plugin);
346 }
347 else {
348 args = new String[3];
349 args[0] = "pluginfo.pl";
350 args[1] = "-xml";
351 args[2] = getPlugInName(plugin);
352 }
353 // Create the process.
354 Runtime runtime = Runtime.getRuntime();
355 Process process = runtime.exec(args);
356 //InputStream input_stream = process.getErrorStream();
357 BufferedReader error_in = new BufferedReader(new InputStreamReader(process.getErrorStream()));
358 String line = "";
359 StringBuffer xml = new StringBuffer("");
360 boolean xml_content = false;
361 while((line = error_in.readLine()) != null) {
362 if(xml_content) {
363 xml.append(line);
364 xml.append("\n");
365 }
366 else if(line.trim().startsWith("<?xml")) {
367 xml_content = true;
368 xml.append(line);
369 xml.append("\n");
370 }
371 }
372 error_in = null;
373 process = null;
374 runtime = null;
375 args = null;
376 // If something has gone horribly wrong then xml will be empty.
377 if(xml.length() != 0) {
378 // Then read the xml from the piped input stream.
379 InputSource source = new InputSource(new StringReader(xml.toString()));
380 DOMParser parser = new DOMParser();
381 parser.parse(source);
382 document = parser.getDocument();
383 parser = null;
384 source = null;
385 }
386 else {
387 String plugin_name = getPlugInName(plugin);
388 Gatherer.println("Zero length argument xml detected for: " + plugin_name);
389 JOptionPane.showMessageDialog(Gatherer.g_man, get("PlugIn_XML_Parse_Failed", plugin_name), get("General.Error"), JOptionPane.ERROR_MESSAGE);
390 }
391 }
392 catch (Exception error) {
393 error.printStackTrace();
394 }
395 if(document != null) {
396 parseXML(document.getDocumentElement());
397 }
398 }
399
400 /** Method to initially load information from the standard plug-ins within the gsdl Perl library.
401 */
402 private void loadPlugIns() {
403 // Attempt to restore the cached file.
404 try {
405 FileInputStream file = new FileInputStream(Utility.BASE_DIR + "plugins.dat");
406 ObjectInputStream input = new ObjectInputStream(file);
407 library = (ArrayList) input.readObject();
408 }
409 catch (Exception error) {
410 }
411 if(library == null) {
412 library = new ArrayList();
413 // Retrieve the gsdl home directory...
414 String directory = Gatherer.config.gsdl_path;
415 directory = directory + "perllib" + File.separator + "plugins" + File.separator;
416 loadPlugIns(new File(directory));
417 }
418 }
419 /** Method to load plug-in information from a specified directory. Of course no plug-ins may be found at this location.
420 * @param directory A <strong>File</strong> indicating the directory to be scanned for plug-ins.
421 */
422 private void loadPlugIns(File directory) {
423 File files[] = directory.listFiles();
424 if(files != null) {
425 // Create a progress indicator.
426 ParsingProgress progress = new ParsingProgress(get("CDM.PlugInManager.Parsing.Title"), get("CDM.PlugInManager.Parsing.Message"), files.length);
427 for(int i = 0; i < files.length; i++) {
428 // We only want to check Perl Modules.
429 if(files[i].getName().endsWith(".pm")) {
430 loadPlugIn(files[i]);
431 }
432 progress.inc();
433 }
434 progress.dispose();
435 }
436 }
437
438 private PlugIn parseXML(Node root) {
439 PlugIn plugin = new PlugIn();
440 String node_name = null;
441 for(Node node = root.getFirstChild(); node != null;
442 node = node.getNextSibling()) {
443 node_name = node.getNodeName();
444 if(node_name.equals("Name")) {
445 String name = MSMUtils.getValue(node);
446 // 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.
447 PlugIn existing = getBasePlugIn(name);
448 if(existing != null) {
449 return existing;
450 }
451 plugin.setName(name);
452 }
453 else if(node_name.equals("Desc")) {
454 plugin.setDescription(MSMUtils.getValue(node));
455 }
456 // Parse the multitude of arguments.
457 else if(node_name.equals("Arguments")) {
458 for(Node arg = node.getFirstChild(); arg != null; arg = arg.getNextSibling()) {
459 node_name = arg.getNodeName();
460 // An option.
461 if(node_name.equals("Option")) {
462 Argument argument = new Argument();
463 // If its an option we parse the multitude of details an options might have.
464 for(Node det = arg.getFirstChild(); det != null; det = det.getNextSibling()) {
465 node_name = det.getNodeName();
466 if(node_name.equals("Name")) {
467 argument.setName(MSMUtils.getValue(det));
468 }
469 else if(node_name.equals("Desc")) {
470 argument.setDescription(MSMUtils.getValue(det));
471 }
472 else if(node_name.equals("Type")) {
473 argument.setType(MSMUtils.getValue(det));
474 }
475 else if(node_name.equals("Default")) {
476 argument.setDefaultValue(MSMUtils.getValue(det));
477 }
478 else if(node_name.equals("List")) {
479 // Two final loops are required to parse lists.
480 for(Node value = det.getFirstChild(); value != null; value = value.getNextSibling()) {
481 if(value.getNodeName().equals("Value")) {
482 String key = null;
483 String desc = "";
484 for(Node subvalue = value.getFirstChild(); subvalue != null; subvalue = subvalue.getNextSibling()) {
485 node_name = subvalue.getNodeName();
486 if(node_name.equals("Name")) {
487 key = MSMUtils.getValue(subvalue);
488 }
489 else if(node_name.equals("Desc")) {
490 desc = MSMUtils.getValue(subvalue);
491 }
492 }
493 if(key != null) {
494 argument.addOption(key, desc);
495 }
496 }
497 }
498 }
499 else if(node_name.equals("Required")) {
500 String v = MSMUtils.getValue(det);
501 if(v != null && v.equals("yes")) {
502 argument.setRequired(true);
503 }
504 }
505 }
506 plugin.addArgument(argument);
507 }
508 // A super plugin class.
509 else if(node_name.equals("PlugInfo")) {
510 PlugIn super_plugin = parseXML(arg);
511 plugin.setSuper(super_plugin);
512 }
513 }
514 }
515 }
516 if(plugin.getName() != null) {
517 addPlugIn(plugin);
518 return plugin;
519 }
520 return null;
521 }
522
523 /** A class which provodes controls for assigned and editing plugins. */
524 private class PlugInControl
525 extends JPanel
526 implements Control {
527 /** Button for adding plugins. */
528 private JButton add = null;
529 /** Button for configuring the selected plugin. */
530 private JButton configure = null;
531 /** Buttom to move an assinged plugin as low in the order as possible. */
532 private JButton move_bottom = null;
533 /** Button to move an assigned plugin one position lower in the order. */
534 private JButton move_down = null;
535 /** Button to move an assigned plugin as high in the order as possible. */
536 private JButton move_top = null;
537 /** Button to move an assigned plugin one position higher in the order. */
538 private JButton move_up = null;
539 /** Button to remove the selected plugin. */
540 private JButton remove = null;
541 /** A combobox containing all of the known plugins, including those that may have already been assigned. */
542 private GComboBox plugin = null;
543 /** The label next to the plugin combobox. */
544 private JLabel plugin_label = null;
545 /** The label above the assigned plugin list. */
546 private JLabel plugin_list_label = null;
547 /** The title of this view. */
548 private JLabel title = null;
549 /** A list of assigned plugins. */
550 private JList plugin_list = null;
551 /** The area where the add, configure and remove buttons are placed. */
552 private JPanel button_pane = null;
553 /** The region which divides the central portion of the view into list and controls. */
554 private JPanel central_pane = null;
555 /** The area where title label and instructions sit. */
556 private JPanel header_pane = null;
557 /** The area where movement buttons are placed. */
558 private JPanel movement_pane = null;
559 /** The small region containing the plugin combobox and its label. */
560 private JPanel plugin_pane = null;
561 /** The pane containing the assigned plugin list and its label. */
562 private JPanel plugin_list_pane = null;
563 /** The text area containing instructions on the use of this control. */
564 private JTextArea instructions = null;
565 /** Constructor.
566 */
567 public PlugInControl() {
568 Object plugins[] = library.toArray();
569 Vector plugin_model = new Vector();
570 for(int i = 0; i < plugins.length; i++) {
571 plugin_model.add(((PlugIn)plugins[i]).getName());
572 }
573 Collections.sort(plugin_model);
574 // Create
575 add = new JButton(get("Add"));
576 add.setMnemonic(KeyEvent.VK_A);
577 button_pane = new JPanel();
578 central_pane = new JPanel();
579 configure = new JButton(get("Configure"));
580 configure.setEnabled(false);
581 configure.setMnemonic(KeyEvent.VK_C);
582 header_pane = new JPanel();
583 instructions = new JTextArea(get("Instructions"));
584 instructions.setBackground(Gatherer.config.getColor("coloring.collection_tree_background", false));
585 instructions.setEditable(false);
586 instructions.setLineWrap(true);
587 instructions.setRows(5);
588 instructions.setWrapStyleWord(true);
589 move_bottom = new JButton();
590 JLabel move_bottom_label = new JLabel(get("CDM.Move.Move_Bottom"));
591 move_bottom_label.setHorizontalAlignment(JLabel.CENTER);
592 move_bottom_label.setPreferredSize(LABEL_SIZE);
593 move_bottom.setLayout(new BorderLayout());
594 move_bottom.add(new JLabel(Utility.getImage("arrow-bottom.gif")), BorderLayout.WEST);
595 move_bottom.add(move_bottom_label, BorderLayout.CENTER);
596 move_bottom.add(new JLabel(Utility.getImage("arrow-bottom.gif")), BorderLayout.EAST);
597 move_bottom.setMnemonic(KeyEvent.VK_B);
598 move_down = new JButton();
599 JLabel move_down_label = new JLabel(get("CDM.Move.Move_Down"));
600 move_down_label.setHorizontalAlignment(JLabel.CENTER);
601 move_down_label.setPreferredSize(LABEL_SIZE);
602 move_down.setLayout(new BorderLayout());
603 move_down.add(new JLabel(Utility.getImage("arrow-down.gif")), BorderLayout.WEST);
604 move_down.add(move_down_label, BorderLayout.CENTER);
605 move_down.add(new JLabel(Utility.getImage("arrow-down.gif")), BorderLayout.EAST);
606 move_down.setMnemonic(KeyEvent.VK_D);
607 move_top = new JButton();
608 JLabel move_top_label = new JLabel(get("CDM.Move.Move_Top"));
609 move_top_label.setHorizontalAlignment(JLabel.CENTER);
610 move_top_label.setPreferredSize(LABEL_SIZE);
611 move_top.setLayout(new BorderLayout());
612 move_top.add(new JLabel(Utility.getImage("arrow-top.gif")), BorderLayout.WEST);
613 move_top.add(move_top_label, BorderLayout.CENTER);
614 move_top.add(new JLabel(Utility.getImage("arrow-top.gif")), BorderLayout.EAST);
615 move_top.setMnemonic(KeyEvent.VK_T);
616 move_up = new JButton();
617 JLabel move_up_label = new JLabel(get("CDM.Move.Move_Up"));
618 move_up_label.setHorizontalAlignment(JLabel.CENTER);
619 move_up_label.setPreferredSize(LABEL_SIZE);
620 move_up.setLayout(new BorderLayout());
621 move_up.add(new JLabel(Utility.getImage("arrow-up.gif")), BorderLayout.WEST);
622 move_up.add(move_up_label, BorderLayout.CENTER);
623 move_up.add(new JLabel(Utility.getImage("arrow-up.gif")), BorderLayout.EAST);
624 move_up.setMnemonic(KeyEvent.VK_U);
625 movement_pane = new JPanel();
626
627 plugin = new GComboBox(getAvailable());
628 plugin.setBackgroundNonSelectionColor(Gatherer.config.getColor("coloring.editable", false));
629 plugin.setBackgroundSelectionColor(Gatherer.config.getColor("coloring.collection_selection_background", false));
630 plugin.setEditable(true);
631 plugin.setSelectedIndex(0);
632 plugin.setTextNonSelectionColor(Gatherer.config.getColor("coloring.workspace_tree_foreground", false));
633 plugin.setTextSelectionColor(Gatherer.config.getColor("coloring.collection_selection_foreground", false));
634
635 plugin_label = new JLabel(get("PlugIn"));
636 plugin_list = new JList(model);
637 plugin_list.setCellRenderer(new ListRenderer());
638 plugin_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
639 plugin_list_label = new JLabel(get("Assigned"));
640 plugin_list_label.setHorizontalAlignment(JLabel.CENTER);
641 plugin_list_label.setOpaque(true);
642 plugin_list_pane = new JPanel();
643 plugin_pane = new JPanel();
644 remove = new JButton(get("Remove"));
645 remove.setEnabled(false);
646 remove.setMnemonic(KeyEvent.VK_R);
647 title = new JLabel(get("Title"));
648 title.setHorizontalAlignment(JLabel.CENTER);
649 title.setOpaque(true);
650 // Listeners
651 add.addActionListener(new AddListener());
652 configure.addActionListener(new ConfigureListener());
653 MoveListener ml = new MoveListener();
654 move_bottom.addActionListener(ml);
655 move_down.addActionListener(ml);
656 move_top.addActionListener(ml);
657 move_up.addActionListener(ml);
658 remove.addActionListener(new RemoveListener());
659 plugin_list.addMouseListener(new ClickListener());
660 plugin_list.addListSelectionListener(new ListListener());
661 // Layout
662 title.setBorder(BorderFactory.createEmptyBorder(0,0,2,0));
663
664 instructions.setBorder(BorderFactory.createEmptyBorder(2,5,2,5));
665
666 header_pane.setLayout(new BorderLayout());
667 header_pane.add(title, BorderLayout.NORTH);
668 header_pane.add(new JScrollPane(instructions), BorderLayout.CENTER);
669
670 plugin_list_label.setBorder(BorderFactory.createEmptyBorder(0,2,0,2));
671
672 movement_pane.setLayout(new GridLayout(4,1));
673 movement_pane.add(move_top);
674 movement_pane.add(move_up);
675 movement_pane.add(move_down);
676 movement_pane.add(move_bottom);
677
678 plugin_list_pane.setLayout(new BorderLayout());
679 plugin_list_pane.add(plugin_list_label, BorderLayout.NORTH);
680 plugin_list_pane.add(new JScrollPane(plugin_list), BorderLayout.CENTER);
681 plugin_list_pane.add(movement_pane, BorderLayout.EAST);
682
683 plugin_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
684
685 plugin_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
686 plugin_pane.setLayout(new GridLayout(1,2));
687 plugin_pane.add(plugin_label);
688 plugin_pane.add(plugin);
689
690 // Scope these mad bordering skillz.
691 JPanel temp = new JPanel(new BorderLayout());
692 temp.setBorder
693 (BorderFactory.createCompoundBorder
694 (BorderFactory.createEmptyBorder(5,0,5,0),
695 BorderFactory.createCompoundBorder
696 (BorderFactory.createTitledBorder(get("Controls")),
697 BorderFactory.createEmptyBorder(2,2,2,2))));
698
699 temp.add(plugin_pane, BorderLayout.NORTH);
700 temp.add(button_pane, BorderLayout.SOUTH);
701
702 central_pane.setLayout(new BorderLayout());
703 central_pane.add(plugin_list_pane, BorderLayout.CENTER);
704 central_pane.add(temp, BorderLayout.SOUTH);
705
706 button_pane.setLayout(new GridLayout(3,1));
707 button_pane.add(add);
708 button_pane.add(configure);
709 button_pane.add(remove);
710
711 setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
712 setLayout(new BorderLayout());
713 add(header_pane, BorderLayout.NORTH);
714 add(central_pane, BorderLayout.CENTER);
715 //add(button_pane, BorderLayout.SOUTH);
716 }
717 /** Method which acts like a destructor, tidying up references to persistant objects.
718 */
719 public void destroy() {
720 }
721
722 /** This method is overridden to ensure the instructions are scrolled to top, before the super classes updateUI() is called.
723 */
724 public void gainFocus() {
725 if(instructions != null) {
726 instructions.setCaretPosition(0);
727 }
728 super.updateUI();
729 }
730
731 public void loseFocus() {
732
733 }
734
735 /** This class listens for actions upon the add button in the controls, and if detected calls the <i>assignPlugIn()</i> method. */
736 private class AddListener
737 implements ActionListener {
738 /** 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.
739 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
740 */
741 public void actionPerformed(ActionEvent event) {
742 Object selected_object = plugin.getSelectedItem();
743 // If there is something in the combobox, but we haven't registered a selection, then add the object and select it!
744 if(selected_object != null && plugin.getSelectedIndex() == -1) {
745 plugin.insertItemAt(selected_object, plugin.getItemCount());
746 }
747 if(selected_object != null) {
748 // Create a new element in the DOM
749 Element element = CollectionDesignManager.collect_config.document.createElement(CollectionConfiguration.PLUGIN_ELEMENT);
750 // Remember that the plugin supplied might be a custom string rather than a base plugin
751 PlugIn new_plugin = null;
752 if(selected_object instanceof PlugIn) {
753 PlugIn base_plugin = (PlugIn) selected_object;
754 element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, base_plugin.getName());
755 new_plugin = new PlugIn(element, base_plugin);
756 base_plugin = null;
757 }
758 else {
759 element.setAttribute(CollectionConfiguration.TYPE_ATTRIBUTE, selected_object.toString());
760 new_plugin = new PlugIn(element, null);
761 }
762 if(!model.contains(new_plugin)) {
763 // Automatically chain to configuration. This ensures required arguments are filled out.
764 ArgumentConfiguration ac = new ArgumentConfiguration(new_plugin);
765 if(ac.display()) {
766 assignPlugIn(new_plugin);
767 plugin_list.setSelectedValue(new_plugin, true);
768 }
769 ac = null;
770 new_plugin = null;
771 plugin.setSelectedIndex(0);
772 }
773 else {
774 JOptionPane.showMessageDialog(Gatherer.g_man, get("PlugIn_Exists"), get("General.Error"), JOptionPane.ERROR_MESSAGE);
775 }
776 }
777 }
778 }
779
780 /** Listens for double clicks apon the list and react as if the configure button was pushed. */
781 private class ClickListener
782 extends MouseAdapter {
783 /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt.
784 * @param event A <strong>MouseEvent</strong> containing information about the mouse click.
785 */
786 public void mouseClicked(MouseEvent event) {
787 if(event.getClickCount() == 2 ) {
788 if(!plugin_list.isSelectionEmpty()) {
789 PlugIn plugin = (PlugIn) plugin_list.getSelectedValue();
790 if(!plugin.isSeparator()) {
791 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
792 if(ac.display()) {
793 refresh(plugin);
794 }
795 ac.destroy();
796 ac = null;
797 }
798 }
799 }
800 }
801 }
802
803 /** 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.
804 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration
805 */
806 private class ConfigureListener
807 implements ActionListener {
808 /** 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.
809 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
810 */
811 public void actionPerformed(ActionEvent event) {
812 if(!plugin_list.isSelectionEmpty()) {
813 PlugIn plugin = (PlugIn) plugin_list.getSelectedValue();
814 if(!plugin.isSeparator()) {
815 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
816 if(ac.display()) {
817 refresh(plugin);
818 }
819 ac.destroy();
820 ac = null;
821 }
822 }
823 }
824 }
825
826 /** 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 */
827 private class ListListener
828 implements ListSelectionListener {
829
830 public void valueChanged(ListSelectionEvent e) {
831 if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one ( the second one)
832 if (plugin_list.isSelectionEmpty()) {
833 configure.setEnabled(false);
834 remove.setEnabled(false);
835 } else {
836 configure.setEnabled(true);
837 remove.setEnabled(true);
838 }
839 }
840 }
841 }
842
843 /** A special list renderer which is able to render separating lines as well. */
844 private class ListRenderer
845 extends DefaultListCellRenderer {
846 /** 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.
847 * @param list - The <strong>JList</strong> we're painting.
848 * @param value - The value returned by list.getModel().getElementAt(index) as an <strong>Object</strong>.
849 * @param index - The cells index as an <i>int</i>.
850 * @param isSelected - <i>true</i> if the specified cell was selected.
851 * @param cellHasFocus - <i>true</i> if the specified cell has the focus.
852 * @return A <strong>Component</strong> whose paint() method will render the specified value.
853 * @see javax.swing.JList
854 * @see javax.swing.JSeparator
855 * @see javax.swing.ListModel
856 * @see javax.swing.ListSelectionModel
857 */
858 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
859 PlugIn plugin = (PlugIn) value;
860 if(plugin.isSeparator()) {
861 return separator;
862 }
863 else {
864 return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
865 }
866 }
867 }
868
869
870 /** 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. */
871 private class MoveListener
872 implements ActionListener {
873 /** 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.
874 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
875 */
876 public void actionPerformed(ActionEvent event) {
877 if(!plugin_list.isSelectionEmpty()) {
878 Object object = plugin_list.getSelectedValue();
879 if(object instanceof PlugIn) {
880 PlugIn plugin = (PlugIn) object;
881 if(event.getSource() == move_top) {
882 movePlugIn(plugin, true, true);
883 }
884 else if(event.getSource() == move_up) {
885 movePlugIn(plugin, true, false);
886 }
887 else if(event.getSource() == move_down) {
888 movePlugIn(plugin, false, false);
889 }
890 else {
891 movePlugIn(plugin, false, true);
892 }
893 plugin_list.setSelectedValue(plugin, true);
894 }
895 }
896 }
897 }
898 /** This class listens for actions upon the remove button in the controls, and if detected calls the <i>removePlugIn()</i> method.
899 */
900 private class RemoveListener
901 implements ActionListener {
902 /** 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.
903 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
904 */
905 public void actionPerformed(ActionEvent event) {
906 if(!plugin_list.isSelectionEmpty()) {
907 Object [] objects = plugin_list.getSelectedValues();
908 for(int i = 0; i < objects.length; i++) {
909 if(objects[i] instanceof PlugIn) {
910 removePlugIn((PlugIn)objects[i]);
911 }
912 }
913 //Object object = plugin_list.getSelectedValue();
914 //if(object instanceof PlugIn) {
915 //removePlugIn((PlugIn)object);
916 //}
917 }
918 }
919 }
920 }
921 /** Creates a list separator.
922 * Code courtesy of Farwell, Paul. Contact <a href="mailto:[email protected]">[email protected]</a>
923 */
924 static private JPanel getSeparator() {
925 // we put the separator inside a panel to control
926 // its appearance
927 JPanel _sepPanel = new JPanel();
928 _sepPanel.setOpaque(false);
929 _sepPanel.setBorder(BorderFactory.createEmptyBorder(1, 3, 1, 3));
930 _sepPanel.setLayout(new BoxLayout(_sepPanel, BoxLayout.Y_AXIS));
931 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
932 _sepPanel.add(new JPopupMenu.Separator());
933 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
934 return _sepPanel;
935 }
936}
Note: See TracBrowser for help on using the repository browser.