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

Last change on this file since 14234 was 14234, checked in by xiao, 17 years ago

modify RemoveListener so that the remove button is disabled after the plugin above MetadataXMLPlug was deleted and the focus automatically falls on the MetadataXMLPlug.

  • Property svn:keywords set to Author Date Id Revision
File size: 39.2 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.collection.CollectionManager;
45import org.greenstone.gatherer.greenstone.Plugins;
46import org.greenstone.gatherer.gui.DesignPaneHeader;
47import org.greenstone.gatherer.gui.GComboBox;
48import org.greenstone.gatherer.gui.GLIButton;
49import org.greenstone.gatherer.gui.ModalDialog;
50import org.greenstone.gatherer.gui.WarningDialog;
51import org.greenstone.gatherer.remote.RemoteGreenstoneServer;
52import org.greenstone.gatherer.util.JarTools;
53import org.greenstone.gatherer.util.StaticStrings;
54import org.greenstone.gatherer.util.Utility;
55import org.greenstone.gatherer.util.XMLTools;
56import org.w3c.dom.*;
57import org.xml.sax.*;
58
59
60/** This class is for maintaining a list of known plug-ins, and importing new plugins using the parser. */
61public class PluginManager
62 extends DOMProxyListModel
63 implements CollectionContentsChangedListener
64{
65 /** 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. */
66 private boolean modify_row_count = false;
67 /** The controls for editing the contents of this manager. */
68 private Control controls = null;
69 private DOMProxyListModel model;
70 private JPanel separator;
71 private Plugin separator_plugin;
72 /** Constructor.
73 */
74 public PluginManager() { super(CollectionDesignManager.collect_config.getDocumentElement(), StaticStrings.PLUGIN_ELEMENT, new Plugin());
75 DebugStream.println("PluginManager: " + super.getSize() + " plugins parsed.");
76 model = this;
77
78 // Force the assigned plugins to be loaded and cached now
79 for (int i = 0; i < getSize(); i++) {
80 getElementAt(i);
81 }
82
83 // Create the separator, cause we can reuse it.
84 separator = getSeparator();
85 placeSeparator();
86 // check for MetadataXMLPlug
87 ensureMetadataXMLPlugIsLoaded();
88 // Listen for CollectionContentsChanged events, so we can give plugin hints when new files are added
89 CollectionManager.addCollectionContentsChangedListener(this);
90
91 }
92
93 private void ensureMetadataXMLPlugIsLoaded() {
94
95 Plugin metaxmlplug = Plugins.getPlugin("MetadataXMLPlug", false);
96 if (metaxmlplug!= null) {
97 if (!contains(metaxmlplug)) {
98 // Create a new element in the DOM
99 Element new_plugin_element = CollectionConfiguration.createElement(StaticStrings.PLUGIN_ELEMENT);
100 new_plugin_element.setAttribute(StaticStrings.TYPE_ATTRIBUTE, metaxmlplug.getName());
101 Plugin new_plugin = new Plugin(new_plugin_element, metaxmlplug);
102 assignPlugin(new_plugin);
103 }
104 }
105 }
106
107
108 /** Retrieve a list of the plugins that are available to be added to the collection. */
109 private Object[] getAvailablePlugins()
110 {
111 ArrayList available = new ArrayList();
112
113 // Add all the non-abstract core Greenstone plugins, except for ArcPlug and RecPlug
114 ArrayList plugins_list = Plugins.getPluginsList();
115 for (int i = 0; i < plugins_list.size(); i++) {
116 Plugin plugin = (Plugin) plugins_list.get(i);
117 if (!plugin.isAbstract()) {
118 String plugin_name = plugin.getName();
119 if (!plugin_name.equals(StaticStrings.ARCPLUG_STR) && !plugin_name.equals(StaticStrings.RECPLUG_STR)) {
120 available.add(plugin);
121 }
122 }
123 }
124
125 // Now remove any assigned plugins
126 if (Configuration.getMode() < Configuration.SYSTEMS_MODE) {
127 available.removeAll(children());
128 }
129
130 // Sort the available plugins into alphabetical order
131 Collections.sort(available);
132
133 return available.toArray();
134 }
135
136
137 public ArrayList getExploderPlugins(File file)
138 {
139 ArrayList exploder_plugins = new ArrayList();
140 ArrayList plugins_list = Plugins.getPluginsList();
141 for (int i = 0; i < plugins_list.size(); i++) {
142 Plugin plugin = (Plugin) plugins_list.get(i);
143 if (plugin.doesExplodeMetadataDatabases() == true && plugin.doesProcessFile(file)) {
144 exploder_plugins.add(plugin);
145 }
146 }
147
148 return exploder_plugins;
149 }
150
151
152 public boolean isFileExplodable(File file)
153 {
154 ArrayList plugins_list = Plugins.getPluginsList();
155 for (int i = 0; i < plugins_list.size(); i++) {
156 Plugin plugin = (Plugin) plugins_list.get(i);
157 if (plugin.doesExplodeMetadataDatabases() == true && plugin.doesProcessFile(file) == true) {
158 return true;
159 }
160 }
161
162 return false;
163 }
164
165
166 /** Method to assign a plugin
167 * @param plugin the Plugin to assign
168 */
169 private void assignPlugin(Plugin plugin) {
170 if(plugin.getName().equals(StaticStrings.RECPLUG_STR) || plugin.getName().equals(StaticStrings.ARCPLUG_STR)) {
171 addAfter(plugin, separator_plugin); // Adds after separator
172 } else {
173 addBefore(plugin, separator_plugin);
174 }
175 }
176
177
178 /** Destructor. */
179 public void destroy()
180 {
181 CollectionManager.removeCollectionContentsChangedListener(this);
182
183 if (controls != null) {
184 controls.destroy();
185 controls = null;
186 }
187 }
188
189
190 /** This function listens for new files being added to the collection and hints about suitable plugins. */
191 public void fileAddedToCollection(File file)
192 {
193 // First check the plugins already assigned in the collection
194 for (int i = 0; i < super.getSize(); i++) {
195 Plugin assigned_plugin = (Plugin) getElementAt(i);
196 if (assigned_plugin.isSeparator() == false && (assigned_plugin.doesProcessFile(file) == true || assigned_plugin.doesBlockFile(file) == true)) {
197 // This file will be processed by an assigned plugin, so no suggestion is necessary
198 DebugStream.println("Processed by assigned plugin: " + assigned_plugin);
199 return;
200 }
201 }
202
203 // Next try the plugins NOT already assigned in the collection
204 ArrayList suitable_plugins = new ArrayList();
205 Object[] unassigned_plugins = getAvailablePlugins();
206 for (int i = 0; i < unassigned_plugins.length; i++) {
207 Plugin unassigned_plugin = (Plugin) unassigned_plugins[i];
208 if (unassigned_plugin.doesProcessFile(file) == true) {
209 DebugStream.println("Processed by unassigned plugin: " + unassigned_plugin);
210 suitable_plugins.add(unassigned_plugin);
211 }
212 }
213
214 // If there appear to be no suitable plugins, warn the user about this and be done
215 if (suitable_plugins.size() == 0) {
216 String[] args = new String[1];
217 args[0] = file.getName();
218 WarningDialog warning_dialog = new WarningDialog("warning.NoPluginExpectedToProcessFile", Dictionary.get("NoPluginExpectedToProcessFile.Title"), Dictionary.get("NoPluginExpectedToProcessFile.Message", args), null, false);
219 warning_dialog.display();
220 warning_dialog.dispose();
221 return;
222 }
223
224 // Generate a dialog
225 new PluginSuggestionPrompt(file.getName(), suitable_plugins);
226 }
227
228
229 /** Method to retrieve the control for this manager.
230 * @return the Control
231 */
232 public Control getControls() {
233 if(controls == null) {
234 // Build controls
235 controls = new PluginControl();
236 }
237 return controls;
238 }
239
240
241 /** Overrides getSize in DOMProxyListModel to take into account the row count modifier used to hide the last three rows in lower detail modes
242 * @return an int indicating the number of rows in the model, or more correctly the desired number of rows to display
243 */
244 public int getSize() {
245 int result = super.getSize();
246 if(modify_row_count) {
247 result = result-3;
248 }
249 return result;
250 }
251
252 /** Called when the detail mode has changed which in turn may cause several design elements to be available/hidden
253 * @param mode the new mode as an int
254 */
255 public void modeChanged(int mode) {
256 if(controls != null) {
257 ((PluginControl)controls).modeChanged(mode);
258 }
259 }
260
261 /** Method to move a plugin in the list order.
262 * @param plugin the Plugin you want to move.
263 * @param direction true to move the plugin up, false to move it down.
264 * @param all true to move to move all the way, false for a single step.
265 */
266 // why are all the error notices there when the buttons are disabled is you cant move???
267 private void movePlugin(Plugin plugin, boolean direction, boolean all) {
268 // Can't ever move RecPlug or ArcPlug
269 if(super.getSize() < 4) {
270 //DebugStream.println("Not enough plugins to allow moving.");
271 return;
272 }
273 if(plugin.getName().equals(StaticStrings.ARCPLUG_STR) || plugin.getName().equals(StaticStrings.RECPLUG_STR)) {
274 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.Fixed"), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
275 return;
276 }
277 if(all) {
278 // Move to top
279 if(direction) {
280 // Remove the moving plugin
281 remove(plugin);
282 // Retrieve the first plugin
283 Plugin first_plugin = (Plugin) getElementAt(0);
284 // Add the moving plugin before the first plugin
285 addBefore(plugin, first_plugin);
286 first_plugin = null;
287 }
288 else {
289 // Remove the moving plugin
290 remove(plugin);
291 // Add the moving plugin before the separator
292 addBefore(plugin, separator_plugin);
293 }
294 }
295 else {
296 // Try to move the plugin one step in the desired direction.
297 int index = indexOf(plugin);
298 ///ystem.err.println("Index of " + plugin + " = " + index);
299 if(direction) {
300 index--;
301 if(index < 0) {
302 String args[] = new String[2];
303 args[0] = Dictionary.get("CDM.PlugInManager.PlugIn_Str");
304 args[1] = plugin.getName();
305 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.At_Top", args), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
306 return;
307 }
308 remove(plugin);
309 add(index, plugin);
310 }
311 else {
312 index++;
313 Plugin next_plugin = (Plugin) getElementAt(index);
314 if(next_plugin.isSeparator()) {
315 String args[] = new String[1];
316 args[0] = plugin.getName();
317 JOptionPane.showMessageDialog(Gatherer.g_man, Dictionary.get("CDM.Move.Cannot", args), Dictionary.get("CDM.Move.Title"), JOptionPane.ERROR_MESSAGE);
318 // Still not going to move RecPlug or ArcPlug.
319 return;
320 }
321 remove(plugin);
322 add(index, plugin);
323 }
324 }
325 }
326
327 /** 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.
328 */
329 private void placeSeparator() {
330 ///ystem.err.println("Placing separator.");
331 int separator_index = super.getSize();
332 if(separator_index > 0) {
333 boolean found_fixed = false;
334 int index = separator_index - 1;
335 while(index > 0) {
336 Plugin plugin = (Plugin) getElementAt(index);
337 String name = plugin.getName();
338 if(name.equals(StaticStrings.RECPLUG_STR) || name.equals(StaticStrings.ARCPLUG_STR)) {
339 found_fixed = true;
340 index--;
341 }
342 else {
343 if(found_fixed) {
344 separator_index = index + 1;
345 index = -1;
346 }
347 else {
348 index--;
349 }
350 }
351 name = null;
352 plugin = null;
353 }
354 }
355 Element element = CollectionConfiguration.createElement(StaticStrings.PLUGIN_ELEMENT);
356 element.setAttribute(StaticStrings.TYPE_ATTRIBUTE, StaticStrings.SEPARATOR_ATTRIBUTE);
357 element.setAttribute(StaticStrings.SEPARATOR_ATTRIBUTE, StaticStrings.TRUE_STR);
358 separator_plugin = new Plugin(element, null);
359 ///atherer.println("Adding plugin separator at: " + separator_index);
360 add(separator_index, separator_plugin);
361 }
362
363 /** 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.
364 * @param plugin The <strong>Plugin</strong> to remove.
365 */
366 private void removePlugin(Plugin plugin) {
367 remove(plugin);
368
369 }
370
371
372 /** Inform the model to hide/show the last three lines on the list.
373 * @param modify_row_count true to hide the last three lines, false otherwise
374 */
375 private void setHideLines(boolean modify_row_count) {
376 this.modify_row_count = modify_row_count;
377 int original_size = super.getSize();
378 if(modify_row_count) {
379 fireIntervalRemoved(this, original_size - 4, original_size - 1);
380 }
381 else {
382 fireIntervalAdded(this, original_size - 4, original_size - 1);
383 }
384 }
385
386 /** Determine the current separator index. */
387 private int findSeparatorIndex() {
388 int separator_index = super.getSize() - 1;
389 while(separator_index >= 0) {
390 Plugin search = (Plugin) getElementAt(separator_index);
391 if(search.isSeparator()) {
392 return separator_index;
393 }
394 separator_index--;
395 }
396 return separator_index;
397 }
398
399
400 /** A class which provodes controls for assigned and editing plugins. */
401 private class PluginControl
402 extends JPanel
403 implements Control {
404 /** Button for adding plugins. */
405 private JButton add = null;
406 /** Button for configuring the selected plugin. */
407 private JButton configure = null;
408 /** Button to move an assigned plugin one position lower in the order. */
409 private JButton move_down_button = null;
410 /** Button to move an assigned plugin one position higher in the order. */
411 private JButton move_up_button = null;
412 /** Button to remove the selected plugin. */
413 private JButton remove = null;
414 /** A combobox containing all of the known plugins, including those that may have already been assigned. */
415 private JComboBox plugin_combobox = null;
416 /** The label next to the plugin combobox. */
417 private JLabel plugin_label = null;
418 /** The label above the assigned plugin list. */
419 private JLabel plugin_list_label = null;
420 /** A list of assigned plugins. */
421 private JList plugin_list = null;
422 /** The area where the add, configure and remove buttons are placed. */
423 private JPanel button_pane = null;
424 /** The region which divides the central portion of the view into list and controls */
425 private JPanel central_pane = null;
426 /** The area where movement buttons are placed. */
427 private JPanel movement_pane = null;
428 /** The small region containing the plugin combobox and its label. */
429 private JPanel plugin_pane = null;
430 /** The pane containing the assigned plugin list and its label. */
431 private JPanel plugin_list_pane = null;
432
433 /** Constructor.
434 */
435 public PluginControl()
436 {
437 // Create
438 add = new GLIButton(Dictionary.get("CDM.PlugInManager.Add"), Dictionary.get("CDM.PlugInManager.Add_Tooltip"));
439
440 button_pane = new JPanel();
441 central_pane = new JPanel();
442
443 configure = new GLIButton(Dictionary.get("CDM.PlugInManager.Configure"), Dictionary.get("CDM.PlugInManager.Configure_Tooltip"));
444 configure.setEnabled(false);
445
446 JPanel header_pane = new DesignPaneHeader("CDM.GUI.Plugins", "plugins");
447 move_up_button = new GLIButton(Dictionary.get("CDM.Move.Move_Up"), JarTools.getImage("arrow-up.gif"), Dictionary.get("CDM.Move.Move_Up_Tooltip"));
448 move_up_button.setEnabled(false);
449
450 move_down_button = new GLIButton(Dictionary.get("CDM.Move.Move_Down"), JarTools.getImage("arrow-down.gif"), Dictionary.get("CDM.Move.Move_Down_Tooltip"));
451 move_down_button.setEnabled(false);
452
453 movement_pane = new JPanel();
454
455 PluginComboboxListener picl = new PluginComboboxListener();
456 plugin_combobox = new JComboBox(getAvailablePlugins());
457 plugin_combobox.setOpaque(!Utility.isMac());
458 plugin_combobox.setEditable(false);
459 picl.itemStateChanged(new ItemEvent(plugin_combobox, 0, null, ItemEvent.SELECTED));
460
461 plugin_label = new JLabel(Dictionary.get("CDM.PlugInManager.PlugIn"));
462
463 plugin_list = new JList(model);
464 plugin_list.setOpaque(true);
465 plugin_list.setCellRenderer(new ListRenderer());
466 plugin_list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
467 plugin_list_label = new JLabel(Dictionary.get("CDM.PlugInManager.Assigned"));
468 //plugin_list_label.setHorizontalAlignment(JLabel.CENTER);
469 plugin_list_label.setOpaque(true);
470
471 plugin_list_pane = new JPanel();
472 plugin_pane = new JPanel();
473 plugin_pane.setOpaque(true);
474
475 remove = new GLIButton(Dictionary.get("CDM.PlugInManager.Remove"), Dictionary.get("CDM.PlugInManager.Remove_Tooltip"));
476 remove.setEnabled(false);
477
478 // Listeners
479 add.addActionListener(new AddListener());
480 configure.addActionListener(new ConfigureListener());
481 MoveListener ml = new MoveListener();
482 move_down_button.addActionListener(ml);
483 move_down_button.addActionListener(CollectionDesignManager.all_change_listener);
484 move_up_button.addActionListener(ml);
485 move_up_button.addActionListener(CollectionDesignManager.all_change_listener);
486 plugin_combobox.addItemListener(picl);
487 remove.addActionListener(new RemoveListener());
488 remove.addActionListener(CollectionDesignManager.all_change_listener);
489 plugin_list.addMouseListener(new ClickListener());
490 plugin_list.addListSelectionListener(new ListListener());
491 picl = null;
492
493 // Layout
494 movement_pane.setBorder(BorderFactory.createEmptyBorder(0,2,0,0));
495 movement_pane.setLayout(new GridLayout(4,1));
496 movement_pane.add(move_up_button);
497 movement_pane.add(new JPanel());
498 movement_pane.add(new JPanel());
499 movement_pane.add(move_down_button);
500
501 plugin_list_pane.setLayout(new BorderLayout());
502 plugin_list_pane.add(plugin_list_label, BorderLayout.NORTH);
503 plugin_list_pane.add(new JScrollPane(plugin_list), BorderLayout.CENTER);
504 modeChanged(Configuration.getMode()); // Whether the movement buttons are visible is mode dependant
505
506 plugin_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
507
508 plugin_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
509 plugin_pane.setLayout(new BorderLayout(5,0));
510 plugin_pane.add(plugin_label, BorderLayout.WEST);
511 plugin_pane.add(plugin_combobox, BorderLayout.CENTER);
512
513 button_pane.setLayout(new GridLayout(1,3));
514 button_pane.add(add);
515 button_pane.add(configure);
516 button_pane.add(remove);
517
518 // Scope these mad bordering skillz.
519 JPanel temp = new JPanel(new BorderLayout());
520 temp.add(plugin_pane, BorderLayout.NORTH);
521 temp.add(button_pane, BorderLayout.SOUTH);
522
523 central_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
524 central_pane.setLayout(new BorderLayout());
525 central_pane.add(plugin_list_pane, BorderLayout.CENTER);
526 central_pane.add(temp, BorderLayout.SOUTH);
527
528 setBorder(BorderFactory.createEmptyBorder(0,5,0,0));
529 setLayout(new BorderLayout());
530 add(header_pane, BorderLayout.NORTH);
531 add(central_pane, BorderLayout.CENTER);
532 }
533
534 /** Method which acts like a destructor, tidying up references to persistant objects.
535 */
536 public void destroy() {
537 }
538
539 /** This method is overridden to ensure the instructions are scrolled to top, before the super classes updateUI() is called.
540 no longer have instructions, do we still need updateUI???
541 */
542 public void gainFocus() {
543 super.updateUI();
544 }
545
546 public void loseFocus() {
547 }
548
549 /** 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
550 * @param mode the current mode as an int, which can be matched against static ints in the Configuration class
551 */
552 public void modeChanged(int mode) {
553 // 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
554 plugin_list.clearSelection();
555 // The first change is dependant on whether the user is systems mode or higher
556
557 plugin_combobox.setModel(new DefaultComboBoxModel(getAvailablePlugins()));
558
559 if(mode >= Configuration.SYSTEMS_MODE) {
560 // Show movement buttons
561
562 plugin_list_pane.add(movement_pane, BorderLayout.EAST);
563 // Do we show Arc and RecPlugs or hide them and the separator line
564 setHideLines(!(mode >= Configuration.EXPERT_MODE));
565 }
566 // Otherwise hide the movement buttons and fixed plugins
567 else {
568 plugin_list_pane.remove(movement_pane);
569 setHideLines(true);
570 }
571 plugin_list_pane.updateUI();
572 }
573
574
575 private class AddListener
576 implements ActionListener
577 {
578 public void actionPerformed(ActionEvent event)
579 {
580 if (plugin_combobox.getSelectedItem() != null) {
581 // This must be done on a new thread for the remote building code
582 new AddPluginTask(plugin_combobox.getSelectedItem().toString()).start();
583 }
584 }
585 }
586
587
588 private class AddPluginTask
589 extends Thread
590 {
591 private String plugin_name;
592
593 public AddPluginTask(String plugin_name)
594 {
595 this.plugin_name = plugin_name;
596 }
597
598 public void run()
599 {
600 // Retrieve the plugin
601 Plugin plugin = Plugins.getPlugin(plugin_name, true);
602 if (plugin == null) {
603 System.err.println("Error: getPlugin() returned null.");
604 return;
605 }
606
607 // Create a new element in the DOM
608 Element new_plugin_element = CollectionConfiguration.createElement(StaticStrings.PLUGIN_ELEMENT);
609 new_plugin_element.setAttribute(StaticStrings.TYPE_ATTRIBUTE, plugin.getName());
610 Plugin new_plugin = new Plugin(new_plugin_element, plugin);
611
612 ArgumentConfiguration ac = new ArgumentConfiguration(new_plugin);
613 ac.addOKButtonActionListener(CollectionDesignManager.all_change_listener);
614 if (ac.display()) {
615 assignPlugin(new_plugin);
616 plugin_list.setSelectedValue(new_plugin, true);
617
618 // Remove the plugin from the available list (unless we're in a high mode, or it's UnknownPlug)
619 if (Configuration.getMode() < Configuration.SYSTEMS_MODE && !plugin_name.equals(StaticStrings.UNKNOWNPLUG_STR)) {
620 plugin_combobox.removeItem(plugin);
621 plugin_combobox.setSelectedIndex(0);
622 }
623 }
624 }
625 }
626
627
628 /** Listens for double clicks apon the list and react as if the configure button was pushed. */
629 private class ClickListener
630 extends MouseAdapter {
631 /** Called whenever the mouse is clicked over a registered component, we use this to chain through to the configure prompt.
632 * @param event A <strong>MouseEvent</strong> containing information about the mouse click.
633 */
634 public void mouseClicked(MouseEvent event) {
635 if(event.getClickCount() == 2 ) {
636 if(!plugin_list.isSelectionEmpty()) {
637 Plugin plugin = (Plugin) plugin_list.getSelectedValue();
638 if(!plugin.isSeparator()) {
639 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
640 ac.addOKButtonActionListener(CollectionDesignManager.all_change_listener);
641 if (ac.display()) {
642 refresh(plugin);
643 }
644 ac.destroy();
645 ac = null;
646 }
647 }
648 }
649 }
650 }
651
652 /** 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.
653 * @see org.greenstone.gatherer.cdm.ArgumentConfiguration
654 */
655 private class ConfigureListener
656 implements ActionListener {
657 /** 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.
658 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
659 */
660 public void actionPerformed(ActionEvent event) {
661 if(!plugin_list.isSelectionEmpty()) {
662 Plugin plugin = (Plugin) plugin_list.getSelectedValue();
663 if(!plugin.isSeparator()) {
664 ArgumentConfiguration ac = new ArgumentConfiguration(plugin);
665 ac.addOKButtonActionListener(CollectionDesignManager.all_change_listener);
666 if (ac.display()) {
667 refresh(plugin);
668 }
669 ac.destroy();
670 ac = null;
671 }
672 }
673 }
674 }
675
676 /** 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 */
677 private class ListListener
678 implements ListSelectionListener {
679
680 public void valueChanged(ListSelectionEvent e) {
681 if (!e.getValueIsAdjusting()) { // we get two events for one change in list selection - use the false one (the second one)
682 if (plugin_list.isSelectionEmpty()) {
683 move_up_button.setEnabled(false);
684 move_down_button.setEnabled(false);
685 configure.setEnabled(false);
686 remove.setEnabled(false);
687 } else {
688 Plugin selected_plugin = (Plugin) plugin_list.getSelectedValue();
689 if(selected_plugin.isSeparator()) {
690 move_up_button.setEnabled(false);
691 move_down_button.setEnabled(false);
692 configure.setEnabled(false);
693 remove.setEnabled(false);
694 } else {
695 configure.setEnabled(true);
696 String plugin_name = selected_plugin.getName();
697 // Some buttons are only available for plugins other than ArcPlug and RecPlug
698 if(plugin_name.equals(StaticStrings.ARCPLUG_STR) || plugin_name.equals(StaticStrings.RECPLUG_STR) ) {
699 move_up_button.setEnabled(false);
700 move_down_button.setEnabled(false);
701 remove.setEnabled(false);
702 } else if (plugin_name.equals(StaticStrings.METADATAXMLPLUG_STR)) {
703 remove.setEnabled(false);
704 }
705
706 else {
707 // don't let people remove special plugins such GAPlug an METSPlug,
708 // unless they are in systems mode or above
709 int mode = Configuration.getMode();
710 for (int i=0; i<StaticStrings.KEEP_PLUG.length; i++) {
711 if ((plugin_name.equals(StaticStrings.KEEP_PLUG[i])) &&
712 (mode < Configuration.SYSTEMS_MODE)) {
713 remove.setEnabled(false);
714 break;
715 } else {
716 remove.setEnabled(true);
717 }
718 }
719
720 // Move ups are only enabled if the selected plugin isn't already at the top
721 Plugin first_plugin = (Plugin) getElementAt(0);
722 if(!first_plugin.equals(selected_plugin)) {
723 move_up_button.setEnabled(true);
724 } else {
725 move_up_button.setEnabled(false);
726 }
727 // And move downs are only allowed when the selected plugin isn't at an index one less than the separator line.
728 int separator_index = findSeparatorIndex();
729 int selected_index = plugin_list.getSelectedIndex();
730 if(selected_index < separator_index - 1) {
731 move_down_button.setEnabled(true);
732 } else {
733 move_down_button.setEnabled(false);
734 }
735 }
736 selected_plugin = null;
737 plugin_name = null;
738 }
739 }
740 }
741 }
742 }
743
744 /** A special list renderer which is able to render separating lines as well. */
745 private class ListRenderer
746 extends DefaultListCellRenderer {
747 /** 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.
748 * @param list - The <strong>JList</strong> we're painting.
749 * @param value - The value returned by list.getModel().getElementAt(index) as an <strong>Object</strong>.
750 * @param index - The cells index as an <i>int</i>.
751 * @param isSelected - <i>true</i> if the specified cell was selected.
752 * @param cellHasFocus - <i>true</i> if the specified cell has the focus.
753 * @return A <strong>Component</strong> whose paint() method will render the specified value.
754 * @see javax.swing.JList
755 * @see javax.swing.JSeparator
756 * @see javax.swing.ListModel
757 * @see javax.swing.ListSelectionModel
758 */
759 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
760 Plugin plugin = (Plugin) value;
761 if(plugin.isSeparator()) {
762 return separator;
763 }
764 else {
765 return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
766 }
767 }
768 }
769
770
771 /** 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. */
772 private class MoveListener
773 implements ActionListener {
774 /** 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.
775 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
776 */
777 public void actionPerformed(ActionEvent event) {
778 if (!plugin_list.isSelectionEmpty()) {
779 Object object = plugin_list.getSelectedValue();
780 if (object instanceof Plugin) {
781 Plugin plugin = (Plugin) object;
782 if (event.getSource() == move_up_button) {
783 movePlugin(plugin, true, false);
784 }
785 else if (event.getSource() == move_down_button) {
786 movePlugin(plugin, false, false);
787 }
788 plugin_list.setSelectedValue(plugin, true);
789 }
790 }
791 }
792 }
793
794 /** This listener reacts to changes in the current selection of the plugin combobox. */
795 private class PluginComboboxListener
796 implements ItemListener {
797 /** When a user selects a certain plugin, update the tooltip to show the plugin description. */
798 public void itemStateChanged(ItemEvent event) {
799 if(event.getStateChange() == ItemEvent.SELECTED) {
800 // Retrieve the selected plugin
801 Object current_selection = plugin_combobox.getSelectedItem();
802 // And reset the tooltip. If the plugin is null or is a string, then go back to the default message
803 if(current_selection == null || current_selection instanceof String) {
804 plugin_combobox.setToolTipText(Dictionary.get("CDM.PlugInManager.PlugIn_Tooltip"));
805 }
806 else {
807 Plugin current_plugin = (Plugin) current_selection;
808 plugin_combobox.setToolTipText(Utility.formatHTMLWidth(current_plugin.getDescription(), 40));
809 current_plugin = null;
810 }
811 current_selection = null;
812 }
813 }
814 }
815
816 /** This class listens for actions upon the remove button in the controls, and if detected calls the <i>removePlugin()</i> method.
817 */
818 private class RemoveListener
819 implements ActionListener {
820 /** 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.
821 * @param event An <strong>ActionEvent</strong> containing information garnered from the control action.
822 */
823 public void actionPerformed(ActionEvent event) {
824 int selected_index = plugin_list.getSelectedIndex();
825 if(selected_index != -1) {
826 Plugin selected_plugin = (Plugin) plugin_list.getSelectedValue();
827 removePlugin(selected_plugin);
828 selected_plugin = null;
829 // Refresh the available plugins
830 plugin_combobox.setModel(new DefaultComboBoxModel(getAvailablePlugins()));
831
832 // Select the next plugin if available
833 if(selected_index < plugin_list.getModel().getSize()) {
834 // If the new selection is above the separator we can remove it
835 if(selected_index < findSeparatorIndex()) {
836 plugin_list.setSelectedIndex(selected_index);
837
838 // don't let people remove special plugins such GAPlug an METSPlug,
839 // unless they are in systems mode or above
840 int mode = Configuration.getMode();
841 for (int i=0; i<StaticStrings.KEEP_PLUG.length; i++) {
842 String selected_plugin_name
843 = ((Plugin)plugin_list.getSelectedValue()).getName();
844
845 if (selected_plugin_name.equals(StaticStrings.METADATAXMLPLUG_STR)) {
846 //this plug cannot be removed under any conditions.
847 remove.setEnabled(false);
848 }else if ((selected_plugin_name.equals(StaticStrings.KEEP_PLUG[i])) &&
849 (mode < Configuration.SYSTEMS_MODE)) {
850 remove.setEnabled(false);
851 break;
852 } else {
853 remove.setEnabled(true);
854 }
855 }
856 }
857 // Otherwise select the first non-removable plugin
858 else {
859 plugin_list.setSelectedIndex(selected_index + 1);
860 remove.setEnabled(false);
861 }
862 } else {
863 remove.setEnabled(false);
864 }
865 } else {
866 remove.setEnabled(false);
867 }
868 }
869 }
870 }
871
872
873 private class PluginSuggestionPrompt
874 extends ModalDialog
875 implements ActionListener
876 {
877 private Dimension size = new Dimension(480, 240);
878 private GComboBox suitable_plugins_combobox = null;
879 private GLIButton add_button = null;
880 private GLIButton ignore_button = null;
881
882 public PluginSuggestionPrompt(String filename, ArrayList suitable_plugins)
883 {
884 super(Gatherer.g_man, true);
885 setModal(true);
886 setSize(size);
887 setTitle(Dictionary.get("CDM.PluginManager.SuggestedPluginListTitle"));
888
889 String[] args = new String[1];
890 args[0] = filename;
891
892 JTextArea instructions_textarea = new JTextArea(Dictionary.get("CDM.PluginManager.Plugin_Suggestion_Prompt", args));
893 instructions_textarea.setCaretPosition(0);
894 instructions_textarea.setEditable(false);
895 instructions_textarea.setLineWrap(true);
896 instructions_textarea.setRows(5);
897 instructions_textarea.setWrapStyleWord(true);
898
899 JLabel suitable_plugins_label = new JLabel(Dictionary.get("CDM.PlugInManager.PlugIn"));
900 suitable_plugins_label.setBorder(BorderFactory.createEmptyBorder(0,0,5,0));
901
902 suitable_plugins_combobox = new GComboBox(suitable_plugins);
903 suitable_plugins_combobox.setBackgroundNonSelectionColor(Configuration.getColor("coloring.editable_background", false));
904 suitable_plugins_combobox.setBackgroundSelectionColor(Configuration.getColor("coloring.collection_selection_background", false));
905 suitable_plugins_combobox.setTextNonSelectionColor(Configuration.getColor("coloring.workspace_tree_foreground", false));
906 suitable_plugins_combobox.setTextSelectionColor(Configuration.getColor("coloring.collection_selection_foreground", false));
907
908 JPanel suitable_plugins_pane = new JPanel();
909 //suitable_plugins_pane.setBorder(BorderFactory.createEmptyBorder(5,0,5,0));
910 suitable_plugins_pane.setLayout(new BorderLayout(5,0));
911 suitable_plugins_pane.add(suitable_plugins_label, BorderLayout.WEST);
912 suitable_plugins_pane.add(suitable_plugins_combobox, BorderLayout.CENTER);
913
914 add_button = new GLIButton(Dictionary.get("CDM.PlugInManager.QuickAdd"), Dictionary.get("CDM.PlugInManager.Add_Tooltip"));
915 ignore_button = new GLIButton(Dictionary.get("CDM.PlugInManager.Ignore"), Dictionary.get("CDM.PlugInManager.Ignore_Tooltip"));
916
917 add_button.addActionListener(this);
918 ignore_button.addActionListener(this);
919
920 JPanel button_pane = new JPanel();
921 button_pane.setLayout(new GridLayout(1,2,5,0));
922 button_pane.add(add_button);
923 button_pane.add(ignore_button);
924
925 JPanel controls_pane = new JPanel();
926 controls_pane.setBorder(BorderFactory.createEmptyBorder(5,0,0,0));
927 controls_pane.setLayout(new GridLayout(2,1,0,5));
928 controls_pane.add(suitable_plugins_pane);
929 controls_pane.add(button_pane);
930
931 JPanel content_pane = (JPanel) getContentPane();
932 content_pane.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
933 content_pane.setLayout(new BorderLayout());
934 content_pane.add(instructions_textarea, BorderLayout.CENTER);
935 content_pane.add(controls_pane, BorderLayout.SOUTH);
936
937 // Show
938 Dimension screen_size = Configuration.screen_size;
939 setLocation((screen_size.width - size.width) / 2, (screen_size.height - size.height) / 2);
940 setVisible(true);
941 }
942
943
944 public void actionPerformed(ActionEvent event)
945 {
946 // Close the dialog
947 setVisible(false);
948
949 if (event.getSource() == add_button) {
950 AddPluginWithoutConfigurationTask add_plugin_task = new AddPluginWithoutConfigurationTask(suitable_plugins_combobox.getSelectedItem().toString());
951 add_plugin_task.start();
952
953 // We have to wait for this task to complete before continuing, otherwise
954 // we might offer to add the same plugin again before the task has completed
955 try {
956 add_plugin_task.join();
957 }
958 catch (Exception exception) {
959 DebugStream.printStackTrace(exception);
960 }
961 }
962 }
963
964
965 private class AddPluginWithoutConfigurationTask
966 extends Thread
967 {
968 private String plugin_name;
969
970 public AddPluginWithoutConfigurationTask(String plugin_name)
971 {
972 this.plugin_name = plugin_name;
973 }
974
975 public void run()
976 {
977 // Retrieve the plugin
978 Plugin plugin = Plugins.getPlugin(plugin_name, true);
979 if (plugin == null) {
980 System.err.println("Error: getPlugin() returned null.");
981 return;
982 }
983
984 // Create a new element in the DOM
985 Element new_plugin_element = CollectionConfiguration.createElement(StaticStrings.PLUGIN_ELEMENT);
986 new_plugin_element.setAttribute(StaticStrings.TYPE_ATTRIBUTE, plugin.getName());
987 Plugin new_plugin = new Plugin(new_plugin_element, plugin);
988
989 // Just assign the plugin with no argument configuration
990 assignPlugin(new_plugin);
991 }
992 }
993 }
994
995
996 /** Creates a list separator.
997 * Found on Google Groups. Code courtesy of Paul Farwell.
998 */
999 private JPanel getSeparator() {
1000 // We put the separator inside a panel to control its appearance
1001 JPanel _sepPanel = new JPanel();
1002 _sepPanel.setOpaque(false);
1003 _sepPanel.setBorder(BorderFactory.createEmptyBorder(1, 3, 1, 3));
1004 _sepPanel.setLayout(new BoxLayout(_sepPanel, BoxLayout.Y_AXIS));
1005 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
1006 // 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
1007 _sepPanel.add(new BasicSeparator());
1008 _sepPanel.add(Box.createRigidArea(new Dimension(0, 4)));
1009 return _sepPanel;
1010 }
1011
1012 /** 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. */
1013 private static class BasicSeparator
1014 extends JSeparator {
1015
1016 private ComponentUI basic_ui;
1017
1018 public BasicSeparator() {
1019 super();
1020 basic_ui = new BasicSeparatorUI();
1021 }
1022
1023 public void paintComponent(Graphics g) {
1024 if (basic_ui != null) {
1025 basic_ui.update(g, this);
1026 }
1027 }
1028 }
1029}
Note: See TracBrowser for help on using the repository browser.