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

Last change on this file since 12750 was 12749, checked in by mdewsnip, 18 years ago

Fixed some bugs in noticing what type of rebuild is required. This is all done completely wrong though -- it should be done at the CollectionConfiguration level, not at the GUI level.

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