001: /*
002: * VFSDirectoryEntryTable.java - VFS directory entry table
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2003, 2005 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.browser;
024:
025: //{{{ Imports
026: import javax.swing.event.*;
027: import javax.swing.table.*;
028: import javax.swing.*;
029: import java.awt.event.*;
030: import java.awt.font.*;
031: import java.awt.*;
032: import java.util.LinkedList;
033: import java.util.Set;
034:
035: import org.gjt.sp.jedit.browser.VFSDirectoryEntryTableModel.Entry;
036: import org.gjt.sp.jedit.io.VFS;
037: import org.gjt.sp.jedit.io.VFSFile;
038: import org.gjt.sp.jedit.io.VFSManager;
039: import org.gjt.sp.jedit.msg.VFSPathSelected;
040: import org.gjt.sp.jedit.ActionContext;
041: import org.gjt.sp.jedit.EditAction;
042: import org.gjt.sp.jedit.EditBus;
043: import org.gjt.sp.jedit.MiscUtilities;
044: import org.gjt.sp.jedit.GUIUtilities;
045: import org.gjt.sp.jedit.jEdit;
046: import org.gjt.sp.util.Log;
047:
048: //}}}
049:
050: /**
051: * @author Slava Pestov
052: * @version $Id: VFSDirectoryEntryTable.java 11009 2007-11-09 22:06:28Z ezust $
053: * @since jEdit 4.2pre1
054: */
055: public class VFSDirectoryEntryTable extends JTable {
056: //{{{ VFSDirectoryEntryTable constructor
057: public VFSDirectoryEntryTable(BrowserView browserView) {
058: super (new VFSDirectoryEntryTableModel());
059: this .browserView = browserView;
060: setShowGrid(false);
061:
062: setIntercellSpacing(new Dimension(0, 0));
063:
064: setDefaultRenderer(VFSDirectoryEntryTableModel.Entry.class,
065: renderer = new FileCellRenderer());
066:
067: header = getTableHeader();
068: header.setReorderingAllowed(false);
069: addMouseListener(new MainMouseHandler());
070: header.addMouseListener(new MouseHandler());
071: header
072: .setDefaultRenderer(new HeaderRenderer(
073: (DefaultTableCellRenderer) header
074: .getDefaultRenderer()));
075:
076: setRowSelectionAllowed(true);
077:
078: getColumnModel().addColumnModelListener(new ColumnHandler());
079:
080: setAutoResizeMode(AUTO_RESIZE_OFF);
081: } //}}}
082:
083: //{{{ selectFile() method
084: public boolean selectFile(String path) {
085: for (int i = 0; i < getRowCount(); i++) {
086: VFSDirectoryEntryTableModel.Entry entry = (VFSDirectoryEntryTableModel.Entry) getValueAt(
087: i, 1);
088: if (entry.dirEntry.getPath().equals(path)) {
089: setSelectedRow(i);
090: return true;
091: }
092: }
093:
094: return false;
095: } //}}}
096:
097: //{{{ doTypeSelect() method
098: public void doTypeSelect(String str, boolean dirsOnly) {
099: if (str.length() == 0)
100: clearSelection();
101: else if (getSelectedRow() == -1)
102: doTypeSelect(str, 0, getRowCount(), dirsOnly);
103: else {
104: int start = getSelectionModel().getMaxSelectionIndex();
105: boolean retVal = doTypeSelect(str, start, getRowCount(),
106: dirsOnly);
107:
108: if (!retVal) {
109: // scan from selection to end failed, so
110: // scan from start to selection
111: doTypeSelect(str, 0, start, dirsOnly);
112: }
113: }
114: } //}}}
115:
116: //{{{ getSelectedFiles() method
117: public VFSFile[] getSelectedFiles() {
118: VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) getModel();
119:
120: LinkedList<VFSFile> returnValue = new LinkedList<VFSFile>();
121: int[] selectedRows = getSelectedRows();
122: for (int i = 0; i < selectedRows.length; i++) {
123: returnValue.add(model.files[selectedRows[i]].dirEntry);
124: }
125: return returnValue.toArray(new VFSFile[returnValue.size()]);
126: } //}}}
127:
128: //{{{ getExpandedDirectories() method
129: public void getExpandedDirectories(Set<String> set) {
130: VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) getModel();
131:
132: if (model.files != null) {
133: for (int i = 0; i < model.files.length; i++) {
134: if (model.files[i].expanded)
135: set.add(model.files[i].dirEntry.getPath());
136: }
137: }
138: } //}}}
139:
140: //{{{ toggleExpanded() method
141: public void toggleExpanded(final int row) {
142: VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) getModel();
143:
144: VFSDirectoryEntryTableModel.Entry entry = model.files[row];
145: if (entry.dirEntry.getType() == VFSFile.FILE)
146: return;
147:
148: if (entry.expanded) {
149: model.collapse(VFSManager.getVFSForPath(entry.dirEntry
150: .getPath()), row);
151: resizeColumns();
152: } else {
153: browserView.clearExpansionState();
154: browserView.loadDirectory(entry, entry.dirEntry.getPath(),
155: false);
156: }
157:
158: VFSManager.runInAWTThread(new Runnable() {
159: public void run() {
160: setSelectedRow(row);
161: }
162: });
163: } //}}}
164:
165: //{{{ setDirectory() method
166: public void setDirectory(VFS vfs, Object node,
167: java.util.List<VFSFile> list, Set<String> tmpExpanded) {
168: timer.stop();
169: typeSelectBuffer.setLength(0);
170:
171: VFSDirectoryEntryTableModel model = ((VFSDirectoryEntryTableModel) getModel());
172: int startIndex;
173: if (node == null) {
174: startIndex = 0;
175: model.setRoot(vfs, list);
176: } else {
177: startIndex = model.expand(vfs,
178: (VFSDirectoryEntryTableModel.Entry) node, list);
179: startIndex++;
180: }
181:
182: for (int i = 0; i < list.size(); i++) {
183: VFSDirectoryEntryTableModel.Entry e = model.files[startIndex
184: + i];
185: String path = e.dirEntry.getPath();
186: if (tmpExpanded.contains(path)) {
187: browserView.loadDirectory(e, path, false);
188: tmpExpanded.remove(path);
189: }
190: }
191:
192: resizeColumns();
193: } //}}}
194:
195: //{{{ maybeReloadDirectory() method
196: public void maybeReloadDirectory(String path) {
197: VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) getModel();
198:
199: for (int i = 0; i < model.files.length; i++) {
200: VFSDirectoryEntryTableModel.Entry e = model.files[i];
201: if (!e.expanded || e.dirEntry.getType() == VFSFile.FILE)
202: continue;
203:
204: VFSFile dirEntry = e.dirEntry;
205: // work around for broken FTP plugin!
206: String otherPath;
207: if (dirEntry.getSymlinkPath() == null)
208: otherPath = dirEntry.getPath();
209: else
210: otherPath = dirEntry.getSymlinkPath();
211: if (MiscUtilities.pathsEqual(path, otherPath)) {
212: browserView.saveExpansionState();
213: browserView.loadDirectory(e, path, false);
214: return;
215: }
216: }
217: } //}}}
218:
219: //{{{ propertiesChanged() method
220: public void propertiesChanged() {
221: renderer.propertiesChanged();
222:
223: VFSFile template = new VFSFile("foo", "foo", "foo",
224: VFSFile.FILE, 0L, false);
225: setRowHeight(renderer.getTableCellRendererComponent(this ,
226: new VFSDirectoryEntryTableModel.Entry(template, 0),
227: false, false, 0, 0).getPreferredSize().height);
228: Dimension prefSize = getPreferredSize();
229: setPreferredScrollableViewportSize(new Dimension(
230: prefSize.width, getRowHeight() * 12));
231: } //}}}
232:
233: //{{{ scrollRectToVisible() method
234: public void scrollRectToVisible(Rectangle rect) {
235: // avoid scrolling to the right
236: rect.width = 0;
237: super .scrollRectToVisible(rect);
238: } //}}}
239:
240: //{{{ processKeyEvent() method
241: public void processKeyEvent(KeyEvent evt) {
242: if (evt.getID() == KeyEvent.KEY_PRESSED) {
243: VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) getModel();
244: int row = getSelectedRow();
245: ActionContext ac = VFSBrowser.getActionContext();
246: ActionContext jac = jEdit.getActionContext();
247: VFSBrowser browser = browserView.getBrowser();
248: switch (evt.getKeyCode()) {
249: case KeyEvent.VK_LEFT:
250: evt.consume();
251: if (row != -1) {
252: if (model.files[row].expanded) {
253: toggleExpanded(row);
254: return;
255: }
256:
257: for (int i = row - 1; i >= 0; i--) {
258: if ((model.files[i].expanded)
259: && (model.files[i].level < model.files[row].level)) {
260: setSelectedRow(i);
261: return;
262: }
263: }
264: }
265:
266: String dir = browserView.getBrowser().getDirectory();
267: dir = MiscUtilities.getParentOfPath(dir);
268: browserView.getBrowser().setDirectory(dir);
269: break;
270: case KeyEvent.VK_BACK_SPACE:
271: evt.consume();
272: EditAction ea = ac.getAction("vfs.browser.up");
273: ac.invokeAction(evt, ea);
274: break;
275: case KeyEvent.VK_DELETE:
276: evt.consume();
277: ea = ac.getAction("vfs.browser.delete");
278: ac.invokeAction(evt, ea);
279: break;
280: case KeyEvent.CTRL_MASK | KeyEvent.VK_N:
281: evt.consume();
282: ea = ac.getAction("vfs.browser.new-file");
283: ac.invokeAction(evt, ea);
284: break;
285: case KeyEvent.VK_INSERT:
286: evt.consume();
287: ea = ac.getAction("vfs.browser.new-directory");
288: ac.invokeAction(evt, ea);
289: break;
290: case KeyEvent.VK_ESCAPE:
291: ea = jac.getAction("close-docking-area");
292: ea.invoke(jEdit.getActiveView());
293: evt.consume();
294: break;
295: case KeyEvent.VK_F2:
296: ea = ac.getAction("vfs.browser.rename");
297: evt.consume();
298: ac.invokeAction(evt, ea);
299: break;
300: case KeyEvent.VK_F5:
301: evt.consume();
302: ea = ac.getAction("vfs.browser.reload");
303: ac.invokeAction(evt, ea);
304: break;
305: case KeyEvent.VK_F6:
306: case KeyEvent.VK_TAB:
307: browser.focusOnDefaultComponent();
308: evt.consume();
309: break;
310: case KeyEvent.VK_RIGHT:
311: evt.consume();
312: if (row != -1) {
313: if (!model.files[row].expanded)
314: toggleExpanded(row);
315: }
316:
317: break;
318: case KeyEvent.VK_ENTER:
319: evt.consume();
320: browserView.getBrowser().filesActivated(
321: (evt.isShiftDown() ? VFSBrowser.M_OPEN_NEW_VIEW
322: : VFSBrowser.M_OPEN), false);
323:
324: break;
325: }
326: } else if (evt.getID() == KeyEvent.KEY_TYPED) {
327:
328: if (evt.isControlDown() || evt.isAltDown()
329: || evt.isMetaDown()) {
330: evt.consume();
331: return;
332: }
333:
334: // hack...
335: if (evt.isShiftDown() && evt.getKeyChar() == '\n') {
336: evt.consume();
337: return;
338: }
339:
340: VFSBrowser browser = browserView.getBrowser();
341:
342: switch (evt.getKeyChar()) {
343: case '~':
344: evt.consume();
345: if (browser.getMode() == VFSBrowser.BROWSER)
346: browser.setDirectory(System
347: .getProperty("user.home"));
348: break;
349: case '/':
350: evt.consume();
351: if (browser.getMode() == VFSBrowser.BROWSER)
352: browser.rootDirectory();
353: break;
354: case '-':
355: evt.consume();
356: if (browser.getMode() == VFSBrowser.BROWSER) {
357: browser.setDirectory(browser.getView().getBuffer()
358: .getDirectory());
359: }
360: break;
361: default:
362: evt.consume();
363: typeSelectBuffer.append(evt.getKeyChar());
364: doTypeSelect(
365: typeSelectBuffer.toString(),
366: browser.getMode() == VFSBrowser.CHOOSE_DIRECTORY_DIALOG);
367:
368: timer.stop();
369: timer.setInitialDelay(750);
370: timer.setRepeats(false);
371: timer.start();
372: return;
373: }
374: }
375:
376: if (!evt.isConsumed())
377: super .processKeyEvent(evt);
378: } //}}}
379:
380: //{{{ setSelectedRow() method
381: public void setSelectedRow(int row) {
382: getSelectionModel().setSelectionInterval(row, row);
383: scrollRectToVisible(getCellRect(row, 0, true));
384: } //}}}
385:
386: //{{{ Private members
387: private BrowserView browserView;
388: private JTableHeader header;
389: private FileCellRenderer renderer;
390: private StringBuffer typeSelectBuffer = new StringBuffer();
391: private Timer timer = new Timer(0, new ClearTypeSelect());
392: private boolean resizingColumns;
393:
394: //{{{ doTypeSelect() method
395: private boolean doTypeSelect(String str, int start, int end,
396: boolean dirsOnly) {
397: VFSFile[] files = ((VFSDirectoryEntryTableModel) getModel())
398: .getFiles();
399:
400: int index = VFSFile.findCompletion(files, start, end, str,
401: dirsOnly);
402: if (index != -1) {
403: setSelectedRow(index);
404: return true;
405: } else
406: return false;
407: } //}}}
408:
409: //{{{ resizeColumns() method
410: private void resizeColumns() {
411: VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) getModel();
412:
413: FontRenderContext fontRenderContext = new FontRenderContext(
414: null, false, false);
415: int[] widths = new int[model.getColumnCount()];
416: for (int i = 0; i < widths.length; i++) {
417: String columnName = model.getColumnName(i);
418: if (columnName != null) {
419: widths[i] = (int) renderer.plainFont.getStringBounds(
420: columnName, fontRenderContext).getWidth();
421: }
422: }
423:
424: for (int i = 1; i < widths.length; i++) {
425: String extAttr = model.getExtendedAttribute(i);
426: widths[i] = Math.max(widths[i], model.getColumnWidth(i));
427: }
428:
429: for (int i = 0; i < model.files.length; i++) {
430: VFSDirectoryEntryTableModel.Entry entry = model.files[i];
431: Font font = (entry.dirEntry.getType() == VFSFile.FILE ? renderer.plainFont
432: : renderer.boldFont);
433:
434: widths[0] = Math.max(widths[0], renderer.getEntryWidth(
435: entry, font, fontRenderContext));
436: }
437:
438: widths[0] += 10;
439:
440: TableColumnModel columns = getColumnModel();
441:
442: try {
443: resizingColumns = true;
444: for (int i = 0; i < widths.length; i++) {
445: columns.getColumn(i).setPreferredWidth(widths[i]);
446: columns.getColumn(i).setWidth(widths[i]);
447: }
448: } finally {
449: resizingColumns = false;
450: }
451:
452: doLayout();
453: } //}}}
454:
455: //{{{ saveWidths() method
456: private void saveWidths() {
457: if (resizingColumns)
458: return;
459:
460: VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) getModel();
461: TableColumnModel columns = getColumnModel();
462:
463: for (int i = 1; i < model.getColumnCount(); i++)
464: model.setColumnWidth(i, columns.getColumn(i).getWidth());
465: } //}}}
466:
467: //}}}
468:
469: //{{{ ClearTypeSelect class
470: class ClearTypeSelect implements ActionListener {
471: public void actionPerformed(ActionEvent evt) {
472: typeSelectBuffer.setLength(0);
473: }
474: } //}}}
475:
476: //{{{ ColumnHandler class
477: class ColumnHandler implements TableColumnModelListener {
478: public void columnAdded(TableColumnModelEvent e) {
479: }
480:
481: public void columnRemoved(TableColumnModelEvent e) {
482: }
483:
484: public void columnMoved(TableColumnModelEvent e) {
485: }
486:
487: public void columnSelectionChanged(ListSelectionEvent e) {
488: }
489:
490: public void columnMarginChanged(ChangeEvent e) {
491: saveWidths();
492: }
493: } //}}}
494:
495: //{{{ class MainMouseHandler
496: class MainMouseHandler extends MouseInputAdapter {
497:
498: @Override
499: public void mouseClicked(MouseEvent e) {
500: super .mouseClicked(e);
501: int ind = getSelectionModel().getMinSelectionIndex();
502: Entry node = (Entry) (getModel().getValueAt(ind, 0));
503: boolean isDir = (node.dirEntry.getType() == VFSFile.DIRECTORY);
504: EditBus.send(new VFSPathSelected(jEdit.getActiveView(),
505: node.dirEntry.getPath(), isDir));
506: }
507:
508: } //}}}
509:
510: //{{{ MouseHandler class
511: class MouseHandler extends MouseInputAdapter {
512: public void mousePressed(MouseEvent evt) {
513: // double click on columns header
514: if (evt.getSource() == header && evt.getClickCount() == 2) {
515: VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) header
516: .getTable().getModel();
517: TableColumnModel columnModel = header.getColumnModel();
518: int viewColumn = columnModel.getColumnIndexAtX(evt
519: .getX());
520: int column = columnModel.getColumn(viewColumn)
521: .getModelIndex();
522: saveWidths();
523: if (model.sortByColumn(column)) {
524: resizeColumns();
525: Log
526: .log(
527: Log.DEBUG,
528: this ,
529: "VFSDirectoryEntryTable sorted by "
530: + model
531: .getColumnName(column)
532: + (model.getAscending() ? " ascending"
533: : " descending"));
534: }
535: }
536: }
537: } //}}}
538:
539: //{{{ HeaderRenderer
540: static class HeaderRenderer extends DefaultTableCellRenderer {
541: private DefaultTableCellRenderer tcr;
542:
543: HeaderRenderer(DefaultTableCellRenderer tcr) {
544: this .tcr = tcr;
545: }
546:
547: public Component getTableCellRendererComponent(JTable table,
548: Object value, boolean isSelected, boolean hasFocus,
549: int row, int column) {
550: JLabel l = (JLabel) tcr.getTableCellRendererComponent(
551: table, value, isSelected, hasFocus, row, column);
552: VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) table
553: .getModel();
554: Icon icon = (column == model.getSortColumn()) ? model
555: .getAscending() ? ASC_ICON : DESC_ICON : null;
556: l.setIcon(icon);
557: // l.setHorizontalTextPosition(l.LEADING);
558: return l;
559: }
560: } //}}}
561:
562: //{{{ SortOrder Icons
563:
564: static final Icon ASC_ICON = GUIUtilities.loadIcon("arrow-asc.png");
565: static final Icon DESC_ICON = GUIUtilities
566: .loadIcon("arrow-desc.png");
567:
568: //}}}
569:
570: }
|