FileFormatI further tweaks, clean compile
[jalview.git] / src / jalview / io / JalviewFileChooser.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  *
5  * This file is part of Jalview.
6  *
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * Jalview is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE.  See the GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 //////////////////////////////////////////////////////////////////
22 package jalview.io;
23
24 import jalview.util.MessageManager;
25 import jalview.util.Platform;
26
27 import java.awt.Component;
28 import java.awt.Dimension;
29 import java.awt.EventQueue;
30 import java.awt.HeadlessException;
31 import java.awt.event.MouseAdapter;
32 import java.awt.event.MouseEvent;
33 import java.io.File;
34 import java.util.ArrayList;
35 import java.util.Collections;
36 import java.util.List;
37 import java.util.StringTokenizer;
38 import java.util.Vector;
39
40 import javax.swing.DefaultListCellRenderer;
41 import javax.swing.JFileChooser;
42 import javax.swing.JList;
43 import javax.swing.JOptionPane;
44 import javax.swing.JPanel;
45 import javax.swing.JScrollPane;
46 import javax.swing.SpringLayout;
47 import javax.swing.plaf.basic.BasicFileChooserUI;
48
49 /**
50  * Enhanced file chooser dialog box.
51  *
52  * NOTE: bug on Windows systems when filechooser opened on directory to view
53  * files with colons in title.
54  *
55  * @author AMW
56  *
57  */
58 public class JalviewFileChooser extends JFileChooser
59 {
60   /**
61    * Factory method to return a file chooser that offers readable alignment file
62    * formats
63    * 
64    * @param directory
65    * @param selected
66    * @param selectAll
67    * @return
68    */
69   public static JalviewFileChooser forRead(String directory,
70           String selected, boolean selectAll)
71   {
72     List<String> extensions = new ArrayList<String>();
73     List<String> descs = new ArrayList<String>();
74     for (FileFormatI format : FileFormat.values())
75     {
76       if (format.isReadable())
77       {
78         extensions.add(format.getExtensions());
79         descs.add(format.getShortDescription());
80       }
81     }
82     return new JalviewFileChooser(directory,
83             extensions.toArray(new String[extensions.size()]),
84             descs.toArray(new String[descs.size()]),
85             selected);
86   }
87
88   /**
89    * Factory method to return a file chooser that offers writable alignment file
90    * formats
91    * 
92    * @param directory
93    * @param selected
94    * @param selectAll
95    * @return
96    */
97   public static JalviewFileChooser forWrite(String directory,
98           String selected, boolean selectAll)
99   {
100     // TODO in Java 8, forRead and forWrite can be a single method
101     // with a lambda expression parameter for isReadable/isWritable
102     List<String> extensions = new ArrayList<String>();
103     List<String> descs = new ArrayList<String>();
104     for (FileFormatI format : FileFormat.values())
105     {
106       if (format.isWritable())
107       {
108         extensions.add(format.getExtensions());
109         descs.add(format.getShortDescription());
110       }
111     }
112     return new JalviewFileChooser(directory,
113             extensions.toArray(new String[extensions.size()]),
114             descs.toArray(new String[descs.size()]), selected);
115   }
116
117   public JalviewFileChooser(String dir)
118   {
119     super(safePath(dir));
120     setAccessory(new RecentlyOpened());
121   }
122
123   public JalviewFileChooser(String dir, String extension, String desc,
124           String selected)
125   {
126     super(safePath(dir));
127     init(Collections.singletonList(new String[] { extension, desc }),
128             selected);
129   }
130
131   public JalviewFileChooser(String dir, String[] extensions, String[] descs,
132           String selected)
133   {
134     super(safePath(dir));
135     if (extensions.length == descs.length)
136     {
137       List<String[]> formats = new ArrayList<String[]>();
138       for (int i = 0; i < extensions.length; i++)
139       {
140         formats.add(new String[] { extensions[i], descs[i] });
141       }
142       init(formats, selected);
143     }
144     else
145     {
146       System.err.println("JalviewFileChooser arguments mismatch: "
147               + extensions + ", " + descs);
148     }
149   }
150
151   private static File safePath(String dir)
152   {
153     if (dir == null)
154     {
155       return null;
156     }
157
158     File f = new File(dir);
159     if (f.getName().indexOf(':') > -1)
160     {
161       return null;
162     }
163     return f;
164   }
165
166   /**
167    * 
168    * @param formats
169    *          a list of {extensions, description} for each file format
170    * @param selected
171    */
172   void init(List<String[]> formats, String selected)
173   {
174
175     JalviewFileFilter chosen = null;
176
177     // SelectAllFilter needs to be set first before adding further
178     // file filters to fix bug on Mac OSX
179     setAcceptAllFileFilterUsed(true);
180
181     for (String[] format : formats)
182     {
183       JalviewFileFilter jvf = new JalviewFileFilter(format[0], format[1]);
184       addChoosableFileFilter(jvf);
185       if ((selected != null) && selected.equalsIgnoreCase(format[1]))
186       {
187         chosen = jvf;
188       }
189     }
190
191     if (chosen != null)
192     {
193       setFileFilter(chosen);
194     }
195
196     setAccessory(new RecentlyOpened());
197   }
198
199   @Override
200   public void setFileFilter(javax.swing.filechooser.FileFilter filter)
201   {
202     super.setFileFilter(filter);
203
204     try
205     {
206       if (getUI() instanceof BasicFileChooserUI)
207       {
208         final BasicFileChooserUI fcui = (BasicFileChooserUI) getUI();
209         final String name = fcui.getFileName().trim();
210
211         if ((name == null) || (name.length() == 0))
212         {
213           return;
214         }
215
216         EventQueue.invokeLater(new Thread()
217         {
218           @Override
219           public void run()
220           {
221             String currentName = fcui.getFileName();
222             if ((currentName == null) || (currentName.length() == 0))
223             {
224               fcui.setFileName(name);
225             }
226           }
227         });
228       }
229     } catch (Exception ex)
230     {
231       ex.printStackTrace();
232       // Some platforms do not have BasicFileChooserUI
233     }
234   }
235
236   public FileFormatI getSelectedFormat()
237   {
238     if (getFileFilter() == null)
239     {
240       return null;
241     }
242
243     String format = getFileFilter().getDescription();
244     format = format.substring(0, format.indexOf("(") - 1);
245     return FileFormat.valueOf(format);
246   }
247
248   @Override
249   public int showSaveDialog(Component parent) throws HeadlessException
250   {
251     this.setAccessory(null);
252
253     setDialogType(SAVE_DIALOG);
254
255     int ret = showDialog(parent, MessageManager.getString("action.save"));
256
257     if (getFileFilter() instanceof JalviewFileFilter)
258     {
259       JalviewFileFilter jvf = (JalviewFileFilter) getFileFilter();
260
261       if (!jvf.accept(getSelectedFile()))
262       {
263         String withExtension = getSelectedFile() + "."
264                 + jvf.getAcceptableExtension();
265         setSelectedFile(new File(withExtension));
266       }
267     }
268     // TODO: ENSURE THAT FILES SAVED WITH A ':' IN THE NAME ARE REFUSED AND THE
269     // USER PROMPTED FOR A NEW FILENAME
270     if ((ret == JalviewFileChooser.APPROVE_OPTION)
271             && getSelectedFile().exists())
272     {
273       int confirm = JOptionPane.showConfirmDialog(parent,
274               MessageManager.getString("label.overwrite_existing_file"),
275               MessageManager.getString("label.file_already_exists"),
276               JOptionPane.YES_NO_OPTION);
277
278       if (confirm != JOptionPane.YES_OPTION)
279       {
280         ret = JalviewFileChooser.CANCEL_OPTION;
281       }
282     }
283
284     return ret;
285   }
286
287   void recentListSelectionChanged(Object selection)
288   {
289     setSelectedFile(null);
290     if (selection != null)
291     {
292       File file = new File((String) selection);
293       if (getFileFilter() instanceof JalviewFileFilter)
294       {
295         JalviewFileFilter jvf = (JalviewFileFilter) this.getFileFilter();
296
297         if (!jvf.accept(file))
298         {
299           setFileFilter(getChoosableFileFilters()[0]);
300         }
301       }
302
303       setSelectedFile(file);
304     }
305   }
306
307   class RecentlyOpened extends JPanel
308   {
309     JList list;
310
311     public RecentlyOpened()
312     {
313
314       String historyItems = jalview.bin.Cache.getProperty("RECENT_FILE");
315       StringTokenizer st;
316       Vector recent = new Vector();
317
318       if (historyItems != null)
319       {
320         st = new StringTokenizer(historyItems, "\t");
321
322         while (st.hasMoreTokens())
323         {
324           recent.addElement(st.nextElement());
325         }
326       }
327
328       list = new JList(recent);
329
330       DefaultListCellRenderer dlcr = new DefaultListCellRenderer();
331       dlcr.setHorizontalAlignment(DefaultListCellRenderer.RIGHT);
332       list.setCellRenderer(dlcr);
333
334       list.addMouseListener(new MouseAdapter()
335       {
336         @Override
337         public void mousePressed(MouseEvent evt)
338         {
339           recentListSelectionChanged(list.getSelectedValue());
340         }
341       });
342
343       this.setBorder(new javax.swing.border.TitledBorder(MessageManager
344               .getString("label.recently_opened")));
345
346       final JScrollPane scroller = new JScrollPane(list);
347
348       SpringLayout layout = new SpringLayout();
349       layout.putConstraint(SpringLayout.WEST, scroller, 5,
350               SpringLayout.WEST, this);
351       layout.putConstraint(SpringLayout.NORTH, scroller, 5,
352               SpringLayout.NORTH, this);
353
354       if (new Platform().isAMac())
355       {
356         scroller.setPreferredSize(new Dimension(500, 100));
357       }
358       else
359       {
360         scroller.setPreferredSize(new Dimension(130, 200));
361       }
362
363       this.add(scroller);
364
365       javax.swing.SwingUtilities.invokeLater(new Runnable()
366       {
367         @Override
368         public void run()
369         {
370           scroller.getHorizontalScrollBar().setValue(
371                   scroller.getHorizontalScrollBar().getMaximum());
372         }
373       });
374
375     }
376
377   }
378 }