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

Last change on this file since 13398 was 13398, checked in by kjdon, 17 years ago

new warningdialog now takes a string for the title rather than a dictionary key

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