/*******************************************************************************
* Rhythos Editor is a game editor and project management tool for making RPGs on top of the Rhythos Game system.
*
* Copyright (C) 2013 David Maletz
*
* This program 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.
*
* This program 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 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 mrpg.editor.resource;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;
import java.util.HashMap;
import javax.swing.Icon;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.filechooser.FileFilter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import mrpg.editor.MapEditor;
import mrpg.editor.WorkspaceBrowser;
import mrpg.editor.WorkspaceBrowser.ExtFileFilter;
public abstract class Resource extends DefaultMutableTreeNode {
private static final long serialVersionUID = -3848661454388754599L;
private File file; private String name; public final MapEditor editor;
protected Resource(File f, MapEditor e){editor = e; _setFile(f);}
protected void _setName(String n){name = n;}
private void _setFile(File f){
file = f; if(file == null) name = "";
else {name = file.getName(); int i = name.lastIndexOf('.'); if(i != -1) name = name.substring(0, i);}
}
public File changeName(String n) throws Exception {
if(name != null && name.equals(n)) return null; String ext = file.getName(); int i = ext.lastIndexOf('.');
if(i == -1) ext = ""; else ext = ext.substring(i);
File f = new File(file.getParent(),n+ext);
if(f.exists()){
JOptionPane.showMessageDialog(editor, "Unable to rename \'"+getName()+"\'.\n\'"+n+"\' already exists.", "Unable to Rename", JOptionPane.ERROR_MESSAGE);
throw new Exception();
} return f;
}
public void updateName(){((DefaultTreeModel)editor.getBrowser().getModel()).nodeChanged(this);}
public void rename(File f) throws Exception {
if(name == null || file.renameTo(f)){
_setFile(f); updateName();
} else{
JOptionPane.showMessageDialog(editor, "Unable to rename \'"+name+"\'.\nPerhaps the file is open in another program?", "Unable to Rename", JOptionPane.ERROR_MESSAGE);
throw new Exception();
}
}
public abstract long getId();
public void setName(String n) throws Exception {
n = MapEditor.safeName(n); if(n.length() == 0) throw new Exception();
File f = changeName(n); if(f != null) rename(f);
}
public String getName(){
if(name == null){
String n = file.getName(); int i = n.lastIndexOf('.'); if(i != -1) n = n.substring(0, i); return n;
} else return name;
}
public File getFile(){return file;}
public boolean edit(){return false;}
public void remove(boolean delete) throws Exception {
for(int i=0; i<getChildCount(); i++) getChild(i).remove(delete);
if(delete && !file.delete()){
JOptionPane.showMessageDialog(editor, "Unable to delete \'"+name+"\'.\nPerhaps the file is open in another program?", "Unable to Delete", JOptionPane.ERROR_MESSAGE);
throw new Exception();
}
}
public void addToProject(Project p, boolean changeProject) throws Exception {
for(int i=0; i<getChildCount(); i++) getChild(i).addToProject(p, changeProject);
}
public abstract int getHeaderSize();
public void properties(){}
public boolean hasProperties(){return false;}
public void contextMenu(JPopupMenu menu){}
public Resource getParent(){return (Resource)super.getParent();}
public Resource getChild(int i){return (Resource)getChildAt(i);}
public boolean canDelete(){return true;}
public boolean canAddChildren(){return false;}
public int indexOf(Resource r){for(int i=0; i<getChildCount(); i++) if(getChild(i) == r) return i; return -1;}
public boolean contains(Resource r){
for(int i=0; i<getChildCount(); i++) if(getChild(i) == r) return true;
return false;
}
public abstract Icon getIcon();
protected abstract void read(File f) throws Exception ;
public void deferredRead(File f) throws Exception {}
public boolean isCompatible(Project p){return true;}
public void copyAssets(Project p) throws Exception {}
public File changeDirectory(File dir, Project p, boolean allowRename, boolean checkCompatible) throws Exception {
if((!allowRename && dir.equals(file.getParentFile())) || (checkCompatible && !isCompatible(p))) throw new Exception();
String ext = file.getName(); int idx = ext.lastIndexOf('.');
if(idx == -1) ext = ""; else ext = ext.substring(idx);
File f = new File(dir.toString(),name+ext);
if(f.exists()){
String n = (String)JOptionPane.showInputDialog(editor, "Enter a new name for \'"+name+"\':", "Name Conflict", JOptionPane.PLAIN_MESSAGE, null, null, "Copy Of "+name);
if(n == null) throw new Exception(); else f = new File(dir.toString(),n+ext);
} if(f.exists()) throw new Exception(); return f;
}
public static void copyFile(File from, File to) throws Exception {
FileChannel source = null, destination = null;
try{
source = new FileInputStream(from).getChannel();
destination = new FileOutputStream(to).getChannel();
destination.transferFrom(source, 0, source.size());
} finally {if(source != null) source.close(); if(destination != null) destination.close();}
}
public static void copyDir(File from, File to){
for(File f : from.listFiles()){
try{
File n = new File(to, f.getName());
if(f.isDirectory()){n.mkdir(); copyDir(f, n);}
else copyFile(f, n);
}catch(Exception e){}
}
}
public File copy(File dir, Project p, boolean checkCompatible) throws Exception {
if(!dir.isDirectory()) throw new Exception(); File f = changeDirectory(dir, p, true, checkCompatible);
copyFile(file, f); return f;
}
public void refresh() throws Exception {
remove(false); removeAllChildren(); read(file);
}
public String toString(){return getName();}
//A synonym for WorkspaceBrowser.getProject(this) if the resource is added to a project. Otherwise, it returns the project where
//this resource WILL be inserted (assuming it is being added in the tree view), or null if there are no projects.
//Use to get the Project during resource creation (before it has been added).
public Project getProject(){
Project p = WorkspaceBrowser.getProject(this);
if(p == null){
WorkspaceBrowser b = editor.getBrowser();
TreePath path; if(b.isSelectionEmpty() && b.getRowCount() == 0){path = null;}
else if(b.isSelectionEmpty()) path = b.getPathForRow(0);
else path = b.getSelectionPath();
if(path != null && path.getPathCount() > 1) p = (Project)path.getPathComponent(1);
} return p;
}
private static final HashMap<String, Class<? extends Resource>> resources = new HashMap<String, Class<? extends Resource>>();
private static final HashMap<String, Class<? extends Resource>> types = new HashMap<String, Class<? extends Resource>>();
public static JFileChooser resourceChooser = new JFileChooser();
static {
resourceChooser.setAcceptAllFileFilterUsed(false); resourceChooser.setMultiSelectionEnabled(true);
resourceChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
}
public abstract String getExt();
public static void register(String name, String ext, String type, Class<? extends Resource> r) throws Exception {
if(resources.containsKey(ext) || types.containsKey(type)) throw new Exception(); types.put(type, r);
resources.put(ext, r); resourceChooser.addChoosableFileFilter(new ExtFileFilter(name, new String[]{ext}));
}
public static void unregister(String ext){
resources.remove(ext); for(FileFilter f : resourceChooser.getChoosableFileFilters()){
if(((ExtFileFilter)f).getExt()[0] == ext){resourceChooser.removeChoosableFileFilter(f); break;}
}
}
public static Resource readFile(File f, MapEditor e) throws Exception {
if(f.isDirectory()){
Resource ret = new Folder(f, e); ret.read(f); return ret;
} else {
String ext = f.getName(); int idx = ext.lastIndexOf('.');
if(idx == -1) throw new Exception();
Class<? extends Resource> r = resources.get(ext.substring(idx+1)); if(r == null) throw new Exception();
Resource ret = r.getConstructor(File.class, MapEditor.class).newInstance(f, e); ret.read(f); return ret;
}
}
public static void register() throws Exception {
Folder.import_options.addItem("Resource File", "database", KeyEvent.VK_R, ActionEvent.CTRL_MASK, new ImportResourceAction());
}
private static class ImportResourceAction implements ActionListener {
public void actionPerformed(ActionEvent e){
MapEditor.instance.getBrowser().importResources();
}
}
}