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

Last change on this file since 13074 was 12808, checked in by mdewsnip, 18 years ago

Removed CollectionManager.configurationChanged() and all 50 calls to it. The CollectionConfiguration class now works out itself whether it needs to be changed or not -- a far more reliable approach.

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