/*******************************************************************************
* Copyright (c) 2015 - 2016
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package jsettlers.main.swing.foldertree;
import java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.net.URI;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import jsettlers.graphics.localization.Labels;
import jsettlers.main.swing.resources.SettlersFolderChecker;
/**
* Select settler root folder dialog
*
* @author Andreas Butti
*/
public class SelectSettlersFolderDialog extends JFrame {
private static final long serialVersionUID = 1L;
private static final String HELP_URL = "https://github.com/jsettlers/settlers-remake/blob/master/README.md";
private final ExecutorService executorService = Executors.newSingleThreadExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "fs-loader-thread");
thread.setDaemon(true);
return thread;
}
});
/**
* Tree model
*/
private DefaultTreeModel model;
/**
* The tree
*/
private JTree tree;
/**
* Panel with the current path
*/
private final PathPanel pathPanel = new PathPanel(new IPathPanelListener() {
@Override
public void pathChanged(Object[] newPath) {
tree.setSelectionPath(new TreePath(newPath));
}
});
/**
* Chosen folder
*/
private String selectedFolder = null;
/**
* Panel to display found result
*/
private final FolderFoundPanel foundPanel = new FolderFoundPanel(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
selectedFolder = e.getActionCommand();
synchronized (syncObject) {
syncObject.notifyAll();
}
dispose();
}
});
/**
* For synchronization only
*/
private final Object syncObject = new Object();
/**
* Listener for Tree selection
*/
private final TreeSelectionListener selectionListener = new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent event) {
TreePath path = event.getPath();
pathPanel.setPath(path.getPath());
Object lastPathComponent = path.getLastPathComponent();
if (lastPathComponent instanceof FilesystemTreeNode) {
FilesystemTreeNode fileSystemTreeNode = (FilesystemTreeNode) lastPathComponent;
if (!fileSystemTreeNode.wasExpanded()) {
fileSystemTreeNode.setWasExpanded(true);
tree.expandPath(path);
}
if (fileSystemTreeNode.isSettlersFolder()) {
foundPanel.setFolder(fileSystemTreeNode.getFile().getAbsolutePath());
} else {
foundPanel.resetFolder();
}
}
}
};
/**
* Listener for expansion
*/
private final TreeExpansionListener expansionListener = new TreeExpansionListener() {
@Override
public void treeExpanded(TreeExpansionEvent event) {
Object lastPathComponent = event.getPath().getLastPathComponent();
if (lastPathComponent instanceof FilesystemTreeNode) {
FilesystemTreeNode fileSystemTreeNode = (FilesystemTreeNode) lastPathComponent;
fileSystemTreeNode.setWasExpanded(true);
}
}
@Override
public void treeCollapsed(TreeExpansionEvent event) {
}
};
/**
* Constructor
*/
public SelectSettlersFolderDialog() {
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setTitle(Labels.getString("select-settlers-3-folder-header"));
setLayout(new BorderLayout());
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
synchronized (syncObject) {
syncObject.notifyAll();
}
}
});
SwingUtilities.invokeLater(new Runnable() { // this invoke later prevents an out of bounds exception in the tree
@Override
public void run() {
initHeader();
initTree();
add(new JScrollPane(tree), BorderLayout.CENTER);
add(foundPanel, BorderLayout.SOUTH);
setSize(750, 640);
setLocationRelativeTo(null);
}
});
}
/**
* Initialize the header panel with the label, help button and go to button
*/
private void initHeader() {
JPanel pHeader = new JPanel();
pHeader.setLayout(new BorderLayout());
JPanel pHeaderText = new JPanel();
pHeaderText.setLayout(new BorderLayout());
pHeaderText.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
pHeaderText.add(new JLabel(Labels.getString("select-settlers-3-folder")), BorderLayout.CENTER);
JPanel pButton = new JPanel();
pButton.setLayout(new FlowLayout());
JButton btGoTo = new JButton(Labels.getString("enter-path"));
btGoTo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String path = JOptionPane.showInputDialog(SelectSettlersFolderDialog.this, Labels.getString("enter-path"));
if (path != null) {
if (SettlersFolderChecker.checkSettlersFolder(path).isValidSettlersFolder()) {
foundPanel.setFolder(path);
} else {
JOptionPane.showMessageDialog(SelectSettlersFolderDialog.this, Labels.getString("settlers-folder-still-invalid"), "JSettler",
JOptionPane.ERROR_MESSAGE);
}
}
}
});
pButton.add(btGoTo);
JButton btHelp = new JButton(Labels.getString("i-need-help-button"));
btHelp.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
Desktop.getDesktop().browse(new URI(HELP_URL));
} catch (Exception e1) {
JOptionPane.showMessageDialog(SelectSettlersFolderDialog.this, Labels.getString("error-open-url") + ": " + HELP_URL);
}
}
});
pButton.add(btHelp);
pHeaderText.add(pButton, BorderLayout.EAST);
pHeader.add(pHeaderText, BorderLayout.CENTER);
pHeader.add(pathPanel, BorderLayout.SOUTH);
add(pHeader, BorderLayout.NORTH);
}
/**
* Initialize the Tree with the filesystem
*/
private void initTree() {
RootTreeNode root = new RootTreeNode(executorService);
for (File f : File.listRoots()) {
root.add(new FilesystemTreeNode(f));
}
model = new DefaultTreeModel(root);
// to fire change event when the loading is finished
root.setModel(model);
tree = new JTree(model);
tree.addTreeSelectionListener(selectionListener);
tree.addTreeExpansionListener(expansionListener);
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.expandRow(0);
tree.setRootVisible(false);
tree.setCellRenderer(new FileTreeCellRenderer());
}
/**
* Test main
*
* @param args
*/
public static void main(String[] args) {
try {
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (Exception e) {
}
SelectSettlersFolderDialog dlg = new SelectSettlersFolderDialog();
dlg.setVisible(true);
}
/**
* Blocks the caller thread, returns when the user confirmed the dialog
*
* @return Selected file
*/
public File waitForUserInput() {
synchronized (syncObject) {
try {
syncObject.wait();
} catch (InterruptedException e) { // ignore exception here
}
}
if (selectedFolder == null) {
return null;
}
return new File(selectedFolder);
}
@Override
public void dispose() {
super.dispose();
executorService.shutdownNow();
}
}