New class, allows sorting
[jalview.git] / src / jalview / io / JalviewFileChooser.java
1 package jalview.io;
2
3 import java.awt.*;
4 import java.awt.event.*;
5 import java.io.*;
6 import java.util.*;
7
8 import javax.swing.*;
9 import javax.swing.plaf.*;
10 import javax.swing.plaf.basic.*;
11 import javax.swing.plaf.metal.*;
12 import javax.swing.table.*;
13
14 public class JalviewFileChooser extends JFileChooser
15 {
16
17   private static final int COLUMN_FILENAME = 0;
18   private static final int COLUMN_FILESIZE = 1;
19   private static final int COLUMN_FILETYPE = 2;
20   private static final int COLUMN_FILEDATE = 3;
21   private static final int COLUMN_FILEATTR = 4;
22   private static final int COLUMN_COLCOUNT = 5;
23   private static String[] COLUMNS = null;
24
25   public JalviewFileChooser(String dir)
26   {
27     super(dir);
28     if (COLUMNS == null)
29     {
30       Locale l = getLocale();
31       COLUMNS = new String[]{
32         UIManager.getString("FileChooser.fileNameHeaderText",l),
33         UIManager.getString("FileChooser.fileSizeHeaderText",l),
34         UIManager.getString("FileChooser.fileTypeHeaderText",l),
35         UIManager.getString("FileChooser.fileDateHeaderText",l),
36         UIManager.getString("FileChooser.fileAttrHeaderText",l)
37       };
38     }
39
40   }
41
42   public JalviewFileChooser()
43   {
44     if (COLUMNS == null)
45     {
46       Locale l = getLocale();
47       COLUMNS = new String[]{
48         UIManager.getString("FileChooser.fileNameHeaderText",l),
49         UIManager.getString("FileChooser.fileSizeHeaderText",l),
50         UIManager.getString("FileChooser.fileTypeHeaderText",l),
51         UIManager.getString("FileChooser.fileDateHeaderText",l),
52         UIManager.getString("FileChooser.fileAttrHeaderText",l)
53       };
54     }
55   }
56
57
58   /**************************************************************************
59    * Always create the local UI
60    * @param comp
61    *************************************************************************/
62   public final void setUI(ComponentUI comp)
63   {
64     super.setUI(new UI(this));
65   }
66
67    /**************************************************************************
68     * Internal implementation of Metal LookAndFeel to create the table sorting
69     * ability.
70     *************************************************************************/
71    private final static class UI extends MetalFileChooserUI
72    {
73      private DirectoryModel model;
74
75      /**************************************************************************
76       * Must be overridden to extend
77       * @param e
78       *************************************************************************/
79      public UI(JFileChooser e)
80      {
81        super(e);
82      }
83
84      /**************************************************************************
85       * Overridden to create our own model
86       *************************************************************************/
87      protected final void createModel()
88      {
89        model = new DirectoryModel(getFileChooser());
90      }
91
92      /**************************************************************************
93       * Overridden to get our own model
94       * @return
95       *************************************************************************/
96      public final BasicDirectoryModel getModel() {       return model;     }
97
98      /**************************************************************************
99       * Calls the default method then adds a MouseListener to the JTable
100       * @param chooser
101       * @return
102       *************************************************************************/
103      protected final JPanel createDetailsView(JFileChooser chooser)
104      {
105        final JPanel panel = super.createDetailsView(chooser);
106
107        //Since we can't access MetalFileChooserUI's private member detailsTable
108        //directly, we have to find it in the JPanel
109        final JTable tbl = findJTable(panel.getComponents());
110        if (tbl != null)
111        {
112          //Fix the columns so they can't be rearranged, if we don't do this
113          //we would need to keep track when each column is moved
114          tbl.getTableHeader().setReorderingAllowed(false);
115
116          //Add a mouselistener to listen for clicks on column headers
117          tbl.getTableHeader().addMouseListener(new MouseAdapter(){
118            public void mouseClicked(MouseEvent e)
119            {
120              //Only process single clicks
121              if (e.getClickCount() > 1) return;
122              e.consume();
123              final int col = tbl.getTableHeader().columnAtPoint(e.getPoint());
124              if (col == COLUMN_FILENAME || col == COLUMN_FILESIZE ||
125                  col == COLUMN_FILEDATE)
126                model.sort(col,tbl);
127            }
128          });
129        }
130        return panel;
131      }
132
133      /**************************************************************************
134       * Finds the JTable in the panel so we can add MouseListener
135       * @param comp
136       * @return
137       *************************************************************************/
138      private final static JTable findJTable(Component[] comp)
139      {
140        for (int i=0;i<comp.length;i++)
141        {
142          if (comp[i] instanceof JTable)
143          {
144            return (JTable)comp[i];
145          }
146          if (comp[i] instanceof Container)
147          {
148            JTable tbl = findJTable(((Container)comp[i]).getComponents());
149            if (tbl != null) return tbl;
150          }
151        }
152        return null;
153      }
154    }
155
156   /***************************************************************************
157    * Implementation of BasicDirectoryModel that sorts the Files by column
158    **************************************************************************/
159   private final static class DirectoryModel extends BasicDirectoryModel
160    {
161      int col = 0;
162      boolean ascending;
163
164      /**************************************************************************
165       * Must be overridden to extend BasicDirectoryModel
166       * @param chooser
167       *************************************************************************/
168      DirectoryModel(JFileChooser chooser)
169      {
170        super(chooser);
171      }
172
173
174      /**************************************************************************
175       * Supposedly this is not used anymore, hopefully not.  We implemented
176       * some basic attempt at sorting just in case
177       * @param a
178       * @param b
179       * @return
180       *************************************************************************/
181      protected final boolean lt(File a, File b)
182      {
183        System.out.println("LT called?");
184        boolean less = false;
185        switch (col)
186        {
187          case COLUMN_FILEDATE:
188            less = a.lastModified() > b.lastModified();
189            break;
190          case COLUMN_FILESIZE:
191            less = a.length() > b.length();
192            break;
193          default:
194            less = a.getName().compareToIgnoreCase(b.getName()) > 0;
195          break;
196        }
197        if (ascending) return less = !less;
198        return less;
199      }
200
201      /**************************************************************************
202       * Resorts the JFileChooser table based on new column
203       * @param c
204       *************************************************************************/
205      protected final void sort(int c, JTable tbl)
206      {
207        //Set column and order
208        col = c;
209        ascending = !ascending;
210        String indicator = " (^)";
211        if (ascending)
212          indicator = " (v)";
213
214        final JTableHeader th = tbl.getTableHeader();
215        final TableColumnModel tcm = th.getColumnModel();
216
217        for (int i=0;i<JalviewFileChooser.COLUMN_COLCOUNT;i++)
218        {
219          final TableColumn tc = tcm.getColumn( i ); // the column to change
220          tc.setHeaderValue( COLUMNS[i] );
221        }
222
223        final TableColumn tc = tcm.getColumn( col ); // the column to change
224        tc.setHeaderValue( COLUMNS[col] + indicator );
225
226        th.repaint();
227
228        //Requery the file listing
229        validateFileCache();
230      }
231
232      /**************************************************************************
233       * Sorts the data based on current column setting
234       * @param data
235       *************************************************************************/
236      protected final void sort(Vector data)
237      {
238        switch (col)
239        {
240          case COLUMN_FILEDATE:
241            Collections.sort(data,new Comparator(){
242              public int compare(Object o1,Object o2)
243              {
244                int ret = 1;
245                final File a = (File)o1;
246                final File b = (File)o2;
247                if (a.lastModified() > b.lastModified())
248                  ret = -1;
249                else if (a.lastModified() == b.lastModified())
250                  ret = 0;
251
252                if (ascending)
253                  ret *= -1;
254                return ret;
255              }
256
257            });
258            break;
259          case COLUMN_FILESIZE:
260            Collections.sort(data,new Comparator(){
261              public int compare(Object o1,Object o2)
262              {
263                int ret = 1;
264                final File a = (File)o1;
265                final File b = (File)o2;
266                if (a.length() > b.length())
267                  ret = -1;
268                else if (a.length() == b.length())
269                  ret = 0;
270
271                if (ascending)
272                  ret *= -1;
273                return ret;
274              }
275
276            });
277            break;
278          case COLUMN_FILENAME:
279            Collections.sort(data,new Comparator(){
280              public int compare(Object o1,Object o2)
281              {
282                final File a = (File)o1;
283                final File b = (File)o2;
284                if (ascending)
285                  return a.getName().compareToIgnoreCase(b.getName());
286                else
287                  return -1 * a.getName().compareToIgnoreCase(b.getName());
288              }
289
290            });
291            break;
292        }
293      }
294   }
295 }
296