/******************************************************************************* * 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; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Rectangle; import java.awt.dnd.DragSource; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.io.File; import java.util.ArrayList; import javax.swing.BorderFactory; import javax.swing.JFileChooser; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JTree; import javax.swing.border.Border; import javax.swing.filechooser.FileFilter; import javax.swing.plaf.basic.BasicTreeUI; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import mrpg.editor.resource.AutoTile; import mrpg.editor.resource.Folder; import mrpg.editor.resource.Image; import mrpg.editor.resource.Map; import mrpg.editor.resource.Media; import mrpg.editor.resource.Modifiable; import mrpg.editor.resource.Project; import mrpg.editor.resource.Resource; import mrpg.editor.resource.Script; import mrpg.editor.resource.Tileset; import mrpg.editor.resource.Workspace; import mrpg.script.HaxeCompiler; public class WorkspaceBrowser extends JTree implements ActionListener, MouseListener, MouseMotionListener { private static final long serialVersionUID = -2561363020096125820L; public static JFileChooser imgChooser = new JFileChooser(), sndChooser = new JFileChooser(); public static class ExtFileFilter extends FileFilter { private String[] ext; private String name; public ExtFileFilter(String n, String[] _ext){ ext = _ext; StringBuilder b = new StringBuilder(); b.append(n); b.append(" ("); for(String e : ext){ b.append("*."); b.append(e); if(e != ext[ext.length-1]) b.append(","); } b.append(")"); name = b.toString(); } public String[] getExt(){return ext;} public boolean accept(File f){ if(f == null || f.toString() == null) return false; if(f.isDirectory()) return true; String s = f.toString(); for(String e : ext){ if(s.endsWith(e)) return true; } return false; } public String getDescription(){return name;} } static { imgChooser.setAcceptAllFileFilterUsed(false); imgChooser.setFileFilter(new ExtFileFilter("Image Files", new String[]{".png",".jpeg",".jpg",".gif"})); imgChooser.setMultiSelectionEnabled(true); imgChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); sndChooser.setAcceptAllFileFilterUsed(false); sndChooser.setFileFilter(new ExtFileFilter("Audio Files", new String[]{".wav",".mp3"})); sndChooser.setMultiSelectionEnabled(true); sndChooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); } public static final String EDIT_ICON = "edit"; private final int NO_DRAG=0, CHECK_DRAG=1, HAS_DRAG=2; private final MapEditor editor; private final JPopupMenu context_menu; private Resource dragTo = null, dragLine = null; private int dragType = NO_DRAG; private int shouldSelect = -1; private Resource clipboard[] = null; public WorkspaceBrowser(MapEditor e){ super(new Workspace(e)); editor = e; setCellRenderer(new ProjectTreeRenderer()); context_menu = new JPopupMenu(); context_menu.setOpaque(true); context_menu.setLightWeightPopupEnabled(true); MouseListener m = getMouseListeners()[0]; addMouseListener(this); addMouseMotionListener(this); removeMouseListener(m); addMouseListener(m); setFocusable(false); getSelectionModel().setSelectionMode(TreeSelectionModel.CONTIGUOUS_TREE_SELECTION); setAutoscrolls(true); setRootVisible(false); ((BasicTreeUI)getUI()).setExpandedIcon(MapEditor.getIcon("expand")); ((BasicTreeUI)getUI()).setCollapsedIcon(MapEditor.getIcon("collapse")); putClientProperty("JTree.lineStyle", "None"); } public Workspace getWorkspace(){return (Workspace)getModel().getRoot();} public void addProject(Project p){ DefaultTreeModel m = (DefaultTreeModel)getModel(); Workspace w = (Workspace)m.getRoot(); m.insertNodeInto(p, w, w.getChildCount()); TreePath path = new TreePath(new Object[]{w, p}); expandPath(path); setSelectionPath(path); scrollPathToVisible(path); editor.updateSaveButtons(); MapEditor.doDeferredRead(false); if(!editor.hasMap()) p.getResources(Map.TYPE).iterator().next().edit(); } public Project addProject(File f) throws Exception { Project p = Project.openProject(editor, (Workspace)getModel().getRoot(), f); addProject(p); return p; } public static Project getProject(TreePath path){return (path.getPathCount() <= 1)?null:(Project)path.getPathComponent(1);} public static Project getProject(Resource node){ while(true){ node = node.getParent(); if(node == null) return null; if(node instanceof Project) return (Project)node; } } public void addResource(Resource r, Resource parent){ DefaultTreeModel m = (DefaultTreeModel)getModel(); m.insertNodeInto(r, parent, parent.getChildCount()); TreePath path = new TreePath(r.getPath()); setSelectionPath(path); scrollPathToVisible(path); } public void removeResource(Resource r, boolean delete){ try{r.remove(delete);}catch(Exception ex){} ((DefaultTreeModel)getModel()).removeNodeFromParent(r); } private void selectProjectError() throws Exception { JOptionPane.showMessageDialog(editor, "Please select a project or folder to import into.", "Unable to Import", JOptionPane.ERROR_MESSAGE); throw new Exception(); } private Resource getInsertResource() throws Exception { if(isSelectionEmpty() && getRowCount() == 0) selectProjectError(); TreePath path; if(isSelectionEmpty()) path = getPathForRow(0); else path = getSelectionPath(); Resource parent = getResource(path); if(parent == null) selectProjectError(); while(!parent.canAddChildren()){ parent = parent.getParent(); if(parent == null) selectProjectError(); } return parent; } private void importImage(Resource parent, File f, Project p, boolean sub){ String dir = parent.getFile().toString(); String n = f.getName(); int idx = n.lastIndexOf('.'); if(idx != -1) n = n.substring(0,idx); Resource r; try{ if(f.isDirectory()){ r = Folder.create(new File(dir,n), editor); for(File c : f.listFiles()) if(sub || !c.isDirectory()) importImage(r, c, p, sub); } else { r = Image.importImage(f, new File(dir,n+"."+Image.EXT), editor, p); } addResource(r, parent); }catch(Exception e){} } private void importMedia(Resource parent, File f, Project p, boolean sub){ String dir = parent.getFile().toString(); String n = f.getName(); int idx = n.lastIndexOf('.'); if(idx != -1) n = n.substring(0,idx); Resource r; try{ if(f.isDirectory()){ r = Folder.create(new File(dir,n), editor); for(File c : f.listFiles()) if(sub || !c.isDirectory()) importMedia(r, c, p, sub); } else { r = Media.importMedia(f, new File(dir,n+"."+Media.EXT), editor, p); } addResource(r, parent); }catch(Exception e){} } private void importResource(Resource parent, File f, Project p){ String dir = parent.getFile().toString(); try{ if(!f.isDirectory()){ File f2 = new File(dir,f.getName()); Resource.copyFile(f, f2); addResource(Resource.readFile(f2, editor), parent); } }catch(Exception e){} } public void importImages(){ if(imgChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION){ try{ Resource parent = getInsertResource(); boolean sub = false; for(File f : imgChooser.getSelectedFiles()) if(f.isDirectory()) sub = true; if(sub) sub = JOptionPane.showConfirmDialog(this, "Include Subdirectories?", "Import Images", JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.YES_OPTION; for(File f : imgChooser.getSelectedFiles()) importImage(parent, f, getProject(parent), sub); MapEditor.doDeferredRead(true); } catch(Exception ex){} } } public void importMedia(){ if(sndChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION){ try{ Resource parent = getInsertResource(); boolean sub = false; for(File f : sndChooser.getSelectedFiles()) if(f.isDirectory()) sub = true; if(sub) sub = JOptionPane.showConfirmDialog(this, "Include Subdirectories?", "Import Media", JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.YES_OPTION; for(File f : sndChooser.getSelectedFiles()) importMedia(parent, f, getProject(parent), sub); MapEditor.doDeferredRead(true); } catch(Exception ex){} } } public void importResources(){ if(Resource.resourceChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION){ try{ Resource parent = getInsertResource(); for(File f : Resource.resourceChooser.getSelectedFiles()) importResource(parent, f, getProject(parent)); MapEditor.doDeferredRead(true); } catch(Exception ex){} } } public void addFolder(){ try{ Resource parent = getInsertResource(); String name = (String)JOptionPane.showInputDialog(editor, "Enter a name for the new folder:", "Create New Folder", JOptionPane.PLAIN_MESSAGE, null, null, "New Folder"); if(name == null) throw new Exception(); String dir = parent.getFile().toString(); File f = new File(dir, name); if(f.exists()){ JOptionPane.showMessageDialog(editor, "\'"+name+"\' already exists!", "Create New Folder", JOptionPane.ERROR_MESSAGE); throw new Exception(); } addResource(Folder.create(f, editor), parent); }catch(Exception e){} } public void addMap(){ try{ Resource parent = getInsertResource(); Map m = Map.createMap(parent, editor, getProject(parent)); addResource(m, parent); if(editor.getWorld() == null) m.edit(); }catch(Exception e){} } public void addResource(Class<? extends Resource> cls){ try{ Resource parent = getInsertResource(); Resource r = (Resource)cls.getDeclaredMethod("create", Resource.class, MapEditor.class, Project.class). invoke(null, parent, editor, getProject(parent)); addResource(r, parent); }catch(Exception e){} } public void addScript(){ try{ Resource parent = getInsertResource(); String name = (String)JOptionPane.showInputDialog(editor, "Enter a name for the new script:", "Create Script", JOptionPane.PLAIN_MESSAGE, null, null, "New Script"); if(name == null) throw new Exception(); String dir = parent.getFile().toString(); File f = new File(dir,name+"."+Script.EXT); if(f.exists()){ JOptionPane.showMessageDialog(editor, "\'"+name+"\' already exists!", "Create Script", JOptionPane.ERROR_MESSAGE); throw new Exception(); } addResource(Script.createScript(f, editor, getProject(parent)), parent); }catch(Exception e){} } public void addTileset(){ try{ Resource parent = getInsertResource(); Tileset t = Tileset.createTileset(parent, editor, getProject(parent)); addResource(t, parent); if(editor.getTilesetViewer().getTilemap() == null) editor.getTilesetViewer().setTilemap(t.getTilemap(), getProject(t)); }catch(Exception e){} } public void addAutoTile(){ try{ Resource parent = getInsertResource(); addResource(AutoTile.createAutoTile(parent, editor, getProject(parent)), parent); }catch(Exception e){} } private static final Border underline=BorderFactory.createMatteBorder(1,0,0,0,Color.black), empty=BorderFactory.createEmptyBorder(1,0,0,0); private class ProjectTreeRenderer extends DefaultTreeCellRenderer { private static final long serialVersionUID = -6438073546792557714L; public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus){ selected |= value == dragTo; super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus); setIcon(((Resource)value).getIcon()); if(value == dragLine) setBorder(underline); else setBorder(empty); return this; } } public Resource getSelectedResource(){return getResource(getSelectionPath());} public static Resource getResource(TreePath p){return (Resource)p.getLastPathComponent();} public static Object[] getPathToRoot(Resource r, Object root){ ArrayList<Object> path = new ArrayList<Object>(); while(true){ path.add(r); if(r == root) break; r = r.getParent(); } Object p[] = new Resource[path.size()]; for(int i=0; i<path.size(); i++) p[path.size()-1-i] = path.get(i); return p; } public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); if(command == MapEditor.SAVE) saveSelection(); else if(command == MapEditor.SAVE_ALL) saveAll(); else if(command == MapEditor.REVERT) revertSelection(); else if(command == MapEditor.OPEN){ try{ addProject(Project.openProject(editor, (Workspace)getModel().getRoot())); }catch(Exception ex){} } else if(command == MapEditor.DELETE){ deleteSelection(); } else if(command == MapEditor.REFRESH){ refreshSelection(); } else if(command == MapEditor.REMOVE){ Resource r = getSelectedResource(); if(innerAnyModified(r)){ int i = JOptionPane.showConfirmDialog(this, "Save all maps before removing project?"); if(i == JOptionPane.NO_OPTION) removeResource(r, false); else if(i == JOptionPane.YES_OPTION){ innerSaveAll(r); removeResource(r, false); editor.updateSaveButtons(); } } else removeResource(r, false); } else if(command == MapEditor.RENAME){ Resource r = getSelectedResource(); String s = r.getName(); String name = JOptionPane.showInputDialog(this, "Enter new name for \""+s+"\":", s); if(name == null || name.length() == 0) return; try{ r.setName(name); }catch(Exception ex){} } else if(command == EDIT_ICON){getSelectedResource().edit(); } else if(command == MapEditor.PROPERTIES){getSelectedResource().properties(); } else if(command == Project.PROJECT) try{ Project p = Project.createProject(editor); addProject(p); MapEditor.doDeferredRead(true); if(!editor.hasMap()) p.getResources(Map.TYPE).iterator().next().edit(); }catch(Exception ex){} else if(command == MapEditor.BUILD){ try{ Project p = getProject(getSelectedResource()); innerSaveAll(p); editor.updateSaveButtons(); HaxeCompiler.compile(p); }catch(Exception ex){ex.printStackTrace();} } else if(command == MapEditor.TEST){ try{ Project p = getProject(getSelectedResource()); innerSaveAll(p); editor.updateSaveButtons(); HaxeCompiler.compile(p); HaxeCompiler.run(p); }catch(Exception ex){ex.printStackTrace();} } } public void mouseClicked(MouseEvent e) {} public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) { editor.gainBrowserFocus(); int r = getRowForLocation(e.getX(), e.getY()); if(r != -1 && isRowSelected(r)){e.consume(); shouldSelect = r;} dragType = CHECK_DRAG; } public boolean canRefreshSelection(){ return getSelectionCount() > 0; } public boolean canDeleteSelection(){ int ct = getSelectionCount(); if(ct == 0) return false; if(ct > 1){ for(TreePath p : getSelectionPaths()) if(!getResource(p).canDelete()) return false; return true; } else return getResource(getSelectionPath()).canDelete(); } public static int trimChildren(JTree t, TreePath[] paths){ int ret = 0; for(int i=0; i<paths.length; i++){ if(t.isPathSelected(paths[i].getParentPath())) paths[i] = null; else ret++; } return ret; } public TreePath[] getSelectionPathsTrimChildren(){TreePath[] ret = getSelectionPaths(); trimChildren(this, ret); return ret;} public void deleteSelection(){ int ct = getSelectionCount(); String del = (ct == 1)?"\""+getResource(getSelectionPath())+"\"?":"the "+ct+" selected resources?"; if(JOptionPane.showConfirmDialog(this, "Are you sure you wish to permanently delete "+del+"?\nThis operation cannot be undone.") == JOptionPane.YES_OPTION){ for(TreePath p : getSelectionPathsTrimChildren()){ if(p == null) continue; Resource r = getResource(p); try{ removeResource(r, true); } catch(Exception ex){} } } editor.updateSaveButtons(); } public void refreshSelection(){ DefaultTreeModel m = (DefaultTreeModel)getModel(); for(TreePath p : getSelectionPathsTrimChildren()){ if(p == null) continue; Resource r = getResource(p); try{r.refresh(); m.reload(r);}catch(Exception e){} } MapEditor.doDeferredRead(false); } public void revertSelection(){ DefaultTreeModel m = (DefaultTreeModel)getModel(); for(TreePath p : getSelectionPathsTrimChildren()){ if(p == null) continue; try{ Resource r = getResource(p); Modifiable mod = (Modifiable)r; if(mod.isModified()){mod.revert(); m.reload(r);} }catch(Exception e){} } MapEditor.doDeferredRead(false); editor.updateSaveButtons(); } public void saveSelection(){ for(TreePath p : getSelectionPathsTrimChildren()){ if(p == null) continue; try{Modifiable r = (Modifiable)getResource(p); if(r.isModified()) r.save();}catch(Exception e){} } MapEditor.doDeferredRead(false); editor.updateSaveButtons(); } private void innerSaveAll(Resource r){ for(int i=0; i<r.getChildCount(); i++){ Resource c = r.getChild(i); innerSaveAll(c); try{Modifiable m = (Modifiable)c; if(m.isModified()) m.save();}catch(Exception e){} } } public void saveAll(){innerSaveAll(getWorkspace()); editor.updateSaveButtons();} public boolean selectionModified(){ int ct = getSelectionCount(); if(ct == 0) return false; if(ct > 1){ for(TreePath p : getSelectionPaths()){ Resource r = getResource(p); if(r instanceof Modifiable && ((Modifiable)r).isModified()) return true; } return false; } else{ Resource r = getResource(getSelectionPath()); return r instanceof Modifiable && ((Modifiable)r).isModified(); } } private boolean innerAnyModified(Resource r){ for(int i=0; i<r.getChildCount(); i++){ Resource c = r.getChild(i); if(innerAnyModified(c)) return true; if(c instanceof Modifiable && ((Modifiable)c).isModified()) return true; } return false; } public boolean anyModified(){return innerAnyModified(getWorkspace());} public void copy(){ TreePath paths[] = getSelectionPaths(); clipboard = new Resource[trimChildren(this, paths)]; int i=0; for(TreePath p : paths){ if(p != null) clipboard[i++] = getResource(p); } } public void paste() throws Exception { if(clipboard != null){ TreePath path = getSelectionPath(); Resource parent = getInsertResource(); for(int i=0; i<clipboard.length; i++){ addResource(Resource.readFile(clipboard[i].copy(parent.getFile(), getProject(parent), true),editor), parent); if(path == null) clearSelection(); else setSelectionPath(path); } } MapEditor.doDeferredRead(true); } public boolean hasClipboardData(){return clipboard != null;} public void mouseReleased(MouseEvent e) { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); if(dragTo != null || dragLine != null){ DefaultTreeModel m = (DefaultTreeModel)getModel(); if(dragTo != null){ for(TreePath p : getSelectionPathsTrimChildren()){ if(p == null) continue; Resource r = getResource(p); try{ Project project = getProject(dragTo); r.rename(r.changeDirectory(dragTo.getFile(),project,false,true)); if(project != WorkspaceBrowser.getProject(r)){ removeResource(r, false); r.addToProject(project, true); } else m.removeNodeFromParent(r); m.insertNodeInto(r, dragTo, dragTo.getChildCount()); } catch(Exception ex){} } } else { if(!isPathSelected(new TreePath(m.getPathToRoot(dragLine)))){ Resource parent = dragLine.getParent(); for(TreePath p : getSelectionPathsTrimChildren()){ if(p == null) continue; Resource r = getResource(p); try{ Project project = getProject(parent); r.rename(r.changeDirectory(parent.getFile(),project,false,true)); if(project != WorkspaceBrowser.getProject(r)){ removeResource(r, false); r.addToProject(project, true); } else m.removeNodeFromParent(r); m.insertNodeInto(r, parent, parent.getIndex(dragLine)); } catch(Exception ex){} } } } dragTo = null; dragLine = null; shouldSelect = -1; repaint(); } dragType = NO_DRAG; if(e.isPopupTrigger()){ int r = getRowForLocation(e.getX(), e.getY()); context_menu.removeAll(); if(r == -1){ context_menu.add(add_project); context_menu.add(open_project); } else { if(!isRowSelected(r)) setSelectionRow(r); int ct = getSelectionCount(); if(ct == 0) return; if(ct > 1){ for(TreePath p : getSelectionPaths()) if(!getResource(p).canDelete()) return; context_menu.add(delete_all); context_menu.add(refresh_all); } else { Resource res = getResource(getSelectionPath()); res.contextMenu(context_menu); if(res.canDelete()) context_menu.add(delete); else context_menu.add(remove); context_menu.add(refresh); context_menu.add(rename); } } context_menu.show(this, e.getX(), e.getY()); shouldSelect = -1; } if(e.getClickCount() % 2 == 0 && getSelectionCount() == 1){ int r = getRowForLocation(e.getX(), e.getY()); if(r != -1){ if(!getResource(getPathForRow(r)).edit()){ if(isExpanded(r)) collapseRow(r); else expandRow(r); } } } if(shouldSelect != -1){ if(!e.isShiftDown() && getSelectionCount() != 1) setSelectionRow(shouldSelect); shouldSelect = -1; } } public final JMenuItem delete = MapEditor.createMenuItemIcon("Delete", MapEditor.DELETE, this), delete_all = MapEditor.createMenuItemIcon("Delete Selected", MapEditor.DELETE, this), refresh = MapEditor.createMenuItemIcon("Refresh", MapEditor.REFRESH, this), refresh_all = MapEditor.createMenuItemIcon("Refresh Selected", MapEditor.REFRESH, this), save = MapEditor.createMenuItemIcon("Save", MapEditor.SAVE, this), revert = MapEditor.createMenuItemIcon("Revert", MapEditor.REVERT, this), remove = MapEditor.createMenuItemIcon("Remove Project", MapEditor.DELETE, MapEditor.REMOVE, this), rename = MapEditor.createMenuItemIcon("Rename", MapEditor.RENAME, this), edit = MapEditor.createMenuItemIcon("Edit", EDIT_ICON, this), properties = MapEditor.createMenuItemIcon("Properties", MapEditor.PROPERTIES, this), add_project = MapEditor.createMenuItemIcon("Create New Project", Project.PROJECT, this), open_project = MapEditor.createMenuItemIcon("Open Existing Project", MapEditor.OPEN, this); private boolean notSelected(TreePath p){ while(p != null){ if(isPathSelected(p)) return false; p = p.getParentPath(); } return true; } private boolean canDrag(int close, int r){ TreePath p = getPathForRow(close); if(r == close) return getResource(p).canAddChildren() && notSelected(p); else return p.getPathCount() > 1 && ((Resource)(p.getPathComponent(p.getPathCount()-2))).canAddChildren() && notSelected(isRowSelected(close-1)?p:p.getParentPath()); } public void mouseDragged(MouseEvent e) { scrollRectToVisible(new Rectangle(e.getX(), e.getY(), 1, 1)); if(dragType == NO_DRAG) return; if(dragType == CHECK_DRAG){ int r = getRowForLocation(e.getX(), e.getY()); if(r == -1 || !isRowSelected(r)){dragType = NO_DRAG; return;} Resource res = getResource(getPathForRow(r)); if(!res.canDelete()){dragType = NO_DRAG;} else dragType = HAS_DRAG; if(dragType == NO_DRAG){setCursor(DragSource.DefaultMoveNoDrop); return;} } shouldSelect = -1; dragTo = null; dragLine = null; if(e.getX() > getWidth() || e.getY() > getHeight()){setCursor(DragSource.DefaultMoveNoDrop); repaint(); return;} int close = getClosestRowForLocation(e.getX(), e.getY()); int r = getRowForLocation(e.getX(), e.getY()); if(r != -1 && !getResource(getPathForRow(r)).canAddChildren()) r = -1; if(canDrag(close, r)){ setCursor(DragSource.DefaultMoveDrop); if(r == close) dragTo = getResource(getPathForRow(close)); else dragLine = getResource(getPathForRow(close)); } else setCursor(DragSource.DefaultMoveNoDrop); repaint(); } public void mouseMoved(MouseEvent e) {} private boolean innerExpand(Resource r, File f){ for(int i=0; i<r.getChildCount(); i++){ Resource c = r.getChild(i); if(innerExpand(c, f)) return true; if(c.getFile().equals(f)){expandPath(new TreePath(c.getPath())); return true;} } return false; } public void expand(File f){innerExpand(getWorkspace(), f);} }