Clover icon

Coverage Report

  1. Project Clover database Wed Sep 18 2024 02:54:09 BST
  2. Package jalview.io

File JalviewFileChooser.java

 

Coverage histogram

../../img/srcFileCovDistChart5.png
43% of files have more coverage

Code metrics

132
276
35
3
972
711
125
0.45
7.89
11.67
3.57

Classes

Class Line # Actions
JalviewFileChooser 79 220 110
0.3215258732.2%
JalviewFileChooser.RecentlyOpened 719 23 5
0.9333333493.3%
JalviewFileChooser.recentlyOpenedCellRenderer 786 33 10
0.869565287%
 

Contributing tests

This file is covered by 88 tests. .

Source view

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 java.awt.Component;
25    import java.awt.Dimension;
26    import java.awt.EventQueue;
27    import java.awt.HeadlessException;
28    import java.awt.event.ActionEvent;
29    import java.awt.event.ActionListener;
30    import java.awt.event.MouseAdapter;
31    import java.awt.event.MouseEvent;
32    import java.beans.PropertyChangeEvent;
33    import java.beans.PropertyChangeListener;
34    import java.io.File;
35    import java.util.ArrayList;
36    import java.util.HashMap;
37    import java.util.Iterator;
38    import java.util.List;
39    import java.util.Map;
40    import java.util.StringTokenizer;
41    import java.util.Vector;
42   
43    import javax.swing.BoxLayout;
44    import javax.swing.JCheckBox;
45    import javax.swing.JComboBox;
46    import javax.swing.JDialog;
47    import javax.swing.JFileChooser;
48    import javax.swing.JLabel;
49    import javax.swing.JList;
50    import javax.swing.JOptionPane;
51    import javax.swing.JPanel;
52    import javax.swing.JScrollPane;
53    import javax.swing.ListCellRenderer;
54    import javax.swing.SpringLayout;
55    import javax.swing.SwingConstants;
56    import javax.swing.SwingUtilities;
57    import javax.swing.border.TitledBorder;
58    import javax.swing.filechooser.FileFilter;
59    import javax.swing.plaf.basic.BasicFileChooserUI;
60   
61    import darrylbu.util.SwingUtils;
62    import jalview.bin.Cache;
63    import jalview.gui.AlignFrame;
64    import jalview.gui.JvOptionPane;
65    import jalview.util.ChannelProperties;
66    import jalview.util.MessageManager;
67    import jalview.util.Platform;
68    import jalview.util.dialogrunner.DialogRunnerI;
69   
70    /**
71    * Enhanced file chooser dialog box.
72    *
73    * NOTE: bug on Windows systems when filechooser opened on directory to view
74    * files with colons in title.
75    *
76    * @author AMW
77    *
78    */
 
79    public class JalviewFileChooser extends JFileChooser
80    implements DialogRunnerI, PropertyChangeListener
81    {
82    private static final long serialVersionUID = 1L;
83   
84    private Map<Object, Runnable> callbacks = new HashMap<>();
85   
86    File selectedFile = null;
87   
88    /**
89    * backupfilesCheckBox = "Include backup files" checkbox includeBackupfiles =
90    * flag set by checkbox
91    */
92    private JCheckBox backupfilesCheckBox = null;
93   
94    protected boolean includeBackupFiles = false;
95   
96    /**
97    * default file format preference settings
98    */
99    public final static String DEFAULT_FORMAT_PROPERTY = "DEFAULT_FILE_FORMAT";
100   
101    public final static String DEFAULT_SAVE_FORMAT_PROPERTY = "DEFAULT_SAVE_FILE_FORMAT";
102   
103    public final static String USE_LAST_SAVED_FORMAT_VALUE = "USE_LAST_SAVED";
104   
105    // normally an alignment will default to saving as the format it was loaded or
106    // most recently saved as. Setting this will ignore that and use the
107    // preference default save format.
108    public final static String ALWAYS_USE_DEFAULT_SAVED_FORMAT_PROPERTY = "ALWAYS_USE_DEFAULT_SAVE_FORMAT";
109   
110    /**
111    * Factory method to return a file chooser that offers readable alignment file
112    * formats
113    *
114    * @param directory
115    * @param selected
116    * @return
117    */
 
118  75 toggle public static JalviewFileChooser forRead(String directory,
119    String selected)
120    {
121  75 return JalviewFileChooser.forRead(directory, selected, false);
122    }
123   
 
124  75 toggle public static JalviewFileChooser forRead(String directory,
125    String selected, boolean allowBackupFiles)
126    {
127  75 List<String> extensions = new ArrayList<>();
128  75 List<String> descs = new ArrayList<>();
129  75 for (FileFormatI format : FileFormats.getInstance().getFormats())
130    {
131  1650 if (format.isReadable())
132    {
133  1425 extensions.add(format.getExtensions());
134  1425 descs.add(format.getName());
135    }
136    }
137   
138  75 if (selected == null)
139    {
140  75 selected = defaultLoadFileFormat();
141    }
142   
143  75 return new JalviewFileChooser(directory,
144    extensions.toArray(new String[extensions.size()]),
145    descs.toArray(new String[descs.size()]), selected, true,
146    allowBackupFiles);
147    }
148   
149    /**
150    * Factory method to return a file chooser that offers writable alignment file
151    * formats
152    *
153    * @param directory
154    * @param selected
155    * @return
156    */
 
157  0 toggle public static JalviewFileChooser forWrite(String directory,
158    String selected, boolean addSelectFormatFromFileExtension)
159    {
160    // TODO in Java 8, forRead and forWrite can be a single method
161    // with a lambda expression parameter for isReadable/isWritable
162  0 List<String> extensions = new ArrayList<>();
163  0 List<String> descs = new ArrayList<>();
164  0 for (FileFormatI format : FileFormats.getInstance().getFormats())
165    {
166  0 if (format.isWritable())
167    {
168  0 extensions.add(format.getExtensions());
169  0 descs.add(format.getName());
170    }
171    }
172   
173  0 return new JalviewFileChooser(directory,
174    extensions.toArray(new String[extensions.size()]),
175    descs.toArray(new String[descs.size()]),
176    defaultSaveFileFormat(selected), false, false,
177    addSelectFormatFromFileExtension);
178    }
179   
 
180  0 toggle public JalviewFileChooser(String dir)
181    {
182  0 super(safePath(dir));
183  0 setAccessory(new RecentlyOpened());
184    }
185   
 
186  0 toggle public JalviewFileChooser(String dir, String[] suffix, String[] desc,
187    String selected)
188    {
189  0 this(dir, suffix, desc, selected, true);
190    }
191   
192    /**
193    * Constructor for a single choice of file extension and description
194    *
195    * @param extension
196    * @param desc
197    */
 
198  0 toggle public JalviewFileChooser(String extension, String desc)
199    {
200  0 this(Cache.getProperty("LAST_DIRECTORY"), new String[] { extension },
201    new String[]
202    { desc }, desc, true);
203    }
204   
 
205  0 toggle JalviewFileChooser(String dir, String[] extensions, String[] descs,
206    String selected, boolean acceptAny)
207    {
208  0 this(dir, extensions, descs, selected, acceptAny, false);
209    }
210   
 
211  74 toggle public JalviewFileChooser(String dir, String[] extensions, String[] descs,
212    String selected, boolean acceptAny, boolean allowBackupFiles)
213    {
214  74 this(dir, extensions, descs, selected, acceptAny, allowBackupFiles,
215    false);
216    }
217   
 
218  74 toggle public JalviewFileChooser(String dir, String[] extensions, String[] descs,
219    String selected, boolean acceptAny, boolean allowBackupFiles,
220    boolean addSelectFormatFromExtension)
221    {
222  74 super(safePath(dir));
223  74 if (extensions.length == descs.length)
224    {
225  74 List<String[]> formats = new ArrayList<>();
226  1480 for (int i = 0; i < extensions.length; i++)
227    {
228  1406 formats.add(new String[] { extensions[i], descs[i] });
229    }
230  74 init(formats, selected, acceptAny, allowBackupFiles,
231    addSelectFormatFromExtension);
232    }
233    else
234    {
235  0 jalview.bin.Console
236    .errPrintln("JalviewFileChooser arguments mismatch: "
237    + extensions + ", " + descs);
238    }
239    }
240   
 
241  75 toggle private static File safePath(String dir)
242    {
243  75 if (dir == null)
244    {
245  42 return null;
246    }
247   
248  33 File f = new File(dir);
249  33 if (f.getName().indexOf(':') > -1)
250    {
251  0 return null;
252    }
253  33 return f;
254    }
255   
256    /**
257    * Overridden for JalviewJS compatibility: only one thread in Javascript, so
258    * we can't wait for user choice in another thread and then perform the
259    * desired action
260    */
 
261  0 toggle @Override
262    public int showOpenDialog(Component parent)
263    {
264  0 int value = super.showOpenDialog(this);
265   
266  0 if (!Platform.isJS())
267    /**
268    * Java only
269    *
270    * @j2sIgnore
271    */
272    {
273    /*
274    * code here is not run in JalviewJS, instead
275    * propertyChange() is called for dialog action
276    */
277  0 handleResponse(value);
278    }
279  0 return value;
280    }
281   
282    /**
283    *
284    * @param formats
285    * a list of {extensions, description} for each file format
286    * @param selected
287    * @param acceptAny
288    * if true, 'any format' option is included
289    */
 
290  0 toggle void init(List<String[]> formats, String selected, boolean acceptAny)
291    {
292  0 init(formats, selected, acceptAny, false);
293    }
294   
 
295  0 toggle void init(List<String[]> formats, String selected, boolean acceptAny,
296    boolean allowBackupFiles)
297    {
298  0 init(formats, selected, acceptAny, allowBackupFiles, false);
299    }
300   
 
301  74 toggle void init(List<String[]> formats, String selected, boolean acceptAny,
302    boolean allowBackupFiles, boolean addSelectFormatFromExtension)
303    {
304   
305  74 JalviewFileFilter chosen = null;
306   
307    // SelectAllFilter needs to be set first before adding further
308    // file filters to fix bug on Mac OSX
309  74 setAcceptAllFileFilterUsed(acceptAny);
310   
311    // add a "All known alignment files" option
312  74 List<String> allExtensions = new ArrayList<>();
313  74 for (String[] format : formats)
314    {
315  1406 String[] extensions = format[0].split(",");
316  1406 for (String ext : extensions)
317    {
318  2072 if (!allExtensions.contains(ext))
319    {
320  2072 allExtensions.add(ext);
321    }
322    }
323    }
324  74 allExtensions.sort(null);
325    // only add "All known alignment files" if acceptAny is allowed
326  74 if (acceptAny || addSelectFormatFromExtension)
327    {
328  74 String label = MessageManager
329  74 .getString(addSelectFormatFromExtension ? "label.by_extension"
330    : "label.all_known_alignment_files");
331  74 JalviewFileFilter alljvf = new JalviewFileFilter(
332    allExtensions.toArray(new String[] {}), label);
333  74 alljvf.setMultiFormat(true);
334  74 alljvf.setExtensionListInDescription(false);
335  74 addChoosableFileFilter(alljvf);
336   
337  74 if (selected == null)
338    {
339  16 chosen = alljvf;
340    }
341   
342    // Add the tooltip that appears for the multiformat file type in the
343    // dropdown, but not others
344  74 if (addSelectFormatFromExtension)
345    {
346  0 List<JComboBox> dropdowns = SwingUtils
347    .getDescendantsOfType(JComboBox.class, this);
348  0 for (JComboBox<?> dd : dropdowns)
349    {
350  0 if (dd.getItemCount() > 0
351    && dd.getItemAt(0) instanceof JalviewFileFilter)
352    {
353  0 setByExtensionTooltip(dd);
354  0 dd.addActionListener(new ActionListener()
355    {
 
356  0 toggle @Override
357    public void actionPerformed(ActionEvent e)
358    {
359  0 setByExtensionTooltip(dd);
360    }
361    });
362  0 break;
363    }
364    }
365    }
366    }
367   
368  74 for (String[] format : formats)
369    {
370  1406 JalviewFileFilter jvf = new JalviewFileFilter(format[0], format[1]);
371  1406 if (allowBackupFiles)
372    {
373  0 jvf.setParentJFC(this);
374    }
375  1406 addChoosableFileFilter(jvf);
376  1406 if ((selected != null) && selected.equalsIgnoreCase(format[1]))
377    {
378  58 chosen = jvf;
379    }
380    }
381   
382  74 if (chosen != null)
383    {
384  74 setFileFilter(chosen);
385    }
386   
387  74 JPanel multi = new JPanel();
388  74 multi.setLayout(new BoxLayout(multi, BoxLayout.PAGE_AXIS));
389  74 multi.add(new RecentlyOpened());
390  74 if (allowBackupFiles)
391    {
392  0 if (backupfilesCheckBox == null)
393    {
394  0 try
395    {
396  0 includeBackupFiles = Boolean.parseBoolean(
397    Cache.getProperty(BackupFiles.NS + "_FC_INCLUDE"));
398    } catch (Exception e)
399    {
400  0 includeBackupFiles = false;
401    }
402  0 backupfilesCheckBox = new JCheckBox(
403    MessageManager.getString("label.include_backup_files"),
404    includeBackupFiles);
405  0 backupfilesCheckBox.setAlignmentX(Component.CENTER_ALIGNMENT);
406  0 JalviewFileChooser jfc = this;
407  0 backupfilesCheckBox.addActionListener(new ActionListener()
408    {
 
409  0 toggle @Override
410    public void actionPerformed(ActionEvent e)
411    {
412  0 includeBackupFiles = backupfilesCheckBox.isSelected();
413  0 Cache.setProperty(BackupFiles.NS + "_FC_INCLUDE",
414    String.valueOf(includeBackupFiles));
415   
416  0 FileFilter f = jfc.getFileFilter();
417    // deselect the selected file if it's no longer choosable
418  0 File selectedFile = jfc.getSelectedFile();
419  0 if (selectedFile != null && !f.accept(selectedFile))
420    {
421  0 jfc.setSelectedFile(null);
422    }
423    // fake the OK button changing (to force it to upate)
424  0 String s = jfc.getApproveButtonText();
425  0 jfc.firePropertyChange(APPROVE_BUTTON_TEXT_CHANGED_PROPERTY,
426    null, s);
427    // fake the file filter changing (its behaviour actually has)
428  0 jfc.firePropertyChange(FILE_FILTER_CHANGED_PROPERTY, null, f);
429   
430  0 jfc.rescanCurrentDirectory();
431  0 jfc.revalidate();
432  0 jfc.repaint();
433    }
434    });
435    }
436  0 multi.add(backupfilesCheckBox);
437    }
438    else
439    {
440    // set includeBackupFiles=false to avoid other file choosers from picking
441    // up backup files (Just In Case)
442  74 includeBackupFiles = false;
443    }
444  74 setAccessory(multi);
445    }
446   
447    private static String byExtensionTooltip = null;
448   
 
449  0 toggle private static void setByExtensionTooltip(JComboBox dd)
450    {
451  0 if (dd.getItemCount() > 0
452    && dd.getItemAt(0) instanceof JalviewFileFilter)
453    {
454  0 if (((JalviewFileFilter) dd.getSelectedItem()).isMultiFormat())
455    {
456  0 if (byExtensionTooltip == null)
457    {
458  0 StringBuilder sb = new StringBuilder(
459    MessageManager.getString("label.by_extension_tooltip"));
460  0 JalviewFileFilter jvf = (JalviewFileFilter) dd.getSelectedItem();
461  0 Iterator<String> extensions = jvf.getExtensions();
462  0 if (extensions.hasNext()) // hasAny()
463    {
464  0 sb.append("\n(.");
465  0 while (extensions.hasNext())
466    {
467  0 sb.append(extensions.next());
468  0 if (extensions.hasNext())
469    {
470  0 sb.append(", .");
471    }
472    }
473  0 sb.append(")");
474    }
475  0 byExtensionTooltip = sb.toString();
476    }
477  0 dd.setToolTipText(byExtensionTooltip);
478    }
479    else
480    {
481  0 dd.setToolTipText(null);
482    }
483    }
484    }
485   
 
486  371 toggle @Override
487    public void setFileFilter(javax.swing.filechooser.FileFilter filter)
488    {
489  371 super.setFileFilter(filter);
490   
491  371 try
492    {
493  371 if (getUI() instanceof BasicFileChooserUI)
494    {
495  371 final BasicFileChooserUI fcui = (BasicFileChooserUI) getUI();
496  371 final String name = fcui.getFileName().trim();
497   
498  370 if ((name == null) || (name.length() == 0))
499    {
500  370 return;
501    }
502   
503  0 EventQueue.invokeLater(new Thread()
504    {
 
505  0 toggle @Override
506    public void run()
507    {
508  0 String currentName = fcui.getFileName();
509  0 if ((currentName == null) || (currentName.length() == 0))
510    {
511  0 fcui.setFileName(name);
512    }
513    }
514    });
515    }
516    } catch (Exception ex)
517    {
518  0 ex.printStackTrace();
519    // Some platforms do not have BasicFileChooserUI
520    }
521    }
522   
523    /**
524    * Returns the selected file format, or null if none selected
525    *
526    * @return
527    */
 
528  0 toggle public FileFormatI getSelectedFormat()
529    {
530  0 if (getFileFilter() == null)
531    {
532  0 return null;
533    }
534   
535    /*
536    * logic here depends on option description being formatted as
537    * formatName (extension, extension...)
538    * or the 'no option selected' value
539    * All Files
540    * @see JalviewFileFilter.getDescription
541    */
542  0 String format = getFileFilter().getDescription();
543  0 int parenPos = format.indexOf("(");
544  0 if (parenPos > 0)
545    {
546  0 format = format.substring(0, parenPos).trim();
547  0 try
548    {
549  0 return FileFormats.getInstance().forName(format);
550    } catch (IllegalArgumentException e)
551    {
552  0 jalview.bin.Console.errPrintln("Unexpected format: " + format);
553    }
554    }
555  0 return null;
556    }
557   
558    /**
559    * Unused - could delete ?
560    *
561    * @param format
562    * - matches and configures the filefilter according to given format
563    * @return true if the format given matched an available filter
564    */
 
565  0 toggle public boolean setSelectedFormat(FileFormatI format)
566    {
567  0 if (format == null)
568    {
569  0 return false;
570    }
571  0 String toSelect = format.getName();
572  0 for (FileFilter available : getChoosableFileFilters())
573    {
574  0 if (available instanceof JalviewFileFilter)
575    {
576  0 if (((JalviewFileFilter) available).getDescription()
577    .equals(toSelect))
578    {
579  0 setFileFilter(available);
580  0 return true;
581    }
582    }
583    }
584  0 return false;
585    }
586   
 
587  75 toggle @Override
588    public File getSelectedFile()
589    {
590  75 File f = super.getSelectedFile();
591  75 return f == null ? selectedFile : f;
592    }
593   
 
594  0 toggle @Override
595    public int showSaveDialog(Component parent) throws HeadlessException
596    {
597  0 this.setAccessory(null);
598    // Java 9,10,11 on OSX - clear selected file so name isn't auto populated
599  0 this.setSelectedFile(null);
600   
601  0 return super.showSaveDialog(parent);
602    }
603   
604    /**
605    * If doing a Save, and an existing file is chosen or entered, prompt for
606    * confirmation of overwrite. Proceed if Yes, else leave the file chooser
607    * open.
608    *
609    * @see https://stackoverflow.com/questions/8581215/jfilechooser-and-checking-for-overwrite
610    */
 
611  0 toggle @Override
612    public void approveSelection()
613    {
614  0 if (getDialogType() != SAVE_DIALOG)
615    {
616  0 super.approveSelection();
617  0 return;
618    }
619   
620  0 selectedFile = getSelectedFile();
621   
622  0 if (selectedFile == null)
623    {
624    // Workaround for Java 9,10 on OSX - no selected file, but there is a
625    // filename typed in
626  0 try
627    {
628  0 String filename = ((BasicFileChooserUI) getUI()).getFileName();
629  0 if (filename != null && filename.length() > 0)
630    {
631  0 selectedFile = new File(getCurrentDirectory(), filename);
632    }
633    } catch (Throwable x)
634    {
635  0 jalview.bin.Console.errPrintln(
636    "Unexpected exception when trying to get filename.");
637  0 x.printStackTrace();
638    }
639    // TODO: ENSURE THAT FILES SAVED WITH A ':' IN THE NAME ARE REFUSED AND
640    // THE
641    // USER PROMPTED FOR A NEW FILENAME
642    }
643   
644  0 if (selectedFile == null)
645    {
646  0 return;
647    }
648   
649  0 if (getFileFilter() instanceof JalviewFileFilter)
650    {
651  0 JalviewFileFilter jvf = (JalviewFileFilter) getFileFilter();
652   
653  0 if (!jvf.accept(selectedFile) && !jvf.isMultiFormat())
654    {
655  0 String withExtension = getSelectedFile().getName() + "."
656    + jvf.getAcceptableExtension();
657  0 selectedFile = (new File(getCurrentDirectory(), withExtension));
658  0 setSelectedFile(selectedFile);
659    }
660  0 else if (jvf.isMultiFormat() && jvf.accept(selectedFile))
661    {
662    // if a multiFormat filter is selected, with an acceptable file
663    // extension, see if we can set the format from the file extension
664  0 for (FileFilter jff : this.getChoosableFileFilters())
665    {
666  0 if (jvf != jff && jff.accept(selectedFile))
667    {
668  0 setFileFilter(jff);
669  0 return;
670    }
671    }
672    }
673    }
674   
675  0 if (selectedFile.exists())
676    {
677  0 int confirm = Cache.getDefault("CONFIRM_OVERWRITE_FILE", true)
678    ? JvOptionPane.showConfirmDialog(this,
679    MessageManager
680    .getString("label.overwrite_existing_file"),
681    MessageManager.getString("label.file_already_exists"),
682    JvOptionPane.YES_NO_OPTION)
683    : JOptionPane.YES_OPTION;
684   
685  0 if (confirm != JvOptionPane.YES_OPTION)
686    {
687  0 return;
688    }
689    }
690   
691  0 super.approveSelection();
692    }
693   
 
694  0 toggle void recentListSelectionChanged(Object selection)
695    {
696  0 setSelectedFile(null);
697  0 if (selection != null)
698    {
699  0 File file = new File((String) selection);
700  0 if (getFileFilter() instanceof JalviewFileFilter)
701    {
702  0 JalviewFileFilter jvf = (JalviewFileFilter) this.getFileFilter();
703   
704  0 if (!jvf.accept(file))
705    {
706  0 setFileFilter(getChoosableFileFilters()[0]);
707    }
708    }
709   
710  0 if (!file.isAbsolute() && file.exists())
711    {
712  0 file = file.getAbsoluteFile();
713    }
714   
715  0 setSelectedFile(file);
716    }
717    }
718   
 
719    class RecentlyOpened extends JPanel
720    {
721    private static final long serialVersionUID = 1L;
722   
723    JList<String> list;
724   
 
725  74 toggle RecentlyOpened()
726    {
727  74 setPreferredSize(new Dimension(300, 100));
728  74 String historyItems = Cache.getProperty("RECENT_FILE");
729  74 StringTokenizer st;
730  74 Vector<String> recent = new Vector<>();
731   
732  74 if (historyItems != null)
733    {
734  61 st = new StringTokenizer(historyItems, "\t");
735   
736  358 while (st.hasMoreTokens())
737    {
738  297 recent.addElement(st.nextToken());
739    }
740    }
741   
742  74 list = new JList<>(recent);
743  74 list.setCellRenderer(new recentlyOpenedCellRenderer());
744   
745  74 list.addMouseListener(new MouseAdapter()
746    {
 
747  0 toggle @Override
748    public void mousePressed(MouseEvent evt)
749    {
750  0 recentListSelectionChanged(list.getSelectedValue());
751    }
752    });
753   
754  74 TitledBorder recentlyOpenedBorder = new TitledBorder(
755    MessageManager.getString("label.recently_opened"));
756  74 recentlyOpenedBorder.setTitleFont(
757    recentlyOpenedBorder.getTitleFont().deriveFont(10f));
758  74 this.setBorder(recentlyOpenedBorder);
759   
760  74 final JScrollPane scroller = new JScrollPane(list);
761   
762  74 SpringLayout layout = new SpringLayout();
763  74 layout.putConstraint(SpringLayout.WEST, scroller, 5,
764    SpringLayout.WEST, this);
765  74 layout.putConstraint(SpringLayout.NORTH, scroller, 5,
766    SpringLayout.NORTH, this);
767   
768    // one size okay for all
769  74 scroller.setPreferredSize(new Dimension(280, 105));
770  74 this.add(scroller);
771   
772  74 SwingUtilities.invokeLater(new Runnable()
773    {
 
774  71 toggle @Override
775    public void run()
776    {
777  71 scroller.getHorizontalScrollBar()
778    .setValue(scroller.getHorizontalScrollBar().getMaximum());
779    }
780    });
781   
782    }
783   
784    }
785   
 
786    class recentlyOpenedCellRenderer extends JLabel
787    implements ListCellRenderer<String>
788    {
789    private final static int maxChars = 46;
790   
791    private final static String ellipsis = "...";
792   
 
793  297 toggle @Override
794    public Component getListCellRendererComponent(
795    JList<? extends String> list, String value, int index,
796    boolean isSelected, boolean cellHasFocus)
797    {
798  297 String filename = value.toString();
799  297 String displayFilename;
800  297 if (filename.length() > maxChars)
801    {
802  164 StringBuilder displayFileSB = new StringBuilder();
803  164 File file = new File(filename);
804  164 displayFileSB.append(file.getName());
805  164 if (file.getParent() != null)
806    {
807  164 File parent = file;
808  164 boolean spaceleft = true;
809  983 while (spaceleft && parent.getParent() != null)
810    {
811  819 parent = parent.getParentFile();
812  819 String name = parent.getName();
813  819 displayFileSB.insert(0, File.separator);
814  819 if (displayFileSB.length() + name.length() < maxChars - 1)
815    {
816  655 displayFileSB.insert(0, name);
817    }
818    else
819    {
820  164 displayFileSB.insert(0, ellipsis);
821  164 spaceleft = false;
822    }
823    }
824  164 if (spaceleft && filename.startsWith(File.separator)
825    && !(displayFileSB.charAt(0) == File.separatorChar))
826    {
827  0 displayFileSB.insert(0, File.separator);
828    }
829    }
830  164 displayFilename = displayFileSB.toString();
831    }
832    else
833    {
834  133 displayFilename = filename;
835    }
836  297 this.setText(displayFilename.toString());
837  297 this.setToolTipText(filename);
838  297 if (isSelected)
839    {
840  0 setBackground(list.getSelectionBackground());
841  0 setForeground(list.getSelectionForeground());
842    }
843    else
844    {
845  297 setBackground(list.getBackground());
846  297 setForeground(list.getForeground());
847    }
848  297 this.setHorizontalAlignment(SwingConstants.TRAILING);
849  297 this.setEnabled(list.isEnabled());
850  297 this.setFont(list.getFont().deriveFont(12f));
851  297 this.setOpaque(true);
852  297 return this;
853    }
854   
855    }
856   
857    /*
858    @Override
859    public JalviewFileChooser setResponseHandler(Object response,
860    Runnable action)
861    {
862    callbacks.put(response, new Callable<Void>()
863    {
864    @Override
865    public Void call()
866    {
867    action.run();
868    return null;
869    }
870    });
871    return this;
872    }
873    */
874   
 
875  0 toggle @Override
876    public DialogRunnerI setResponseHandler(Object response, Runnable action)
877    {
878  0 callbacks.put(response, action);
879  0 return this;
880    }
881   
 
882  0 toggle @Override
883    public void handleResponse(Object response)
884    {
885    /*
886    * this test is for NaN in Chrome
887    */
888  0 if (response != null && !response.equals(response))
889    {
890  0 return;
891    }
892  0 Runnable action = callbacks.get(response);
893  0 if (action != null)
894    {
895  0 try
896    {
897  0 action.run();
898    } catch (Exception e)
899    {
900  0 e.printStackTrace();
901    }
902    }
903    }
904   
905    /**
906    * JalviewJS signals file selection by a property change event for property
907    * "SelectedFile". This methods responds to that by running the response
908    * action for 'OK' in the dialog.
909    *
910    * @param evt
911    */
 
912  0 toggle @Override
913    public void propertyChange(PropertyChangeEvent evt)
914    {
915    // TODO other properties need runners...
916  0 switch (evt.getPropertyName())
917    {
918    /*
919    * property name here matches that used in JFileChooser.js
920    */
921  0 case "SelectedFile":
922  0 handleResponse(APPROVE_OPTION);
923  0 break;
924    }
925    }
926   
 
927  0 toggle @Override
928    protected JDialog createDialog(Component parent) throws HeadlessException
929    {
930  0 JDialog dialog = super.createDialog(parent);
931  0 dialog.setIconImages(ChannelProperties.getIconList());
932  0 return dialog;
933    }
934   
935    /**
936    * return a default file format
937    */
 
938  75 toggle public static String defaultLoadFileFormat()
939    {
940  75 return FileLoader.getUseDefaultFileFormat()
941    ? Cache.getProperty(DEFAULT_FORMAT_PROPERTY)
942    : null;
943    }
944   
 
945  28 toggle public static String defaultSaveFileFormat(String lastUsedFormat)
946    {
947  28 if (!Cache.getDefault(ALWAYS_USE_DEFAULT_SAVED_FORMAT_PROPERTY, false)
948    && lastUsedFormat != null)
949    {
950  8 return lastUsedFormat;
951    }
952   
953  20 String pref = Cache.getDefault(DEFAULT_SAVE_FORMAT_PROPERTY, null);
954  20 if (USE_LAST_SAVED_FORMAT_VALUE.equals(pref))
955    {
956  6 FileFormatI globalLastSavedFormat = AlignFrame
957    .getLastAlignmentSavedFormat();
958  6 if (globalLastSavedFormat != null || lastUsedFormat != null)
959    {
960  4 return globalLastSavedFormat != null
961    ? globalLastSavedFormat.toString()
962    : lastUsedFormat;
963    }
964    else
965    {
966  2 pref = null; // there is no USE_LAST_SAVED
967    }
968    }
969  16 return pref == null ? FileFormat.Fasta.getName() : pref;
970    }
971   
972    }