/*
* Copyright (C) 2007-2011 IsmAvatar <IsmAvatar@gmail.com>
* Copyright (C) 2013 Robert B. Colton
*
* This file is part of LateralGM.
*
* LateralGM is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LateralGM is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License (COPYING) for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.lateralgm.main;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.Icon;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.TransferHandler;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileView;
import javax.swing.tree.TreeNode;
import org.lateralgm.components.CustomFileChooser;
import org.lateralgm.components.CustomFileChooser.FilterSet;
import org.lateralgm.components.ErrorDialog;
import org.lateralgm.components.GmMenuBar;
import org.lateralgm.components.impl.CustomFileFilter;
import org.lateralgm.components.impl.ResNode;
import org.lateralgm.file.GMXFileReader;
import org.lateralgm.file.GMXFileWriter;
import org.lateralgm.file.GmFileReader;
import org.lateralgm.file.GmFileWriter;
import org.lateralgm.file.GmFormatException;
import org.lateralgm.file.ProjectFile;
import org.lateralgm.file.ProjectFile.FormatFlavor;
import org.lateralgm.file.ProjectFormatException;
import org.lateralgm.file.ResourceList;
import org.lateralgm.messages.Messages;
import org.lateralgm.resources.Resource;
public class FileChooser
{
public static List<FileReader> readers = new ArrayList<FileReader>();
public static List<FileWriter> writers = new ArrayList<FileWriter>();
public static List<FileView> fileViews = new ArrayList<FileView>();
static ProjectReader projectReader;
FileWriter selectedWriter;
CustomFileChooser fc = new CustomFileChooser("/org/lateralgm","LAST_FILE_DIR"); //$NON-NLS-1$ //$NON-NLS-2$
FilterSet openFs = new FilterSet(), saveFs = new FilterSet();
FilterUnion openAllFilter = new FilterUnion(), saveAllFilter = new FilterUnion();
public static void addDefaultReadersAndWriters()
{
if (projectReader != null) return;
readers.add(projectReader = new ProjectReader());
int[] gmvers = { 1200,810,800,701,600 };
for (int gmver : gmvers)
writers.add(new ProjectWriter(gmver));
}
public static interface GroupFilter
{
FileFilter getGroupFilter();
FileFilter[] getFilters();
}
public static interface FileReader
{
boolean canRead(URI uri);
void read(InputStream is, ProjectFile file, URI pathname, ResNode root) throws ProjectFormatException;
}
public static interface FileWriter
{
void write(OutputStream out, ProjectFile f, ResNode root) throws ProjectFormatException,
IOException;
String getSelectionName();
String getExtension();
FormatFlavor getFlavor();
}
public void addOpenFilters(GroupFilter gf)
{
addFilters(openFs,openAllFilter,gf);
}
public void addSaveFilters(GroupFilter gf)
{
addFilters(saveFs,saveAllFilter,gf);
}
public static void addFilters(FilterSet fs, FilterUnion all, GroupFilter gf)
{
fs.add(gf.getGroupFilter());
all.add(gf.getGroupFilter());
for (FileFilter ff : gf.getFilters())
fs.add(ff);
if (all.size() == 2) fs.add(0,all);
}
/**
* Typically you construct a FileChooser when you want a graphical side of things.
* Headless applications should use the static methods and fields available.
*/
public FileChooser()
{
fc.setFileView(new FileViewUnion());
addDefaultReadersAndWriters();
addOpenFilters(projectReader);
selectedWriter = writers.get(0); //TODO: need a better way to pick a default...
addSaveFilters(new ProjectWriterFilter());
}
public class LGMDropHandler extends FileDropHandler
{
private static final long serialVersionUID = 1L;
public boolean importData(TransferHandler.TransferSupport evt)
{
List<?> files = getDropList(evt);
if (files == null || files.isEmpty()) return false;
if (files.size() != 1) return false; //handle multiple files down the road
Object o = files.get(0);
if (o instanceof File)
{
open(((File) o).toURI());
return true;
}
if (o instanceof URI)
{
open((URI) o);
return true;
}
return false;
}
}
public static abstract class FileDropHandler extends TransferHandler
{
private static final long serialVersionUID = 1L;
public static final String MIME_URI_LIST = "uri-list";
@SuppressWarnings("static-method")
public boolean isDataFlavorSupported(DataFlavor df)
{
return df.isFlavorJavaFileListType()
|| (df.isRepresentationClassReader() && MIME_URI_LIST.equals(df.getSubType()));
}
public boolean canImport(TransferHandler.TransferSupport evt)
{
//Mac won't let us grab the transferable.
return getSupportedFlavor(evt.getDataFlavors()) != null;
}
protected DataFlavor getSupportedFlavor(DataFlavor...dfs)
{
for (DataFlavor df : dfs)
if (isDataFlavorSupported(df)) return df;
return null;
}
public List<?> getDropList(TransferHandler.TransferSupport evt)
{
Transferable tr = evt.getTransferable();
DataFlavor df = getSupportedFlavor(evt.getDataFlavors());
if (df == null) return null;
try
{
if (df.isFlavorJavaFileListType())
return (List<?>) tr.getTransferData(DataFlavor.javaFileListFlavor);
//Linux support (uri-list reader)
if (!df.isRepresentationClassReader() || !MIME_URI_LIST.equals(df.getSubType()))
return null; //Or not? Let implementation handle it.
BufferedReader br = new BufferedReader(df.getReaderForText(tr));
List<URI> uriList = new LinkedList<URI>();
String line;
while ((line = br.readLine()) != null)
{
try
{
// kde seems to append a 0 char to the end of the reader
if (line.isEmpty() || line.length() == 1 && line.charAt(0) == (char) 0) continue;
uriList.add(new URI(line));
}
catch (URISyntaxException ex)
{
//Omit bad URI files from list.
}
catch (IllegalArgumentException ex)
{
//Omit unresolvable URLs from list.
}
}
br.close();
return uriList;
}
catch (UnsupportedFlavorException e)
{
//Looks like our flavor suddenly deserted us. Oh well.
}
catch (IOException e)
{
//The flavor or the reader is misbehaving. Oh well.
}
return null;
}
}
private class FileViewUnion extends FileView
{
@Override
public String getName(File f)
{
for (FileView fv : fileViews)
{
String val = fv.getName(f);
if (val != null) return val;
}
return super.getName(f);
}
@Override
public String getDescription(File f)
{
for (FileView fv : fileViews)
{
String val = fv.getDescription(f);
if (val != null) return val;
}
return super.getDescription(f);
}
@Override
public String getTypeDescription(File f)
{
for (FileView fv : fileViews)
{
String val = fv.getTypeDescription(f);
if (val != null) return val;
}
return super.getTypeDescription(f);
}
@Override
public Icon getIcon(File f)
{
for (FileView fv : fileViews)
{
Icon val = fv.getIcon(f);
if (val != null) return val;
}
return super.getIcon(f);
}
@Override
public Boolean isTraversable(File f)
{
for (FileView fv : fileViews)
{
Boolean val = fv.isTraversable(f);
if (val != null) return val;
}
return super.isTraversable(f);
}
}
public static class FilterUnion extends FileFilter
{
List<FileFilter> filters = new ArrayList<FileFilter>();
public FilterUnion(FileFilter...filters)
{
add(filters);
}
public void add(FileFilter...filters)
{
for (FileFilter ff : filters)
this.filters.add(ff);
}
public int size()
{
return filters.size();
}
@Override
public boolean accept(File f)
{
for (FileFilter ff : filters)
if (ff.accept(f)) return true;
return false;
}
@Override
public String getDescription()
{
return Messages.getString("FileChooser.ALL_SUPPORTED"); //$NON-NLS-1$
}
}
protected static class ProjectReader implements FileReader,GroupFilter
{
protected CustomFileFilter[] filters;
protected CustomFileFilter groupFilter;
protected ProjectReader()
{
String[] exts = { ".gm81",".gmk",".gm6",".gmd",".gmx" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
String[] descs = { "GM81","GMK","GM6","GMD","GMX" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
groupFilter = new CustomFileFilter(Messages.getString("FileChooser.FORMAT_READERS_GM"),exts); //$NON-NLS-1$
filters = new CustomFileFilter[exts.length];
for (int i = 0; i < exts.length; i++)
filters[i] = new CustomFileFilter(
Messages.getString("FileChooser.FORMAT_" + descs[i]),exts[i]); //$NON-NLS-1$
}
public FileFilter getGroupFilter()
{
return groupFilter;
}
public FileFilter[] getFilters()
{
return filters;
}
public boolean canRead(URI f)
{
return groupFilter.accept(new File(f));
}
public void read(InputStream is, ProjectFile file, URI uri, ResNode root) throws ProjectFormatException
{
// TODO: This should not be here. ProjectFile should always have its format set correctly so
// we known which one to delegate to.
if (uri.getPath().endsWith(".project.gmx"))
{
GMXFileReader.readProjectFile(is,file,uri,root);
}
else
{
GmFileReader.readProjectFile(is,file,uri,root);
}
}
}
protected static class ProjectWriter implements FileWriter
{
int ver;
public ProjectWriter(int ver)
{
this.ver = ver;
}
public void write(OutputStream out, ProjectFile f, ResNode root) throws ProjectFormatException
{
if (f.format == FormatFlavor.GMX_1200)
{
try
{
GMXFileWriter.writeProjectFile(out,f,root,ver);
}
catch (Exception e)
{
throw new GmFormatException(f,e);
}
}
else
{
try
{
GmFileWriter.writeProjectFile(out,f,root,ver);
}
catch (IOException e)
{
throw new GmFormatException(f,e);
}
}
}
public String getSelectionName()
{
//XXX: Externalize the version string?
return Integer.toString(ver);
}
public FormatFlavor getFlavor()
{
return FormatFlavor.getVersionFlavor(ver);
}
public String getExtension()
{
switch (ver)
{
case 530:
return ".gmd";
case 600:
return ".gm6";
case 701:
case 800:
return ".gmk";
case 810:
return ".gm81";
case 1200:
return ".project.gmx";
default:
throw new IllegalArgumentException(Integer.toString(ver));
}
}
}
protected class ProjectWriterFilter implements GroupFilter
{
protected CustomFileFilter[] filters;
protected CustomFileFilter groupFilter;
protected ProjectWriterFilter()
{
final String exts[] = { ".gm81",".gmk",".gm6",".gmx" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
final String[] descs = { "GM81","GMK","GM6","GMX" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
groupFilter = new CustomFileFilter(Messages.getString("FileChooser.FORMAT_WRITERS_GM"),exts); //$NON-NLS-1$
filters = new CustomFileFilter[exts.length];
for (int i = 0; i < exts.length; i++)
filters[i] = new CustomFileFilter(
Messages.getString("FileChooser.FORMAT_" + descs[i]),exts[i]); //$NON-NLS-1$
}
public FileFilter[] getFilters()
{
return filters;
}
public FileFilter getGroupFilter()
{
return groupFilter;
}
}
public static void setTitleURI(URI uri)
{
LGM.frame.setTitle(Messages.format("LGM.TITLE",getTitleFromURI(uri))); //$NON-NLS-1$
}
private static String getTitleFromURI(URI uri)
{
if (uri == null) return Messages.getString("LGM.NEWGAME"); //$NON-NLS-1$
try
{
return new File(uri).getName();
}
catch (IllegalArgumentException e)
{
return uri.toString();
}
}
public void newFile()
{
setTitleURI(null);
LGM.newRoot();
LGM.currentFile = new ProjectFile();
LGM.populateTree();
fc.setSelectedFile(new File(new String()));
selectedWriter = null;
LGM.reload(true);
OutputManager.append("\n" + Messages.getString("FileChooser.PROJECTCREATED") + ": " + new Date().toString());
}
public void openNewFile()
{
fc.setFilterSet(openFs);
fc.setAccessory(null);
if (fc.showOpenDialog(LGM.frame) != CustomFileChooser.APPROVE_OPTION) return;
File f = fc.getSelectedFile();
if (f == null) return;
open(f.toURI());
}
/** Note that passing in null will cause an open dialog to display */
public void open(File file)
{
if (file == null || !file.exists())
{
int result = JOptionPane.showConfirmDialog(LGM.frame,
"Would you like to choose a different file?","File Not Found",JOptionPane.YES_NO_OPTION,
JOptionPane.ERROR_MESSAGE);
if (result == JOptionPane.YES_OPTION) {
openNewFile();
}
return;
}
FileReader reader = findReader(file.toURI());
if (reader == null)
{
String title = Messages.getString("FileChooser.UNRECOGNIZED_TITLE"); //$NON-NLS-1$
String message = Messages.format("FileChooser.UNRECOGNIZED",file); //$NON-NLS-1$
JOptionPane.showMessageDialog(LGM.frame,message,title,JOptionPane.WARNING_MESSAGE);
return;
}
open(file.toURI(),reader);
}
/** Note that passing in null will cause an open dialog to display */
public void open(URI uri)
{
if (uri == null)
{
openNewFile();
return;
}
FileReader reader = findReader(uri);
if (reader == null)
{
String title = Messages.getString("FileChooser.UNRECOGNIZED_TITLE"); //$NON-NLS-1$
String message = Messages.format("FileChooser.UNRECOGNIZED",uri); //$NON-NLS-1$
JOptionPane.showMessageDialog(LGM.frame,message,title,JOptionPane.WARNING_MESSAGE);
return;
}
open(uri,reader);
}
/**
* Both open() methods are not headless. For a headless open:
* <code>findReader(uri).read(uriStream,uri,root)</code>
*/
public void open(final URI uri, final FileReader reader)
{
if (uri == null) return;
LGM.getProgressDialog().setVisible(false);
Thread t = new Thread(new Runnable()
{
public void run()
{
LGM.addDefaultExceptionHandler();
try
{
ProjectFile f = new ProjectFile();
f.uri = uri;
reader.read(uri.toURL().openStream(),f,uri,LGM.newRoot());
LGM.currentFile = f;
}
catch (ProjectFormatException ex)
{
LGM.currentFile = ex.file;
LGM.populateTree();
rebuildTree();
LGM.showDefaultExceptionHandler(ex);
ErrorDialog.getInstance().setMessage(Messages.getString("FileChooser.ERROR_LOAD"));
ErrorDialog.getInstance().setTitle(Messages.getString("FileChooser.ERROR_LOAD_TITLE"));
}
catch (Exception e)
{
// TODO: This catches exceptions in reading without freezing the program with the
// progress bar or destroying the tree.
LGM.populateTree();
rebuildTree();
LGM.showDefaultExceptionHandler(e);
ErrorDialog.getInstance().setMessage(Messages.getString("FileChooser.ERROR_LOAD"));
ErrorDialog.getInstance().setTitle(Messages.getString("FileChooser.ERROR_LOAD_TITLE"));
}
setTitleURI(uri);
PrefsStore.addRecentFile(uri.toString());
((GmMenuBar) LGM.frame.getJMenuBar()).updateRecentFiles();
selectedWriter = null;
LGM.setProgressDialogVisible(false);
OutputManager.append("\n" + Messages.getString("FileChooser.PROJECTLOADED") + ": " +
new Date().toString() + " " + uri.getPath());
}
});
t.start();
LGM.setProgressDialogVisible(true);
LGM.reload(true);
}
public static FileReader findReader(URI uri)
{
for (FileReader fr : readers)
if (fr.canRead(uri)) return fr;
return null;
}
private static void rebuildTree()
{
for (int i = 0; i < LGM.root.getChildCount(); i++)
{
TreeNode n = LGM.root.getChildAt(i);
if (!(n instanceof ResNode)) continue;
ResNode rn = (ResNode) n;
if (rn.status != ResNode.STATUS_PRIMARY || !rn.isInstantiable()) continue;
ResourceList<?> rl = (ResourceList<?>) LGM.currentFile.resMap.get(rn.kind);
for (Resource<?,?> r : rl)
rn.add(new ResNode(r.getName(),ResNode.STATUS_SECONDARY,r.getClass(),r.reference));
}
}
public boolean saveNewFile()
{
fc.setFilterSet(saveFs);
//Populated fresh each time to ensure an up-to-date list of writers
fc.setAccessory(makeSelectionAccessory());
URI uri = LGM.currentFile.uri;
File file = uri == null ? null : new File(uri);
fc.setSelectedFile(file);
uri = null;
do //repeatedly display dialog until a valid response is given
{
if (fc.showSaveDialog(LGM.frame) != JFileChooser.APPROVE_OPTION) return false;
file = fc.getSelectedFile();
if (forceExt.isSelected())
{
String ext = selectedWriter.getExtension();
if (!file.getName().endsWith(ext)) file = new File(file.getPath() + ext);
}
// Create the folder for the user, otherwise people get confused.
if (file.getName().endsWith(".project.gmx"))
{
file = new File(file.getAbsolutePath().replace(".project.gmx",".gmx") + "/"
+ file.getName());
file.getParentFile().mkdir();
}
int result = JOptionPane.YES_OPTION;
if (file.exists())
result = JOptionPane.showConfirmDialog(
LGM.frame,
Messages.format("FileChooser.CONFIRM_REPLACE",file.getPath()), //$NON-NLS-1$
Messages.getString("FileChooser.CONFIRM_REPLACE_TITLE"),JOptionPane.YES_NO_CANCEL_OPTION, //$NON-NLS-1$
JOptionPane.WARNING_MESSAGE);
if (result == JOptionPane.YES_OPTION) uri = file.toURI();
if (result == JOptionPane.CANCEL_OPTION) return false;
}
while (uri == null);
return save(uri,selectedWriter.getFlavor());
}
/**
* This method is not headless. For a headless save:
* <code>save(uri,findWriter(flavor))</code>
*/
public boolean save(URI uri, FormatFlavor flavor)
{
selectedWriter = findWriter(flavor);
System.out.println(selectedWriter == null ? "null writer" : selectedWriter.getSelectionName());
if (uri == null || selectedWriter == null) return saveNewFile();
LGM.currentFile.format = flavor;
if (uri != LGM.currentFile.uri)
{
LGM.currentFile.uri = uri;
setTitleURI(uri);
PrefsStore.addRecentFile(uri.toString());
((GmMenuBar) LGM.frame.getJMenuBar()).updateRecentFiles();
}
LGM.commitAll();
String ext = selectedWriter.getExtension();
if (!uri.getPath().endsWith(ext))
{
int result = JOptionPane.showConfirmDialog(LGM.frame,
Messages.format("FileChooser.CONFIRM_EXTENSION",ext,selectedWriter.getSelectionName()), //$NON-NLS-1$
uri.toString(),JOptionPane.YES_NO_CANCEL_OPTION);
if (result == JOptionPane.CANCEL_OPTION) return false;
if (result == JOptionPane.NO_OPTION) return saveNewFile();
//if result == yes then continue
}
attemptBackup();
try
{
save(uri,selectedWriter);
return true;
}
catch (IOException e)
{
e.printStackTrace();
JOptionPane.showMessageDialog(LGM.frame,Messages.format("FileChooser.ERROR_SAVE", //$NON-NLS-1$
uri,e.getClass().getName(),e.getMessage()),
Messages.getString("FileChooser.ERROR_SAVE_TITLE"),JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$
return false;
}
}
/** This method is headless-safe. */
public static void save(final URI uri, final FileWriter writer) throws IOException
{
LGM.resetChanges();
System.out.println(uri);
LGM.getProgressDialog().setVisible(false);
Thread t = new Thread(new Runnable()
{
public void run()
{
LGM.addDefaultExceptionHandler();
try
{
writer.write(new FileOutputStream(new File(uri)),LGM.currentFile,LGM.root);
OutputManager.append("\n" + Messages.getString("FileChooser.PROJECTSAVED") + ": " +
new Date().toString() + " " + uri.getPath());
LGM.setProgressDialogVisible(false);
return;
}
catch (ProjectFormatException e)
{
LGM.showDefaultExceptionHandler(e);
}
catch (Exception e)
{
LGM.showDefaultExceptionHandler(e);
}
URLConnection uc = null;
try
{
uc = uri.toURL().openConnection();
}
catch (Exception e)
{
LGM.showDefaultExceptionHandler(e);
}
uc.setDoOutput(true);
try
{
writer.write(uc.getOutputStream(),LGM.currentFile,LGM.root);
}
catch (ProjectFormatException e)
{
LGM.showDefaultExceptionHandler(e);
}
catch (Exception e)
{
LGM.showDefaultExceptionHandler(e);
}
LGM.setProgressDialogVisible(false);
}
});
t.start();
LGM.setProgressDialogVisible(true);
}
public FileWriter findWriter(FormatFlavor flavor)
{
if (flavor == null)
{
System.out.println("null flavor");
return null;
}
// Already have a selected writer? Don't need to find one (or worry about ambiguity)
if (selectedWriter != null && selectedWriter.getFlavor() == flavor) return selectedWriter;
// Else, look for writers that support our flavor
FileWriter first = null;
for (FileWriter writer : writers)
if (writer.getFlavor() == flavor)
{
if (first == null)
first = writer; //found one
else
{
System.out.println("two flavor writers");
// we found another writer supporting our flavor, leading to ambiguity
// usually, we resolve this by opening a Save As dialog and let the user pick one.
return null;
}
}
if (first == null) System.out.println("No registered writer for flavor");
return first;
}
public static boolean attemptBackup()
{
if (pushBackups(new File(LGM.currentFile.uri))) return true;
int result = JOptionPane.showOptionDialog(LGM.frame,Messages.format("FileChooser.ERROR_BACKUP", //$NON-NLS-1$
LGM.currentFile.uri),Messages.getString("FileChooser.ERROR_BACKUP_TITLE"), //$NON-NLS-1$
JOptionPane.YES_NO_OPTION,JOptionPane.ERROR_MESSAGE,null,null,null);
return result == JOptionPane.YES_OPTION;
}
private static boolean pushBackups(File f)
{
String fn = f.getPath();
int nb = PrefsStore.getNumberOfBackups();
if (nb <= 0 || !new File(fn).exists()) return true;
String bn;
if (fn.endsWith(".gm6") || fn.endsWith(".gmk"))
bn = fn.substring(0,fn.length() - 4);
else if (fn.endsWith(".gm81"))
bn = fn.substring(0,fn.length() - 5);
else
bn = fn;
block:
{
String ff = "%s.gb%d";
int i;
for (i = 1; i <= nb; i++)
{
String bf = String.format(ff,bn,i);
if (!new File(bf).exists()) break;
}
if (i > nb)
{
i = nb;
if (!new File(String.format(ff,bn,i)).delete()) break block;
}
for (i--; i >= 0; i--)
{
File bf = new File(i > 0 ? String.format(ff,bn,i) : fn);
if (!bf.renameTo(new File(String.format(ff,bn,i + 1)))) break block;
}
return true;
}
return false;
}
JCheckBox forceExt = new JCheckBox(Messages.getString("FileChooser.FORCE_EXT"),true); //$NON-NLS-1$
JPanel makeSelectionAccessory()
{
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p,BoxLayout.PAGE_AXIS));
ButtonGroup bg = new ButtonGroup();
selectedWriter = findWriter(LGM.currentFile.format);
// pick an arbitrary default
if (selectedWriter == null) selectedWriter = writers.get(0);
for (final FileWriter writer : writers)
{
JRadioButton b = new JRadioButton(writer.getSelectionName(),selectedWriter == writer);
bg.add(b);
p.add(b);
b.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
selectedWriter = writer;
}
});
}
JPanel r = new JPanel();
r.setLayout(new BoxLayout(r,BoxLayout.PAGE_AXIS));
r.add(new JScrollPane(p));
r.add(forceExt);
return r;
}
}