1   /*
2    *  Copyright (c) 1998-2005, The University of Sheffield.
3    *
4    *  This file is part of GATE (see http://gate.ac.uk/), and is free
5    *  software, licenced under the GNU Library General Public License,
6    *  Version 2, June 1991 (in the distribution as file licence.html,
7    *  and also available at http://gate.ac.uk/gate/licence.html).
8    *
9    *  Valentin Tablan 16/10/2001
10   *
11   *  $Id: ListEditorDialog.java,v 1.11 2006/03/09 13:33:19 ian_roberts Exp $
12   *
13   */
14  
15  package gate.gui;
16  
17  import java.awt.Component;
18  import java.awt.event.ActionEvent;
19  import java.awt.event.ActionListener;
20  import java.util.*;
21  
22  import javax.swing.*;
23  
24  import gate.Gate;
25  import gate.creole.ResourceData;
26  import gate.util.*;
27  
28  /**
29   * A simple editor for List values.
30   */
31  public class ListEditorDialog extends JDialog {
32  
33    /**
34     * Contructs a new ListEditorDialog.
35     * @param owner the component this dialog will be centred on.
36     * @param data a list with the initial values. This list will not be changed,
37     * its values will be cached and if the user selects the OK option a new list
38     * with the updated contents will be returned.
39     * @param itemType the type of the elements in the list in the form of a
40     * fully qualified class name
41     */
42    public ListEditorDialog(Component owner, List data, String itemType) {
43      this.itemType = itemType == null ? "java.lang.String" : itemType;
44      setLocationRelativeTo(owner);
45      initLocalData(data);
46      initGuiComponents();
47      initListeners();
48    }
49  
50    protected void initLocalData(List data){
51      listModel = new DefaultListModel();
52      if(data != null){
53        Iterator elemIter = data.iterator();
54        while(elemIter.hasNext()){
55          listModel.addElement(elemIter.next());
56        }
57      }
58  
59      try{
60        ResourceData rData = (ResourceData)Gate.getCreoleRegister().get(itemType);
61        itemTypeClass = rData == null ?
62                        Class.forName(itemType, true, Gate.getClassLoader()) :
63                        rData.getResourceClass();
64      }catch(ClassNotFoundException cnfe){
65        throw new GateRuntimeException(cnfe.toString());
66      }
67  
68      finiteType = Gate.isGateType(itemType);
69  
70      ResourceData rData = (ResourceData)Gate.getCreoleRegister().get(itemType);
71      setTitle("List of " + ((rData== null) ? itemType :rData.getName()));
72  
73      addAction = new AddAction();
74      removeAction = new RemoveAction();
75    }
76  
77    protected void initGuiComponents(){
78      getContentPane().setLayout(new BoxLayout(getContentPane(),
79                                               BoxLayout.Y_AXIS));
80  
81      //the editor component
82      JComponent editComp = null;
83      if(finiteType){
84        editComp = combo = new JComboBox(new ResourceComboModel());
85        combo.setRenderer(new ResourceRenderer());
86        if(combo.getModel().getSize() > 0){
87          combo.getModel().setSelectedItem(combo.getModel().getElementAt(0));
88        }
89      }else{
90        editComp = textField = new JTextField(20);
91      }
92  
93      getContentPane().add(editComp);
94      getContentPane().add(Box.createVerticalStrut(5));
95  
96      //the buttons box
97      Box buttonsBox = Box.createHorizontalBox();
98      addBtn = new JButton(addAction);
99      removeBtn = new JButton(removeAction);
100     buttonsBox.add(Box.createHorizontalGlue());
101     buttonsBox.add(addBtn);
102     buttonsBox.add(Box.createHorizontalStrut(5));
103     buttonsBox.add(removeBtn);
104     buttonsBox.add(Box.createHorizontalGlue());
105     getContentPane().add(buttonsBox);
106     getContentPane().add(Box.createVerticalStrut(5));
107 
108     //the list component
109     Box horBox = Box.createHorizontalBox();
110     listComponent = new JList(listModel);
111     listComponent.setSelectionMode(ListSelectionModel.
112                                    MULTIPLE_INTERVAL_SELECTION);
113     listComponent.setCellRenderer(new ResourceRenderer());
114     horBox.add(new JScrollPane(listComponent));
115     //up down buttons
116     Box verBox = Box.createVerticalBox();
117     verBox.add(Box.createVerticalGlue());
118     moveUpBtn = new JButton(MainFrame.getIcon("moveup.gif"));
119     verBox.add(moveUpBtn);
120     verBox.add(Box.createVerticalStrut(5));
121     moveDownBtn = new JButton(MainFrame.getIcon("movedown.gif"));
122     verBox.add(moveDownBtn);
123     verBox.add(Box.createVerticalGlue());
124     horBox.add(Box.createHorizontalStrut(3));
125     horBox.add(verBox);
126     horBox.add(Box.createHorizontalStrut(3));
127     getContentPane().add(horBox);
128     getContentPane().add(Box.createVerticalStrut(5));
129 
130     //the bottom buttons
131     buttonsBox = Box.createHorizontalBox();
132     buttonsBox.add(Box.createHorizontalGlue());
133     okButton = new JButton("OK");
134     buttonsBox.add(okButton);
135     buttonsBox.add(Box.createHorizontalStrut(5));
136     cancelButton = new JButton("Cancel");
137     buttonsBox.add(cancelButton);
138     buttonsBox.add(Box.createHorizontalGlue());
139     getContentPane().add(buttonsBox);
140   }
141 
142   protected void initListeners(){
143     okButton.addActionListener(new ActionListener() {
144       public void actionPerformed(ActionEvent e) {
145         userCancelled = false;
146         setVisible(false);
147       }
148     });
149 
150     cancelButton.addActionListener(new ActionListener() {
151       public void actionPerformed(ActionEvent e) {
152         userCancelled = true;
153         setVisible(false);
154       }
155     });
156 
157 
158     moveUpBtn.addActionListener(new ActionListener() {
159       public void actionPerformed(ActionEvent e) {
160         int rows[] = listComponent.getSelectedIndices();
161         if(rows == null || rows.length == 0){
162           JOptionPane.showMessageDialog(
163               ListEditorDialog.this,
164               "Please select some items to be moved ",
165               "GATE", JOptionPane.ERROR_MESSAGE);
166         }else{
167           //we need to make sure the rows are sorted
168           Arrays.sort(rows);
169           //get the list of items
170           for(int i = 0; i < rows.length; i++){
171             int row = rows[i];
172             if(row > 0){
173               //move it up
174               Object value = listModel.remove(row);
175               listModel.add(row - 1, value);
176             }
177           }
178           //restore selection
179           for(int i = 0; i < rows.length; i++){
180             int newRow = -1;
181             if(rows[i] > 0) newRow = rows[i] - 1;
182             else newRow = rows[i];
183             listComponent.addSelectionInterval(newRow, newRow);
184           }
185         }
186 
187       }//public void actionPerformed(ActionEvent e)
188     });
189 
190 
191     moveDownBtn.addActionListener(new ActionListener() {
192       public void actionPerformed(ActionEvent e) {
193         int rows[] = listComponent.getSelectedIndices();
194         if(rows == null || rows.length == 0){
195           JOptionPane.showMessageDialog(
196               ListEditorDialog.this,
197               "Please select some items to be moved ",
198               "GATE", JOptionPane.ERROR_MESSAGE);
199         } else {
200           //we need to make sure the rows are sorted
201           Arrays.sort(rows);
202           //get the list of items
203           for(int i = rows.length - 1; i >= 0; i--){
204             int row = rows[i];
205             if(row < listModel.size() -1){
206               //move it down
207               Object value = listModel.remove(row);
208               listModel.add(row + 1, value);
209             }
210           }
211           //restore selection
212           for(int i = 0; i < rows.length; i++){
213             int newRow = -1;
214             if(rows[i] < listModel.size() - 1) newRow = rows[i] + 1;
215             else newRow = rows[i];
216             listComponent.addSelectionInterval(newRow, newRow);
217           }
218         }
219 
220       }//public void actionPerformed(ActionEvent e)
221     });
222 
223   }
224 
225   /**
226    * Make this dialog visible allowing the editing of the list.
227    * If the user selects the <b>OK</b> option a new list with the updated
228    * contents will be returned; it the <b>Cancel</b> option is selected this
229    * method return <tt>null</tt>.
230    */
231   public List showDialog(){
232     pack();
233     userCancelled = true;
234     setModal(true);
235     super.setVisible(true);
236     return userCancelled ? null : Arrays.asList(listModel.toArray());
237   }
238 
239   /**
240    * test code
241    */
242   public static void main(String[] args){
243     try{
244       Gate.init();
245     }catch(Exception e){
246       e.printStackTrace();
247     }
248     JFrame frame = new JFrame("Foo frame");
249 
250     ListEditorDialog dialog = new ListEditorDialog(frame,
251                                                    new ArrayList(),
252                                                    "java.lang.Integer");
253 
254     frame.setSize(300, 300);
255     frame.setVisible(true);
256     System.out.println(dialog.showDialog());
257   }
258 
259   /**
260    * Adds an element to the list from the editing component located at the top
261    * of this dialog.
262    */
263   protected class AddAction extends AbstractAction{
264     AddAction(){
265       super("Add");
266       putValue(SHORT_DESCRIPTION, "Add the edited value to the list");
267     }
268     public void actionPerformed(ActionEvent e){
269       if(finiteType){
270         listModel.addElement(combo.getSelectedItem());
271       }else{
272         Object value = null;
273         //convert the value to the proper type
274         String stringValue = textField.getText();
275         if(stringValue == null || stringValue.length() == 0) stringValue = null;
276 
277         if(itemTypeClass.isAssignableFrom(String.class)){
278           //no conversion necessary
279           value = stringValue;
280         }else{
281           //try conversion
282           try{
283             value = itemTypeClass.getConstructor(new Class[]{String.class}).
284                                   newInstance( new Object[]{stringValue} );
285           }catch(Exception ex){
286             JOptionPane.showMessageDialog(
287                 ListEditorDialog.this,
288                 "Invalid value!\nIs it the right type?",
289                 "GATE", JOptionPane.ERROR_MESSAGE);
290             return;
291           }
292         }
293         listModel.addElement(value);
294         textField.setText("");
295       }
296     }
297   }
298 
299   /**
300    * Removes the selected element(s) from the list
301    */
302   protected class RemoveAction extends AbstractAction{
303     RemoveAction(){
304       super("Remove");
305       putValue(SHORT_DESCRIPTION, "Remove the selected value(s) from the list");
306     }
307 
308     public void actionPerformed(ActionEvent e){
309       int[] indices = listComponent.getSelectedIndices();
310       Arrays.sort(indices);
311       for(int i = indices.length -1; i >= 0; i--){
312         listModel.remove(indices[i]);
313       }
314     }
315   }
316 
317 
318   /**
319    * A model for a combobox containing the loaded corpora in the system
320    */
321   protected class ResourceComboModel extends AbstractListModel
322                                   implements ComboBoxModel{
323 
324     public int getSize(){
325       //get all corpora regardless of their actual type
326       java.util.List loadedResources = null;
327       try{
328         loadedResources = Gate.getCreoleRegister().
329                                getAllInstances(itemType);
330       }catch(GateException ge){
331         ge.printStackTrace(Err.getPrintWriter());
332       }
333 
334       return loadedResources == null ? 0 : loadedResources.size();
335     }
336 
337     public Object getElementAt(int index){
338       //get all corpora regardless of their actual type
339       java.util.List loadedResources = null;
340       try{
341         loadedResources = Gate.getCreoleRegister().
342                                getAllInstances(itemType);
343       }catch(GateException ge){
344         ge.printStackTrace(Err.getPrintWriter());
345       }
346       return loadedResources == null? null : loadedResources.get(index);
347     }
348 
349     public void setSelectedItem(Object anItem){
350       if(anItem == null) selectedItem = null;
351       else selectedItem = anItem;
352     }
353 
354     public Object getSelectedItem(){
355       return selectedItem;
356     }
357 
358     void fireDataChanged(){
359       fireContentsChanged(this, 0, getSize());
360     }
361 
362     Object selectedItem = null;
363   }
364 
365   /**
366    * The type of the elements in the list
367    */
368   String itemType;
369 
370   /**
371    * The Class for the elements in the list
372    */
373   Class itemTypeClass;
374 
375   /**
376    * The GUI compoenent used to display the list
377    */
378   JList listComponent;
379 
380   /**
381    * Comobox used to select among values for GATE types
382    */
383   JComboBox combo;
384 
385   /**
386    * Text field used to input new arbitrary values
387    */
388   JTextField textField;
389 
390   /**
391    * Used to remove the selected element in the list;
392    */
393   JButton removeBtn;
394 
395   /**
396    * Used to add a new value to the list
397    */
398   JButton addBtn;
399 
400   /**
401    * Moves up one or more items in the list
402    */
403   JButton moveUpBtn;
404 
405   /**
406    * Moves down one or more items in the list
407    */
408   JButton moveDownBtn;
409 
410   /**
411    * The model used by the {@link #listComponent}
412    */
413   DefaultListModel listModel;
414 
415   /**
416    * Does the item type have a finite range (i.e. should we use the combo)?
417    */
418   boolean finiteType;
419 
420   /**
421    * An action that adds the item being edited to the list
422    */
423   Action addAction;
424 
425   /**
426    * An action that removes the item(s) currently selected from the list
427    */
428   Action removeAction;
429 
430   /**
431    * The OK button for this dialog
432    */
433   JButton okButton;
434 
435   /**
436    * The cancel button for this dialog
437    */
438   JButton cancelButton;
439 
440   /**
441    * Did the user press the cancel button?
442    */
443   boolean userCancelled;
444 }
445