PluginManagerUI.java
001 /*
002  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
003  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
004  *  Copyright (c) 2009, Ontotext AD.
005  *
006  *  This file is part of GATE (see http://gate.ac.uk/), and is free
007  *  software, licenced under the GNU Library General Public License,
008  *  Version 2, June 1991 (in the distribution as file licence.html,
009  *  and also available at http://gate.ac.uk/gate/licence.html).
010  *
011  *  PluginManagerUI.java
012  *
013  *  Valentin Tablan, 21-Jul-2004
014  *
015  *  $Id: PluginManagerUI.java 13565 2011-03-26 23:03:34Z johann_p $
016  */
017 
018 package gate.gui;
019 
020 import java.awt.*;
021 import java.awt.event.*;
022 import java.io.File;
023 import java.net.MalformedURLException;
024 import java.net.URISyntaxException;
025 import java.net.URL;
026 import java.util.*;
027 import java.util.Timer;
028 import java.util.List;
029 import java.text.Collator;
030 import java.util.logging.Level;
031 import java.util.logging.Logger;
032 import javax.swing.*;
033 import javax.swing.border.TitledBorder;
034 import javax.swing.event.ListSelectionEvent;
035 import javax.swing.event.ListSelectionListener;
036 import javax.swing.event.DocumentListener;
037 import javax.swing.event.DocumentEvent;
038 import javax.swing.table.*;
039 import gate.Gate;
040 import gate.GateConstants;
041 import gate.swing.XJTable;
042 import gate.swing.XJFileChooser;
043 import gate.util.*;
044 
045 /**
046  * This is the user interface used for plugins management.
047  */
048 public class PluginManagerUI extends JDialog implements GateConstants{
049   
050   public PluginManagerUI(Frame owner){
051     super(owner);
052     initLocalData();
053     initGUI();
054     initListeners();
055   }
056   
057   
058   protected void initLocalData(){
059     loadNowByURL = new HashMap<URL, Boolean>();
060     loadAlwaysByURL = new HashMap<URL, Boolean>();
061     visibleRows = new ArrayList<URL>(Gate.getKnownPlugins());
062   }
063   
064   protected void initGUI(){
065     setTitle("Plugin Management Console");
066     JPanel leftPanel = new JPanel(new BorderLayout());
067 
068     JPanel leftTopPanel = new JPanel(new BorderLayout());
069     JLabel titleLabel = new JLabel("Known CREOLE directories");
070     titleLabel.setBorder(BorderFactory.createEmptyBorder(00040));
071     leftTopPanel.add(titleLabel, BorderLayout.WEST);
072     JPanel leftTopCenterPanel = new JPanel(new BorderLayout());
073     leftTopCenterPanel.add(new JLabel("Filter:"), BorderLayout.WEST);
074     filterTextField = new JTextField();
075     filterTextField.setToolTipText("Type some text to filter the table rows.");
076     leftTopCenterPanel.add(filterTextField, BorderLayout.CENTER);
077     JButton clearFilterButton = new JButton(
078       new AbstractAction("", MainFrame.getIcon("exit.gif")) {
079       this.putValue(MNEMONIC_KEY, KeyEvent.VK_BACK_SPACE);
080         this.putValue(SHORT_DESCRIPTION, "Clear text field")}
081       public void actionPerformed(ActionEvent e) {
082         filterTextField.setText("");
083         filterTextField.requestFocusInWindow();
084       }
085     });
086     clearFilterButton.setBorder(BorderFactory.createEmptyBorder());
087     clearFilterButton.setIconTextGap(0);
088     leftTopCenterPanel.add(clearFilterButton, BorderLayout.EAST);
089     leftTopPanel.add(leftTopCenterPanel, BorderLayout.CENTER);
090     leftPanel.add(leftTopPanel, BorderLayout.NORTH);
091 
092     mainTableModel = new MainTableModel();
093     mainTable = new XJTable();
094     mainTable.setTabSkipUneditableCell(true);
095     mainTable.setModel(mainTableModel);
096     mainTable.setSortedColumn(NAME_COLUMN);
097     Collator collator = Collator.getInstance(Locale.ENGLISH);
098     collator.setStrength(Collator.TERTIARY);
099     mainTable.setComparator(NAME_COLUMN, collator);
100     mainTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
101     DeleteColumnCellRendererEditor rendererEditor =
102       new DeleteColumnCellRendererEditor();
103     mainTable.getColumnModel().getColumn(DELETE_COLUMN).
104       setCellEditor(rendererEditor);
105     mainTable.getColumnModel().getColumn(DELETE_COLUMN).
106       setCellRenderer(rendererEditor);
107     mainTable.getColumnModel().getColumn(ICON_COLUMN).
108       setCellRenderer(new IconTableCellRenderer());
109 
110     resourcesListModel = new ResourcesListModel();
111     resourcesList = new JList(resourcesListModel);
112     resourcesList.setCellRenderer(new ResourcesListCellRenderer());
113     resourcesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
114     //enable tooltips
115     ToolTipManager.sharedInstance().registerComponent(resourcesList);
116     
117     mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
118     mainSplit.setResizeWeight(0.70);
119     mainSplit.setContinuousLayout(true);
120     JScrollPane scroller = new JScrollPane(mainTable);
121     leftPanel.add(scroller, BorderLayout.CENTER);
122     mainSplit.setLeftComponent(leftPanel);
123     
124     scroller = new JScrollPane(resourcesList);
125     scroller.setBorder(BorderFactory.createTitledBorder(
126             scroller.getBorder()
127             "CREOLE resources in directory",
128             TitledBorder.LEFT, TitledBorder.ABOVE_TOP));
129     mainSplit.setRightComponent(scroller);
130     
131     getContentPane().setLayout(new GridBagLayout());
132     GridBagConstraints constraints = new GridBagConstraints();
133     constraints.insets = new Insets(2222);
134     constraints.fill = GridBagConstraints.BOTH;
135     constraints.anchor = GridBagConstraints.WEST;
136     constraints.gridy = 0;
137     constraints.weightx = 1;
138     constraints.weighty = 1;
139     getContentPane().add(mainSplit, constraints);
140     
141     constraints.gridy = 1;
142     constraints.weighty = 0;
143     Box hBox = Box.createHorizontalBox();
144     hBox.add(new JButton(new AddCreoleRepositoryAction()));
145     hBox.add(Box.createHorizontalGlue());
146     getContentPane().add(hBox, constraints);
147     
148     constraints.gridy = 2;
149     constraints.anchor = GridBagConstraints.CENTER;
150     constraints.fill = GridBagConstraints.NONE;
151     hBox = Box.createHorizontalBox();
152     JButton okButton = new JButton(new OkAction());
153     hBox.add(okButton);
154     hBox.add(Box.createHorizontalStrut(5));
155     hBox.add(new JButton(new CancelAction()));
156     hBox.add(Box.createHorizontalStrut(5));
157     hBox.add(new JButton(new HelpAction()));
158     constraints.insets = new Insets(2282);
159     getContentPane().add(hBox, constraints);
160     getRootPane().setDefaultButton(okButton);
161   }
162   
163   protected void initListeners(){
164     mainTable.getSelectionModel().addListSelectionListener(
165       new ListSelectionListener(){
166      public void valueChanged(ListSelectionEvent e){
167        if (!e.getValueIsAdjusting()) {
168         resourcesListModel.dataChanged();
169        }
170      }
171     });
172 
173     // when typing a character in the table, use it for filtering
174     mainTable.addKeyListener(new KeyAdapter() {
175       public void keyTyped(KeyEvent e) {
176         if (e.getKeyChar() != KeyEvent.VK_TAB
177          && e.getKeyChar() != KeyEvent.VK_SPACE
178          && e.getKeyChar() != KeyEvent.VK_BACK_SPACE
179          && e.getKeyChar() != KeyEvent.VK_DELETE) {
180           filterTextField.requestFocusInWindow();
181           filterTextField.setText(String.valueOf(e.getKeyChar()));
182         }
183       }
184     });
185 
186     addComponentListener(new ComponentAdapter(){
187       public void componentShown(ComponentEvent e){
188         SwingUtilities.invokeLater(new Runnable() { public void run() {
189           mainSplit.setDividerLocation(0.8);
190         }});
191       }
192     });
193 
194     // show only the rows containing the text from filterTextField
195     filterTextField.getDocument().addDocumentListener(new DocumentListener() {
196       private Timer timer = new Timer("Plugin manager table rows filter"true);
197       private TimerTask timerTask;
198       public void changedUpdate(DocumentEvent e) { /* do nothing */ }
199       public void insertUpdate(DocumentEvent e) { update()}
200       public void removeUpdate(DocumentEvent e) { update()}
201       private void update() {
202         if (timerTask != null) { timerTask.cancel()}
203         Date timeToRun = new Date(System.currentTimeMillis() 300);
204         timerTask = new TimerTask() { public void run() {
205           filterRows(filterTextField.getText());
206         }};
207         // add a delay
208         timer.schedule(timerTask, timeToRun);
209       }
210     });
211 
212     // Up/Down key events in filterTextField are transferred to the table
213     filterTextField.addKeyListener(new KeyAdapter() {
214       public void keyPressed(KeyEvent e) {
215         if (e.getKeyCode() == KeyEvent.VK_UP
216          || e.getKeyCode() == KeyEvent.VK_DOWN
217          || e.getKeyCode() == KeyEvent.VK_PAGE_UP
218          || e.getKeyCode() == KeyEvent.VK_PAGE_DOWN) {
219           mainTable.dispatchEvent(e);
220         }
221       }
222     });
223 
224     // disable Enter key in the table so this key will confirm the dialog
225     InputMap inputMap = mainTable.getInputMap(
226       JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
227     KeyStroke enter = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0);
228     inputMap.put(enter, "none");
229 
230     // define keystrokes action bindings at the level of the main window
231     inputMap = ((JComponent)this.getContentPane())
232       .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
233     ActionMap actionMap = ((JComponent)this.getContentPane()).getActionMap();
234     inputMap.put(KeyStroke.getKeyStroke("ENTER")"Apply");
235     actionMap.put("Apply"new OkAction());
236     inputMap.put(KeyStroke.getKeyStroke("ESCAPE")"Cancel");
237     actionMap.put("Cancel"new CancelAction());
238     inputMap.put(KeyStroke.getKeyStroke("F1")"Help");
239     actionMap.put("Help"new HelpAction());
240   }
241 
242   private void filterRows(String rowFilter) {
243     final String filter = rowFilter.trim().toLowerCase();
244     final String previousURL = mainTable.getSelectedRow() == -"" :
245       (StringmainTable.getValueAt(mainTable.getSelectedRow(),
246         mainTable.convertColumnIndexToView(URL_COLUMN));
247     ArrayList<URL> previousVisibleRows = new ArrayList<URL>(visibleRows);
248     if (filter.length() 2) {
249       // one character or less, don't filter rows
250       visibleRows = new ArrayList<URL>(Gate.getKnownPlugins());
251     else {
252       // filter rows case insensitively on each plugin URL and its resources
253       visibleRows.clear();
254       for (int i = 0; i < Gate.getKnownPlugins().size(); i++) {
255         Gate.DirectoryInfo dInfo = Gate.getDirectoryInfo(
256           Gate.getKnownPlugins().get(i));
257         String url = dInfo.getUrl().toString();
258         String resources = "";
259         for (int j = 0; j < dInfo.getResourceInfoList().size(); j++) {
260           resources += dInfo.getResourceInfoList().get(j).getResourceName()
261             " ";
262         }
263         if (url.toLowerCase().contains(filter)
264          || resources.toLowerCase().contains(filter)) {
265           visibleRows.add(Gate.getKnownPlugins().get(i));
266         }
267       }
268     }
269     if (!previousVisibleRows.equals(visibleRows)) {
270       mainTableModel.fireTableDataChanged();
271     }
272     if (mainTable.getRowCount() 0) {
273       SwingUtilities.invokeLater(new Runnable() { public void run() {
274       mainTable.setRowSelectionInterval(00);
275       if (filter.length() 2
276        && previousURL != null
277        && !previousURL.equals("")) {
278         // reselect the last selected row based on its name and url values
279         for (int row = 0; row < mainTable.getRowCount(); row++) {
280           String url = (StringmainTable.getValueAt(
281             row, mainTable.convertColumnIndexToView(URL_COLUMN));
282           if (url.equals(previousURL)) {
283             mainTable.setRowSelectionInterval(row, row);
284             mainTable.scrollRectToVisible(
285               mainTable.getCellRect(row, 0true));
286             break;
287           }
288         }
289       }
290       }});
291     }
292   }
293 
294   protected Boolean getLoadNow(URL url){
295     Boolean res = loadNowByURL.get(url);
296     if(res == null){
297       res = Gate.getCreoleRegister().getDirectories().contains(url);
298       loadNowByURL.put(url, res);
299     }
300     return res;
301   }
302   
303   protected Boolean getLoadAlways(URL url){
304     Boolean res = loadAlwaysByURL.get(url);
305     if(res == null){
306       res = Gate.getAutoloadPlugins().contains(url);
307       loadAlwaysByURL.put(url, res);
308     }
309     return res;
310   }
311   
312   protected class MainTableModel extends AbstractTableModel{
313     public MainTableModel(){
314       localIcon = MainFrame.getIcon("open-file");
315       remoteIcon = MainFrame.getIcon("internet");
316       invalidIcon = MainFrame.getIcon("param");
317     }
318     public int getRowCount(){
319       return visibleRows.size();
320     }
321     
322     public int getColumnCount(){
323       return 6;
324     }
325     
326     public String getColumnName(int column){
327       switch (column){
328         case NAME_COLUMN: return "Name";
329         case ICON_COLUMN: return "";
330         case URL_COLUMN: return "URL";
331         case LOAD_NOW_COLUMN: return "Load now";
332         case LOAD_ALWAYS_COLUMN: return "Load always";
333         case DELETE_COLUMN: return "Delete";
334         defaultreturn "?";
335       }
336     }
337     
338     public Class getColumnClass(int columnIndex){
339       switch (columnIndex){
340         case NAME_COLUMN: return String.class;
341         case ICON_COLUMN: return Icon.class;
342         case URL_COLUMN: return String.class;
343         case LOAD_NOW_COLUMN: return Boolean.class;
344         case LOAD_ALWAYS_COLUMN: return Boolean.class;
345         case DELETE_COLUMN: return Object.class;
346         defaultreturn Object.class;
347       }
348     }
349     
350     public Object getValueAt(int row, int column){
351       Gate.DirectoryInfo dInfo = Gate.getDirectoryInfo(visibleRows.get(row));
352       if (dInfo == null) { return null}
353       switch (column){
354         case NAME_COLUMN: return getName4URL(dInfo.getUrl());
355         case ICON_COLUMN: return
356           dInfo.isValid() (
357             dInfo.getUrl().getProtocol().equalsIgnoreCase("file"
358             localIcon : remoteIcon:
359           invalidIcon;
360         case URL_COLUMN: return dInfo.getUrl().toString();
361         case LOAD_NOW_COLUMN: return getLoadNow(dInfo.getUrl());
362         case LOAD_ALWAYS_COLUMN: return getLoadAlways(dInfo.getUrl());
363         case DELETE_COLUMN: return null;
364         defaultreturn null;
365       }
366     }
367 
368     private String getName4URL(URL url) {
369       String path = "";
370       try {
371         path = url.toURI().getPath();
372       catch (URISyntaxException ex) {
373         // ignore, this should have been checked when adding the URL!
374       }
375       if(path.endsWith("/")) {
376         path = path.substring(0,path.length()-1);
377       }
378       int lastSlash = path.lastIndexOf("/");
379       if(lastSlash == -1) {
380         return path;
381       else {
382         return path.substring(lastSlash+1);
383       }
384     }
385 
386     public boolean isCellEditable(int rowIndex, int columnIndex){
387       return columnIndex == LOAD_NOW_COLUMN || 
388         columnIndex == LOAD_ALWAYS_COLUMN ||
389         columnIndex == DELETE_COLUMN;
390     }
391     
392     public void setValueAt(Object aValue, int rowIndex, int columnIndex){
393       Boolean valueBoolean = (Boolean)aValue;
394       Gate.DirectoryInfo dInfo =
395         Gate.getDirectoryInfo(visibleRows.get(rowIndex));
396       if (dInfo == null) { return}
397       switch(columnIndex){
398         case LOAD_NOW_COLUMN: 
399           loadNowByURL.put(dInfo.getUrl(), valueBoolean);
400           // for some reason the focus is sometime lost after editing
401           // however it is needed for Enter key to execute OkAction
402           mainTable.requestFocusInWindow();
403           break;
404         case LOAD_ALWAYS_COLUMN:
405           loadAlwaysByURL.put(dInfo.getUrl(), valueBoolean);
406           mainTable.requestFocusInWindow();
407           break;
408       }
409     }
410     
411     protected Icon localIcon;
412     protected Icon remoteIcon;
413     protected Icon invalidIcon;
414   }
415   
416   protected class ResourcesListModel extends AbstractListModel{
417 
418     public Object getElementAt(int index){
419       int row = mainTable.getSelectedRow();
420       if(row == -1return null;
421       row = mainTable.rowViewToModel(row);
422       Gate.DirectoryInfo dInfo = Gate.getDirectoryInfo(visibleRows.get(row));
423       return dInfo.getResourceInfoList().get(index);
424     }
425     
426     public int getSize(){
427       int row = mainTable.getSelectedRow();
428       if(row == -1return 0;
429       row = mainTable.rowViewToModel(row);
430       Gate.DirectoryInfo dInfo = Gate.getDirectoryInfo(visibleRows.get(row));
431       if (dInfo == null) { return 0}
432       return dInfo.getResourceInfoList().size();
433     }
434     
435     public void dataChanged(){
436       fireContentsChanged(this, 0, getSize() 1);
437     }
438   }
439   
440   /**
441    * This class acts both as cell renderer  and editor for all the cells in the 
442    * delete column.
443    */
444   protected class DeleteColumnCellRendererEditor extends AbstractCellEditor 
445     implements TableCellRenderer, TableCellEditor{
446     
447     public DeleteColumnCellRendererEditor(){
448       label = new JLabel();
449       rendererDeleteButton = new JButton(MainFrame.getIcon("delete"));
450       rendererDeleteButton.setMaximumSize(rendererDeleteButton.getPreferredSize());
451       rendererDeleteButton.setMargin(new Insets(2525));
452       rendererBox = new JPanel();
453       rendererBox.setLayout(new GridBagLayout());
454       rendererBox.setOpaque(false);
455       GridBagConstraints constraints = new GridBagConstraints();
456       constraints.fill = GridBagConstraints.NONE;
457       constraints.gridy = 0;
458       constraints.gridx = GridBagConstraints.RELATIVE;
459       constraints.weightx = 1;
460       rendererBox.add(Box.createGlue(), constraints);
461       constraints.weightx = 0;
462       rendererBox.add(rendererDeleteButton, constraints);
463       constraints.weightx = 1;
464       rendererBox.add(Box.createGlue(), constraints);
465       
466       editorDeleteButton = new JButton(MainFrame.getIcon("delete"));
467       editorDeleteButton.setMargin(new Insets(2525));
468       editorDeleteButton.addActionListener(new ActionListener(){
469         public void actionPerformed(ActionEvent evt){
470           int row = mainTable.getEditingRow();
471           // tell Swing that we aren't really editing this cell, otherwise an
472           // exception occurs when Swing tries to stop editing a cell that has
473           // been deleted.
474           TableCellEditor currentEditor = mainTable.getCellEditor();
475           if(currentEditor != null) {
476             currentEditor.cancelCellEditing();
477           }
478           int rowModel = mainTable.rowViewToModel(row);
479           URL toDelete = visibleRows.get(rowModel);
480           Gate.removeKnownPlugin(toDelete);
481           loadAlwaysByURL.remove(toDelete);
482           loadNowByURL.remove(toDelete);
483           // redisplay the table with the current filter
484           filterRows(filterTextField.getText());
485         }
486       });
487       editorDeleteButton.setMaximumSize(editorDeleteButton.getPreferredSize());
488       editorBox = new JPanel();
489       editorBox.setLayout(new GridBagLayout());
490       editorBox.setOpaque(false);
491       constraints.weightx = 1;
492       editorBox.add(Box.createGlue(), constraints);
493       constraints.weightx = 0;
494       editorBox.add(editorDeleteButton, constraints);
495       constraints.weightx = 1;
496       editorBox.add(Box.createGlue(), constraints);
497     }
498     
499     public Component getTableCellRendererComponent(JTable table,
500             Object value,
501             boolean isSelected,
502             boolean hasFocus,
503             int row,
504             int column){
505       switch(column){
506         case DELETE_COLUMN:
507           return rendererBox;
508         defaultreturn null;
509       }
510     }
511     
512     public Component getTableCellEditorComponent(JTable table,
513             Object value,
514             boolean isSelected,
515             int row,
516             int column){
517       switch(column){
518         case DELETE_COLUMN:
519           return editorBox;
520         defaultreturn null;
521       }
522     }
523     
524     public Object getCellEditorValue(){
525       return null;
526     }
527     
528     JButton editorDeleteButton;
529     JButton rendererDeleteButton;
530     JPanel rendererBox;
531     JPanel editorBox;
532     JLabel label;
533   }
534   
535   protected class IconTableCellRenderer extends DefaultTableCellRenderer{
536     public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
537       if(value instanceof Icon){
538         super.getTableCellRendererComponent(table, ""
539                 isSelected, hasFocus, row, column);
540         setIcon((Icon)value);
541         return this;
542       }else{
543         return super.getTableCellRendererComponent(table, value, 
544                 isSelected, hasFocus, row, column);
545       }
546     }
547     
548   }
549   
550   protected class ResourcesListCellRenderer extends DefaultListCellRenderer{
551     public Component getListCellRendererComponent(JList list, Object value,
552         int index, boolean isSelected, boolean cellHasFocus){
553       Gate.ResourceInfo rInfo = (Gate.ResourceInfo)value;
554       //prepare the renderer
555       String filter = filterTextField.getText().trim().toLowerCase();
556       if (filter.length() 1
557       && rInfo.getResourceName().toLowerCase().contains(filter)) {
558         isSelected = true// select resource if matching table row filter
559       }
560       super.getListCellRendererComponent(list, rInfo.getResourceName(),
561                                          index, isSelected, cellHasFocus);
562       //add tooltip text
563       setToolTipText(rInfo.getResourceComment());
564       return this;
565     }
566   }
567   
568   protected class OkAction extends AbstractAction {
569     public OkAction(){
570       super("OK");
571     }
572     public void actionPerformed(ActionEvent evt){
573       setVisible(false);
574       //update the data structures to reflect the user's choices
575       Iterator pluginIter = loadNowByURL.keySet().iterator();
576       while(pluginIter.hasNext()){
577         URL aPluginURL = (URL)pluginIter.next();
578         boolean load = loadNowByURL.get(aPluginURL);
579         boolean loaded = Gate.getCreoleRegister().
580             getDirectories().contains(aPluginURL)
581         if(load && !loaded){
582           //load the directory
583           try{
584             Gate.getCreoleRegister().registerDirectories(aPluginURL);
585           }catch(GateException ge){
586             throw new GateRuntimeException(ge);
587           }
588         }
589         if(!load && loaded){
590           //remove the directory
591           Gate.getCreoleRegister().removeDirectory(aPluginURL);
592         }
593       }
594       
595       
596       pluginIter = loadAlwaysByURL.keySet().iterator();
597       while(pluginIter.hasNext()){
598         URL aPluginURL = (URL)pluginIter.next();
599         boolean load = loadAlwaysByURL.get(aPluginURL);
600         boolean loaded = Gate.getAutoloadPlugins().contains(aPluginURL)
601         if(load && !loaded){
602           //set autoload top true
603           Gate.addAutoloadPlugin(aPluginURL);
604         }
605         if(!load && loaded){
606           //set autoload to false
607           Gate.removeAutoloadPlugin(aPluginURL);
608         }
609       }
610       loadNowByURL.clear();
611       loadAlwaysByURL.clear();
612     }
613   }
614   
615   /**
616    * Overridden so we can populate the UI before showing.
617    */
618   public void setVisible(boolean visible){
619     if(visible){
620       loadNowByURL.clear();
621       loadAlwaysByURL.clear();      
622       mainTableModel.fireTableDataChanged();
623       if (mainTable.getRowCount() 0) {
624         // select the first row
625         mainTable.setRowSelectionInterval(00);
626         mainTable.scrollRectToVisible(
627           mainTable.getCellRect(00true));
628       }
629     else {
630       // clear the filter
631       filterTextField.setText("");
632     }
633     super.setVisible(visible);
634   }
635 
636   protected class CancelAction extends AbstractAction {
637     public CancelAction(){
638       super("Cancel");
639     }
640     public void actionPerformed(ActionEvent evt){
641       setVisible(false);
642       loadNowByURL.clear();
643       loadAlwaysByURL.clear();      
644     }
645   }
646 
647   protected class HelpAction extends AbstractAction {
648     public HelpAction() {
649       super("Help");
650     }
651     public void actionPerformed(ActionEvent evt) {
652       MainFrame.getInstance().showHelpFrame(
653         "sec:howto:plugins""gate.gui.PluginManagerUI");
654     }
655   }
656 
657   protected class AddCreoleRepositoryAction extends AbstractAction {
658     public AddCreoleRepositoryAction(){
659       super("Add a CREOLE repository",
660         MainFrame.getIcon("crystal-clear-action-edit-add.png"));
661       putValue(SHORT_DESCRIPTION,"Load a new CREOLE repository");
662     }
663 
664     public void actionPerformed(ActionEvent e) {
665       Box messageBox = Box.createHorizontalBox();
666       Box leftBox = Box.createVerticalBox();
667       JTextField urlTextField = new JTextField(20);
668       leftBox.add(new JLabel("Type an URL"));
669       leftBox.add(urlTextField);
670       messageBox.add(leftBox);
671 
672       messageBox.add(Box.createHorizontalStrut(10));
673       messageBox.add(new JLabel("or"));
674       messageBox.add(Box.createHorizontalStrut(10));
675 
676       class URLfromFileAction extends AbstractAction{
677         URLfromFileAction(JTextField textField){
678           super(null, MainFrame.getIcon("open-file"));
679           putValue(SHORT_DESCRIPTION,"Click to select a directory");
680           this.textField = textField;
681         }
682 
683         public void actionPerformed(ActionEvent e){
684           XJFileChooser fileChooser = MainFrame.getFileChooser();
685           fileChooser.setMultiSelectionEnabled(false);
686           fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
687           fileChooser.setFileFilter(fileChooser.getAcceptAllFileFilter());
688           fileChooser.setResource("gate.CreoleRegister");
689           int result = fileChooser.showOpenDialog(PluginManagerUI.this);
690           if(result == JFileChooser.APPROVE_OPTION){
691             try{
692               textField.setText(fileChooser.getSelectedFile().
693                                             toURI().toURL().toExternalForm());
694             }catch(MalformedURLException mue){
695               throw new GateRuntimeException(mue.toString());
696             }
697           }
698         }
699         JTextField textField;
700       //class URLfromFileAction extends AbstractAction
701 
702       Box rightBox = Box.createVerticalBox();
703       rightBox.add(new JLabel("Select a directory"));
704       JButton fileBtn = new JButton(new URLfromFileAction(urlTextField));
705       rightBox.add(fileBtn);
706       messageBox.add(rightBox);
707 
708       int res = JOptionPane.showOptionDialog(PluginManagerUI.this, messageBox,
709         "Enter an URL to the directory containing the \"creole.xml\" file",
710         JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null);
711       if(res == JOptionPane.OK_OPTION){
712         try{
713           final URL creoleURL = new URL(urlTextField.getText());
714           Gate.addKnownPlugin(creoleURL);
715           mainTable.clearSelection();
716           // redisplay the table without filtering
717           filterRows("");
718           // clear the filter text field
719           filterTextField.setText("");
720           // select the new plugin row
721           SwingUtilities.invokeLater(new Runnable() { public void run() {
722             for (int row = 0; row < mainTable.getRowCount(); row++) {
723               String url = (StringmainTable.getValueAt(
724                 row, mainTable.convertColumnIndexToView(URL_COLUMN));
725               if (url.equals(creoleURL.toString())) {
726                 mainTable.setRowSelectionInterval(row, row);
727                 mainTable.scrollRectToVisible(
728                   mainTable.getCellRect(row, 0true));
729                 break;
730               }
731             }
732           }});
733           mainTable.requestFocusInWindow();
734         }catch(Exception ex){
735           JOptionPane.showMessageDialog(
736               PluginManagerUI.this,
737               "There was a problem with your selection:\n" +
738               ex.toString() ,
739               "GATE", JOptionPane.ERROR_MESSAGE);
740           ex.printStackTrace(Err.getPrintWriter());
741         }
742       }
743     }
744   }//class LoadCreoleRepositoryAction extends AbstractAction
745 
746   protected XJTable mainTable;
747   /** Contains the URLs from Gate.getKnownPlugins() that satisfy the filter
748    * filterTextField for the plugin URL and the plugin resources names */
749   protected List<URL> visibleRows;
750   protected JSplitPane mainSplit;
751   protected MainTableModel mainTableModel;
752   protected ResourcesListModel resourcesListModel;
753   protected JList resourcesList; 
754   protected JTextField filterTextField;
755 
756   /**
757    * Map from URL to Boolean. Stores temporary values for the loadNow options.
758    */
759   protected Map<URL, Boolean> loadNowByURL;
760   /**
761    * Map from URL to Boolean. Stores temporary values for the loadAlways 
762    * options.
763    */
764   protected Map<URL, Boolean> loadAlwaysByURL;
765  
766   protected static final int ICON_COLUMN = 0;
767   protected static final int NAME_COLUMN = 1;
768   protected static final int URL_COLUMN = 2;
769   protected static final int LOAD_NOW_COLUMN = 3;
770   protected static final int LOAD_ALWAYS_COLUMN = 4;
771   protected static final int DELETE_COLUMN = 5;
772 }