JAL-1643 Modified JalviewFileChooser to have a more native laf on mac
[jalview.git] / src / jalview / io / JalviewFileChooser.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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
26 import java.awt.Component;
27 import java.awt.Dimension;
28 import java.awt.EventQueue;
29 import java.awt.HeadlessException;
30 import java.awt.event.MouseAdapter;
31 import java.awt.event.MouseEvent;
32 import java.io.File;
33 import java.util.StringTokenizer;
34 import java.util.Vector;
35
36 import javax.swing.DefaultListCellRenderer;
37 import javax.swing.JFileChooser;
38 import javax.swing.JList;
39 import javax.swing.JOptionPane;
40 import javax.swing.JPanel;
41 import javax.swing.JScrollPane;
42 import javax.swing.SpringLayout;
43
44 /**
45  * Enhanced file chooser dialog box.
46  *
47  * NOTE: bug on Windows systems when filechooser opened on directory to view
48  * files with colons in title.
49  *
50  * @author AMW
51  *
52  */
53 public class JalviewFileChooser extends JFileChooser
54 {
55   public JalviewFileChooser(String dir)
56   {
57     super(safePath(dir));
58     setAccessory(new RecentlyOpened());
59   }
60
61   private static File safePath(String dir)
62   {
63     if (dir == null)
64     {
65       return null;
66     }
67
68     File f = new File(dir);
69     if (f.getName().indexOf(':') > -1)
70     {
71       return null;
72     }
73     return f;
74   }
75
76   public JalviewFileChooser(String dir, String[] suffix, String[] desc,
77           String selected, boolean selectAll)
78   {
79     super(safePath(dir));
80     init(suffix, desc, selected, selectAll);
81   }
82
83   public JalviewFileChooser(String dir, String[] suffix, String[] desc,
84           String selected)
85   {
86     super(safePath(dir));
87     init(suffix, desc, selected, true);
88   }
89
90   void init(String[] suffix, String[] desc, String selected,
91           boolean selectAll)
92   {
93
94     JalviewFileFilter chosen = null;
95
96     // SelectAllFilter needs to be set first before adding further
97     // file filters to fix bug on Mac OSX
98     setAcceptAllFileFilterUsed(selectAll);
99
100     for (int i = 0; i < suffix.length; i++)
101     {
102       JalviewFileFilter jvf = new JalviewFileFilter(suffix[i], desc[i]);
103       addChoosableFileFilter(jvf);
104       if ((selected != null) && selected.equalsIgnoreCase(desc[i]))
105       {
106         chosen = jvf;
107       }
108     }
109
110     if (chosen != null)
111     {
112       setFileFilter(chosen);
113     }
114
115     setAccessory(new RecentlyOpened());
116   }
117
118   @Override
119   public void setFileFilter(javax.swing.filechooser.FileFilter filter)
120   {
121     super.setFileFilter(filter);
122
123     try
124     {
125       if (getUI() instanceof javax.swing.plaf.basic.BasicFileChooserUI)
126       {
127         final javax.swing.plaf.basic.BasicFileChooserUI ui = (javax.swing.plaf.basic.BasicFileChooserUI) getUI();
128         final String name = ui.getFileName().trim();
129
130         if ((name == null) || (name.length() == 0))
131         {
132           return;
133         }
134
135         EventQueue.invokeLater(new Thread()
136         {
137           @Override
138           public void run()
139           {
140             String currentName = ui.getFileName();
141             if ((currentName == null) || (currentName.length() == 0))
142             {
143               ui.setFileName(name);
144             }
145           }
146         });
147       }
148     } catch (Exception ex)
149     {
150       ex.printStackTrace();
151       // Some platforms do not have BasicFileChooserUI
152     }
153   }
154
155   public String getSelectedFormat()
156   {
157     if (getFileFilter() == null)
158     {
159       return null;
160     }
161
162     String format = getFileFilter().getDescription();
163
164     if (format.toUpperCase().startsWith("JALVIEW"))
165     {
166       format = "Jalview";
167     }
168     else if (format.toUpperCase().startsWith("FASTA"))
169     {
170       format = "FASTA";
171     }
172     else if (format.toUpperCase().startsWith("MSF"))
173     {
174       format = "MSF";
175     }
176     else if (format.toUpperCase().startsWith("CLUSTAL"))
177     {
178       format = "CLUSTAL";
179     }
180     else if (format.toUpperCase().startsWith("BLC"))
181     {
182       format = "BLC";
183     }
184     else if (format.toUpperCase().startsWith("PIR"))
185     {
186       format = "PIR";
187     }
188     else if (format.toUpperCase().startsWith("PFAM"))
189     {
190       format = "PFAM";
191     }
192     else if (format.toUpperCase().startsWith(PhylipFile.FILE_DESC))
193     {
194       format = PhylipFile.FILE_DESC;
195     }
196
197     return format;
198   }
199
200   @Override
201   public int showSaveDialog(Component parent) throws HeadlessException
202   {
203     this.setAccessory(null);
204
205     setDialogType(SAVE_DIALOG);
206
207     int ret = showDialog(parent, MessageManager.getString("action.save"));
208
209     if (getFileFilter() instanceof JalviewFileFilter)
210     {
211       JalviewFileFilter jvf = (JalviewFileFilter) getFileFilter();
212
213       if (!jvf.accept(getSelectedFile()))
214       {
215         String withExtension = getSelectedFile() + "."
216                 + jvf.getAcceptableExtension();
217         setSelectedFile(new File(withExtension));
218       }
219     }
220     // TODO: ENSURE THAT FILES SAVED WITH A ':' IN THE NAME ARE REFUSED AND THE
221     // USER PROMPTED FOR A NEW FILENAME
222     if ((ret == JalviewFileChooser.APPROVE_OPTION)
223             && getSelectedFile().exists())
224     {
225       int confirm = JOptionPane.showConfirmDialog(parent,
226               MessageManager.getString("label.overwrite_existing_file"), MessageManager.getString("label.file_already_exists"),
227               JOptionPane.YES_NO_OPTION);
228
229       if (confirm != JOptionPane.YES_OPTION)
230       {
231         ret = JalviewFileChooser.CANCEL_OPTION;
232       }
233     }
234
235     return ret;
236   }
237
238   void recentListSelectionChanged(Object selection)
239   {
240     setSelectedFile(null);
241     if (selection != null)
242     {
243       File file = new File((String) selection);
244       if (getFileFilter() instanceof JalviewFileFilter)
245       {
246         JalviewFileFilter jvf = (JalviewFileFilter) this.getFileFilter();
247
248         if (!jvf.accept(file))
249         {
250           setFileFilter(getChoosableFileFilters()[0]);
251         }
252       }
253
254       setSelectedFile(file);
255     }
256   }
257
258   class RecentlyOpened extends JPanel
259   {
260     JList list;
261
262     public RecentlyOpened()
263     {
264
265       String historyItems = jalview.bin.Cache.getProperty("RECENT_FILE");
266       StringTokenizer st;
267       Vector recent = new Vector();
268
269       if (historyItems != null)
270       {
271         st = new StringTokenizer(historyItems, "\t");
272
273         while (st.hasMoreTokens())
274         {
275           recent.addElement(st.nextElement());
276         }
277       }
278
279       list = new JList(recent);
280
281       DefaultListCellRenderer dlcr = new DefaultListCellRenderer();
282       dlcr.setHorizontalAlignment(DefaultListCellRenderer.RIGHT);
283       list.setCellRenderer(dlcr);
284
285       list.addMouseListener(new MouseAdapter()
286       {
287         @Override
288         public void mousePressed(MouseEvent evt)
289         {
290           recentListSelectionChanged(list.getSelectedValue());
291         }
292       });
293
294       this.setBorder(new javax.swing.border.TitledBorder(MessageManager.getString("label.recently_opened")));
295
296       final JScrollPane scroller = new JScrollPane(list);
297
298       SpringLayout layout = new SpringLayout();
299       layout.putConstraint(SpringLayout.WEST, scroller, 5,
300               SpringLayout.WEST, this);
301       layout.putConstraint(SpringLayout.NORTH, scroller, 5,
302               SpringLayout.NORTH, this);
303
304       // scroller.setPreferredSize(new Dimension(130, 200));
305
306
307       scroller.setPreferredSize(new Dimension(500, 100));
308       this.add(scroller);
309
310       javax.swing.SwingUtilities.invokeLater(new Runnable()
311       {
312         @Override
313         public void run()
314         {
315           scroller.getHorizontalScrollBar().setValue(
316                   scroller.getHorizontalScrollBar().getMaximum());
317         }
318       });
319
320     }
321
322   }
323 }