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

Last change on this file since 13594 was 13594, checked in by mdewsnip, 17 years ago

Moved the LocalGreenstone class into the "greenstone" package.

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