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

Last change on this file since 24367 was 24367, checked in by ak19, 13 years ago

Changes to GLI to do with embedded metadata: the plugin and changes to the processing of ex.metadata to deal with ex.something.metadata

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