/*
* VFSDirectoryEntryTable.java - VFS directory entry table
* :tabSize=4:indentSize=4:noTabs=false:
* :folding=explicit:collapseFolds=1:
*
* Copyright (C) 2003, 2005 Slava Pestov
*
* 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 2
* of the License, or 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.gjt.sp.jedit.browser;
//{{{ Imports
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.*;
import java.util.LinkedList;
import java.util.Set;
import org.gjt.sp.jedit.browser.VFSDirectoryEntryTableModel.Entry;
import org.gjt.sp.jedit.io.VFS;
import org.gjt.sp.jedit.io.VFSFile;
import org.gjt.sp.jedit.io.VFSManager;
import org.gjt.sp.jedit.msg.VFSPathSelected;
import org.gjt.sp.jedit.ActionContext;
import org.gjt.sp.jedit.EditAction;
import org.gjt.sp.jedit.EditBus;
import org.gjt.sp.jedit.MiscUtilities;
import org.gjt.sp.jedit.GUIUtilities;
import org.gjt.sp.jedit.jEdit;
import org.gjt.sp.util.Log;
import org.gjt.sp.util.ThreadUtilities;
//}}}
/**
* @author Slava Pestov
* @version $Id: VFSDirectoryEntryTable.java 23222 2013-09-29 20:43:34Z shlomy $
* @since jEdit 4.2pre1
*/
public class VFSDirectoryEntryTable extends JTable
{
//{{{ VFSDirectoryEntryTable constructor
VFSDirectoryEntryTable(BrowserView browserView)
{
super(new VFSDirectoryEntryTableModel());
this.browserView = browserView;
setShowGrid(false);
setIntercellSpacing(new Dimension(0,0));
setDefaultRenderer(Entry.class,
renderer = new FileCellRenderer());
header = getTableHeader();
header.setReorderingAllowed(false);
addMouseListener(new MainMouseHandler());
header.addMouseListener(new MouseHandler());
header.setDefaultRenderer(new HeaderRenderer(
(DefaultTableCellRenderer)header.getDefaultRenderer()));
setRowSelectionAllowed(true);
getColumnModel().addColumnModelListener(new ColumnHandler());
setAutoResizeMode(AUTO_RESIZE_OFF);
} //}}}
//{{{ selectFile() method
public boolean selectFile(String path)
{
for(int i = 0; i < getRowCount(); i++)
{
Entry entry = (Entry) getValueAt(i,1);
if(entry.dirEntry.getPath().equals(path))
{
setSelectedRow(i);
return true;
}
}
return false;
} //}}}
//{{{ doTypeSelect() method
public void doTypeSelect(String str, boolean dirsOnly)
{
if(str.length() == 0)
clearSelection();
else if(getSelectedRow() == -1)
doTypeSelect(str,0,getRowCount(),dirsOnly);
else
{
int start = getSelectionModel().getMaxSelectionIndex();
boolean retVal = doTypeSelect(str,start,getRowCount(),
dirsOnly);
if(!retVal)
{
// scan from selection to end failed, so
// scan from start to selection
doTypeSelect(str,0,start,dirsOnly);
}
}
} //}}}
//{{{ getSelectedFiles() method
public VFSFile[] getSelectedFiles()
{
VFSDirectoryEntryTableModel model
= (VFSDirectoryEntryTableModel)getModel();
java.util.List<VFSFile> returnValue = new LinkedList<VFSFile>();
int[] selectedRows = getSelectedRows();
for (int selectedRow : selectedRows)
returnValue.add(model.files[selectedRow].dirEntry);
return returnValue.toArray(new VFSFile[returnValue.size()]);
} //}}}
//{{{ getExpandedDirectories() method
public void getExpandedDirectories(Set<String> set)
{
VFSDirectoryEntryTableModel model
= (VFSDirectoryEntryTableModel)getModel();
if(model.files != null)
{
for(int i = 0; i < model.files.length; i++)
{
if(model.files[i].expanded)
set.add(model.files[i].dirEntry.getPath());
}
}
} //}}}
//{{{ toggleExpanded() method
public void toggleExpanded(final int row)
{
VFSDirectoryEntryTableModel model
= (VFSDirectoryEntryTableModel)getModel();
Entry entry = model.files[row];
if(entry.dirEntry.getType() == VFSFile.FILE)
return;
Runnable delayedAwtTask = new Runnable()
{
public void run()
{
setSelectedRow(row);
}
};
if(entry.expanded)
{
model.collapse(VFSManager.getVFSForPath(
entry.dirEntry.getPath()),row);
resizeColumns();
ThreadUtilities.runInDispatchThread(delayedAwtTask);
}
else
{
browserView.clearExpansionState();
browserView.loadDirectory(entry,entry.dirEntry.getPath(),
false, delayedAwtTask);
}
} //}}}
//{{{ setDirectory() method
public void setDirectory(VFS vfs, Object node, java.util.List<VFSFile> list,
Set<String> tmpExpanded)
{
timer.stop();
typeSelectBuffer.setLength(0);
VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel)getModel();
int startIndex;
if(node == null)
{
startIndex = 0;
model.setRoot(vfs,list);
}
else
{
startIndex =
model.expand(
vfs,
(Entry)node,
list);
startIndex++;
}
for(int i = 0; i < list.size(); i++)
{
Entry e = model.files[startIndex + i];
String path = e.dirEntry.getPath();
if(tmpExpanded.contains(path))
{
browserView.loadDirectory(e,path,false);
tmpExpanded.remove(path);
}
}
resizeColumns();
} //}}}
//{{{ maybeReloadDirectory() method
public void maybeReloadDirectory(String path)
{
VFSDirectoryEntryTableModel model
= (VFSDirectoryEntryTableModel)getModel();
for(int i = 0; i < model.files.length; i++)
{
Entry e = model.files[i];
if(!e.expanded || e.dirEntry.getType() == VFSFile.FILE)
continue;
VFSFile dirEntry = e.dirEntry;
// work around for broken FTP plugin!
String otherPath;
if(dirEntry.getSymlinkPath() == null)
otherPath = dirEntry.getPath();
else
otherPath = dirEntry.getSymlinkPath();
if(MiscUtilities.pathsEqual(path,otherPath))
{
browserView.saveExpansionState();
browserView.loadDirectory(e,path,false);
return;
}
}
} //}}}
//{{{ propertiesChanged() method
public void propertiesChanged()
{
renderer.propertiesChanged();
VFSFile template = new VFSFile(
"foo","foo","foo",VFSFile.FILE,0L,false);
setRowHeight(renderer.getTableCellRendererComponent(
this,new Entry(template,0),
false,false,0,0).getPreferredSize().height);
Dimension prefSize = getPreferredSize();
setPreferredScrollableViewportSize(new Dimension(prefSize.width,
getRowHeight() * 12));
} //}}}
//{{{ scrollRectToVisible() method
@Override
public void scrollRectToVisible(Rectangle rect)
{
// avoid scrolling to the right
rect.width = 0;
super.scrollRectToVisible(rect);
} //}}}
//{{{ processKeyEvent() method
@Override
public void processKeyEvent(KeyEvent evt)
{
if(evt.getID() == KeyEvent.KEY_PRESSED)
{
VFSDirectoryEntryTableModel model =
(VFSDirectoryEntryTableModel)getModel();
int row = getSelectedRow();
ActionContext ac = VFSBrowser.getActionContext();
ActionContext jac = jEdit.getActionContext();
EditAction browserUp = ac.getAction("vfs.browser.up");
VFSBrowser browser = browserView.getBrowser();
switch(evt.getKeyCode())
{
case KeyEvent.VK_LEFT:
evt.consume();
if ((evt.getModifiers() & InputEvent.ALT_MASK)>0)
{
browser.previousDirectory();
}
else
{
if(row != -1)
{
if(model.files[row].expanded)
{
toggleExpanded(row);
return;
}
for(int i = row - 1; i >= 0; i--)
{
if(model.files[i].expanded &&
model.files[i].level < model.files[row].level)
{
setSelectedRow(i);
return;
}
}
}
String dir = browserView.getBrowser()
.getDirectory();
dir = MiscUtilities.getParentOfPath(dir);
browserView.getBrowser().setDirectory(dir);
}
break;
case KeyEvent.VK_TAB:
evt.consume();
if ((evt.getModifiers() & InputEvent.SHIFT_MASK) > 0)
{
browserView.getParentDirectoryList().requestFocus();
}
else
{
browser.focusOnDefaultComponent();
}
break;
case KeyEvent.VK_BACK_SPACE:
evt.consume();
ac.invokeAction(evt, browserUp);
break;
case KeyEvent.VK_UP:
if ((evt.getModifiers() & InputEvent.ALT_MASK) >0)
{
evt.consume();
ac.invokeAction(evt, browserUp);
}
break;
case KeyEvent.VK_DELETE:
evt.consume();
EditAction deleteAct = ac.getAction("vfs.browser.delete");
ac.invokeAction(evt, deleteAct);
break;
case KeyEvent.VK_N:
if ((evt.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) == InputEvent.CTRL_DOWN_MASK)
{
evt.consume();
EditAction ea = ac.getAction("vfs.browser.new-file");
ac.invokeAction(evt, ea);
}
break;
case KeyEvent.VK_INSERT:
evt.consume();
EditAction newDir = ac.getAction("vfs.browser.new-directory");
ac.invokeAction(evt, newDir);
break;
case KeyEvent.VK_ESCAPE:
EditAction cda = ac.getAction("vfs.browser.closedialog");
ac.invokeAction(evt, cda);
break;
case KeyEvent.VK_F2:
EditAction ren = ac.getAction("vfs.browser.rename");
evt.consume();
ac.invokeAction(evt, ren);
break;
case KeyEvent.VK_F5:
evt.consume();
EditAction reload= ac.getAction("vfs.browser.reload");
ac.invokeAction(evt, reload);
break;
case KeyEvent.VK_F6:
case KeyEvent.VK_RIGHT:
evt.consume();
if ((evt.getModifiers() & InputEvent.ALT_MASK)>0)
{
browser.nextDirectory();
}
else if(row != -1)
{
if(!model.files[row].expanded)
toggleExpanded(row);
}
break;
case KeyEvent.VK_ENTER:
evt.consume();
browserView.getBrowser().filesActivated(
evt.isShiftDown()
? VFSBrowser.M_OPEN_NEW_VIEW
: VFSBrowser.M_OPEN,false);
break;
}
}
else if(evt.getID() == KeyEvent.KEY_TYPED)
{
if(evt.isControlDown() || evt.isAltDown()
|| evt.isMetaDown())
{
evt.consume();
return;
}
// hack...
if(evt.isShiftDown() && evt.getKeyChar() == '\n')
{
evt.consume();
return;
}
VFSBrowser browser = browserView.getBrowser();
switch(evt.getKeyChar())
{
case '~':
evt.consume();
if(browser.getMode() == VFSBrowser.BROWSER)
browser.setDirectory(System.getProperty(
"user.home"));
break;
case '/':
evt.consume();
if(browser.getMode() == VFSBrowser.BROWSER)
browser.rootDirectory();
break;
case '-':
evt.consume();
if(browser.getMode() == VFSBrowser.BROWSER)
{
browser.setDirectory(
browser.getView().getBuffer()
.getDirectory());
}
break;
default:
evt.consume();
typeSelectBuffer.append(evt.getKeyChar());
doTypeSelect(typeSelectBuffer.toString(),
browser.getMode() == VFSBrowser
.CHOOSE_DIRECTORY_DIALOG);
timer.stop();
timer.setInitialDelay(750);
timer.setRepeats(false);
timer.start();
return;
}
}
if(!evt.isConsumed())
super.processKeyEvent(evt);
} //}}}
//{{{ setSelectedRow() method
public void setSelectedRow(int row)
{
getSelectionModel().setSelectionInterval(row,row);
scrollRectToVisible(getCellRect(row,0,true));
} //}}}
//{{{ Private members
private final BrowserView browserView;
private final JTableHeader header;
private final FileCellRenderer renderer;
private final StringBuffer typeSelectBuffer = new StringBuffer();
private final Timer timer = new Timer(0,new ClearTypeSelect());
private boolean resizingColumns;
//{{{ doTypeSelect() method
private boolean doTypeSelect(String str, int start, int end,
boolean dirsOnly)
{
VFSFile[] files = ((VFSDirectoryEntryTableModel)
getModel()).getFiles();
int index = VFSFile.findCompletion(files,start,end,str,dirsOnly);
if(index != -1)
{
setSelectedRow(index);
return true;
}
else
return false;
} //}}}
//{{{ resizeColumns() method
private void resizeColumns()
{
VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel)getModel();
FontRenderContext fontRenderContext = new FontRenderContext(
null,false,false);
int[] widths = new int[model.getColumnCount()];
for(int i = 0; i < widths.length; i++)
{
String columnName = model.getColumnName(i);
if(columnName != null)
{
widths[i] = (int)renderer.plainFont
.getStringBounds(columnName,
fontRenderContext).getWidth();
}
}
for(int i = 1; i < widths.length; i++)
{
//String extAttr = model.getExtendedAttribute(i);
widths[i] = Math.max(widths[i],model.getColumnWidth(i));
}
for(int i = 0; i < model.files.length; i++)
{
Entry entry = model.files[i];
Font font = entry.dirEntry.getType()
== VFSFile.FILE
? renderer.plainFont : renderer.boldFont;
widths[0] = Math.max(widths[0],renderer.getEntryWidth(
entry,font,fontRenderContext));
}
widths[0] += 10;
TableColumnModel columns = getColumnModel();
try
{
resizingColumns = true;
for(int i = 0; i < widths.length; i++)
{
columns.getColumn(i).setPreferredWidth(widths[i]);
columns.getColumn(i).setWidth(widths[i]);
}
}
finally
{
resizingColumns = false;
}
doLayout();
} //}}}
//{{{ saveWidths() method
private void saveWidths()
{
if(resizingColumns)
return;
VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel)getModel();
TableColumnModel columns = getColumnModel();
for(int i = 1; i < model.getColumnCount(); i++)
model.setColumnWidth(i,columns.getColumn(i).getWidth());
} //}}}
//}}}
//{{{ ClearTypeSelect class
class ClearTypeSelect implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
typeSelectBuffer.setLength(0);
}
} //}}}
//{{{ ColumnHandler class
class ColumnHandler implements TableColumnModelListener
{
public void columnAdded(TableColumnModelEvent e) {}
public void columnRemoved(TableColumnModelEvent e) {}
public void columnMoved(TableColumnModelEvent e) {}
public void columnSelectionChanged(ListSelectionEvent e) {}
public void columnMarginChanged(ChangeEvent e)
{
saveWidths();
}
} //}}}
//{{{ class MainMouseHandler
class MainMouseHandler extends MouseInputAdapter
{
@Override
public void mouseClicked(MouseEvent e)
{
super.mouseClicked(e);
if (browserView.getBrowser().getMode() != VFSBrowser.BROWSER) return;
int ind = getSelectionModel().getMinSelectionIndex();
if (ind == -1)
return;
Entry node = (Entry) getModel().getValueAt(ind, 0);
boolean isDir = node.dirEntry.getType() == VFSFile.DIRECTORY;
EditBus.send(new VFSPathSelected(browserView.getBrowser().getView(),
node.dirEntry.getPath(), isDir));
}
} //}}}
//{{{ MouseHandler class
class MouseHandler extends MouseInputAdapter
{
@Override
public void mousePressed(MouseEvent evt)
{
// double click on columns header
if (evt.getSource() == header && evt.getClickCount() == 2)
{
VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) header.getTable().getModel();
TableColumnModel columnModel = header.getColumnModel();
int viewColumn = columnModel.getColumnIndexAtX(evt.getX());
int column = columnModel.getColumn(viewColumn).getModelIndex();
saveWidths();
if(model.sortByColumn(column))
{
resizeColumns();
Log.log(Log.DEBUG,this,"VFSDirectoryEntryTable sorted by "
+ model.getColumnName(column)
+ (model.getAscending() ? " ascending" : " descending") );
}
}
}
} //}}}
//{{{ HeaderRenderer
static class HeaderRenderer extends DefaultTableCellRenderer
{
private final DefaultTableCellRenderer tcr;
HeaderRenderer(DefaultTableCellRenderer tcr)
{
this.tcr = tcr;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
JLabel l = (JLabel)tcr.getTableCellRendererComponent(table,value,isSelected,hasFocus,row,column);
VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel)table.getModel();
Icon icon = column == model.getSortColumn()
? model.getAscending() ? ASC_ICON : DESC_ICON
: null;
l.setIcon(icon);
// l.setHorizontalTextPosition(l.LEADING);
return l;
}
} //}}}
//{{{ SortOrder Icons
static final Icon ASC_ICON = GUIUtilities.loadIcon("arrow-asc.png");
static final Icon DESC_ICON = GUIUtilities.loadIcon("arrow-desc.png");
//}}}
}