PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/jEdit/tags/jedit-4-5-pre1/org/gjt/sp/jedit/browser/VFSDirectoryEntryTable.java

#
Java | 666 lines | 530 code | 76 blank | 60 comment | 82 complexity | f344a9587633546af7a7fcdf81ccc458 MD5 | raw file
Possible License(s): BSD-3-Clause, AGPL-1.0, Apache-2.0, LGPL-2.0, LGPL-3.0, GPL-2.0, CC-BY-SA-3.0, LGPL-2.1, GPL-3.0, MPL-2.0-no-copyleft-exception, IPL-1.0
  1. /*
  2. * VFSDirectoryEntryTable.java - VFS directory entry table
  3. * :tabSize=8:indentSize=8:noTabs=false:
  4. * :folding=explicit:collapseFolds=1:
  5. *
  6. * Copyright (C) 2003, 2005 Slava Pestov
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22. package org.gjt.sp.jedit.browser;
  23. //{{{ Imports
  24. import javax.swing.event.*;
  25. import javax.swing.table.*;
  26. import javax.swing.*;
  27. import java.awt.event.*;
  28. import java.awt.font.*;
  29. import java.awt.*;
  30. import java.util.LinkedList;
  31. import java.util.Set;
  32. import org.gjt.sp.jedit.browser.VFSDirectoryEntryTableModel.Entry;
  33. import org.gjt.sp.jedit.io.VFS;
  34. import org.gjt.sp.jedit.io.VFSFile;
  35. import org.gjt.sp.jedit.io.VFSManager;
  36. import org.gjt.sp.jedit.msg.VFSPathSelected;
  37. import org.gjt.sp.jedit.ActionContext;
  38. import org.gjt.sp.jedit.EditAction;
  39. import org.gjt.sp.jedit.EditBus;
  40. import org.gjt.sp.jedit.MiscUtilities;
  41. import org.gjt.sp.jedit.GUIUtilities;
  42. import org.gjt.sp.jedit.jEdit;
  43. import org.gjt.sp.util.Log;
  44. import org.gjt.sp.util.ThreadUtilities;
  45. //}}}
  46. /**
  47. * @author Slava Pestov
  48. * @version $Id: VFSDirectoryEntryTable.java 18960 2010-11-15 19:23:52Z ezust $
  49. * @since jEdit 4.2pre1
  50. */
  51. public class VFSDirectoryEntryTable extends JTable
  52. {
  53. //{{{ VFSDirectoryEntryTable constructor
  54. VFSDirectoryEntryTable(BrowserView browserView)
  55. {
  56. super(new VFSDirectoryEntryTableModel());
  57. this.browserView = browserView;
  58. setShowGrid(false);
  59. setIntercellSpacing(new Dimension(0,0));
  60. setDefaultRenderer(Entry.class,
  61. renderer = new FileCellRenderer());
  62. header = getTableHeader();
  63. header.setReorderingAllowed(false);
  64. addMouseListener(new MainMouseHandler());
  65. header.addMouseListener(new MouseHandler());
  66. header.setDefaultRenderer(new HeaderRenderer(
  67. (DefaultTableCellRenderer)header.getDefaultRenderer()));
  68. setRowSelectionAllowed(true);
  69. getColumnModel().addColumnModelListener(new ColumnHandler());
  70. setAutoResizeMode(AUTO_RESIZE_OFF);
  71. } //}}}
  72. //{{{ selectFile() method
  73. public boolean selectFile(String path)
  74. {
  75. for(int i = 0; i < getRowCount(); i++)
  76. {
  77. Entry entry = (Entry) getValueAt(i,1);
  78. if(entry.dirEntry.getPath().equals(path))
  79. {
  80. setSelectedRow(i);
  81. return true;
  82. }
  83. }
  84. return false;
  85. } //}}}
  86. //{{{ doTypeSelect() method
  87. public void doTypeSelect(String str, boolean dirsOnly)
  88. {
  89. if(str.length() == 0)
  90. clearSelection();
  91. else if(getSelectedRow() == -1)
  92. doTypeSelect(str,0,getRowCount(),dirsOnly);
  93. else
  94. {
  95. int start = getSelectionModel().getMaxSelectionIndex();
  96. boolean retVal = doTypeSelect(str,start,getRowCount(),
  97. dirsOnly);
  98. if(!retVal)
  99. {
  100. // scan from selection to end failed, so
  101. // scan from start to selection
  102. doTypeSelect(str,0,start,dirsOnly);
  103. }
  104. }
  105. } //}}}
  106. //{{{ getSelectedFiles() method
  107. public VFSFile[] getSelectedFiles()
  108. {
  109. VFSDirectoryEntryTableModel model
  110. = (VFSDirectoryEntryTableModel)getModel();
  111. java.util.List<VFSFile> returnValue = new LinkedList<VFSFile>();
  112. int[] selectedRows = getSelectedRows();
  113. for(int i = 0; i < selectedRows.length; i++)
  114. {
  115. returnValue.add(model.files[selectedRows[i]].dirEntry);
  116. }
  117. return returnValue.toArray(new VFSFile[returnValue.size()]);
  118. } //}}}
  119. //{{{ getExpandedDirectories() method
  120. public void getExpandedDirectories(Set<String> set)
  121. {
  122. VFSDirectoryEntryTableModel model
  123. = (VFSDirectoryEntryTableModel)getModel();
  124. if(model.files != null)
  125. {
  126. for(int i = 0; i < model.files.length; i++)
  127. {
  128. if(model.files[i].expanded)
  129. set.add(model.files[i].dirEntry.getPath());
  130. }
  131. }
  132. } //}}}
  133. //{{{ toggleExpanded() method
  134. public void toggleExpanded(final int row)
  135. {
  136. VFSDirectoryEntryTableModel model
  137. = (VFSDirectoryEntryTableModel)getModel();
  138. Entry entry = model.files[row];
  139. if(entry.dirEntry.getType() == VFSFile.FILE)
  140. return;
  141. Runnable delayedAwtTask = new Runnable()
  142. {
  143. public void run()
  144. {
  145. setSelectedRow(row);
  146. }
  147. };
  148. if(entry.expanded)
  149. {
  150. model.collapse(VFSManager.getVFSForPath(
  151. entry.dirEntry.getPath()),row);
  152. resizeColumns();
  153. ThreadUtilities.runInDispatchThread(delayedAwtTask);
  154. }
  155. else
  156. {
  157. browserView.clearExpansionState();
  158. browserView.loadDirectory(entry,entry.dirEntry.getPath(),
  159. false, delayedAwtTask);
  160. }
  161. } //}}}
  162. //{{{ setDirectory() method
  163. public void setDirectory(VFS vfs, Object node, java.util.List<VFSFile> list,
  164. Set<String> tmpExpanded)
  165. {
  166. timer.stop();
  167. typeSelectBuffer.setLength(0);
  168. VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel)getModel();
  169. int startIndex;
  170. if(node == null)
  171. {
  172. startIndex = 0;
  173. model.setRoot(vfs,list);
  174. }
  175. else
  176. {
  177. startIndex =
  178. model.expand(
  179. vfs,
  180. (Entry)node,
  181. list);
  182. startIndex++;
  183. }
  184. for(int i = 0; i < list.size(); i++)
  185. {
  186. Entry e = model.files[startIndex + i];
  187. String path = e.dirEntry.getPath();
  188. if(tmpExpanded.contains(path))
  189. {
  190. browserView.loadDirectory(e,path,false);
  191. tmpExpanded.remove(path);
  192. }
  193. }
  194. resizeColumns();
  195. } //}}}
  196. //{{{ maybeReloadDirectory() method
  197. public void maybeReloadDirectory(String path)
  198. {
  199. VFSDirectoryEntryTableModel model
  200. = (VFSDirectoryEntryTableModel)getModel();
  201. for(int i = 0; i < model.files.length; i++)
  202. {
  203. Entry e = model.files[i];
  204. if(!e.expanded || e.dirEntry.getType() == VFSFile.FILE)
  205. continue;
  206. VFSFile dirEntry = e.dirEntry;
  207. // work around for broken FTP plugin!
  208. String otherPath;
  209. if(dirEntry.getSymlinkPath() == null)
  210. otherPath = dirEntry.getPath();
  211. else
  212. otherPath = dirEntry.getSymlinkPath();
  213. if(MiscUtilities.pathsEqual(path,otherPath))
  214. {
  215. browserView.saveExpansionState();
  216. browserView.loadDirectory(e,path,false);
  217. return;
  218. }
  219. }
  220. } //}}}
  221. //{{{ propertiesChanged() method
  222. public void propertiesChanged()
  223. {
  224. renderer.propertiesChanged();
  225. VFSFile template = new VFSFile(
  226. "foo","foo","foo",VFSFile.FILE,0L,false);
  227. setRowHeight(renderer.getTableCellRendererComponent(
  228. this,new Entry(template,0),
  229. false,false,0,0).getPreferredSize().height);
  230. Dimension prefSize = getPreferredSize();
  231. setPreferredScrollableViewportSize(new Dimension(prefSize.width,
  232. getRowHeight() * 12));
  233. } //}}}
  234. //{{{ scrollRectToVisible() method
  235. @Override
  236. public void scrollRectToVisible(Rectangle rect)
  237. {
  238. // avoid scrolling to the right
  239. rect.width = 0;
  240. super.scrollRectToVisible(rect);
  241. } //}}}
  242. //{{{ processKeyEvent() method
  243. @Override
  244. public void processKeyEvent(KeyEvent evt)
  245. {
  246. if(evt.getID() == KeyEvent.KEY_PRESSED)
  247. {
  248. VFSDirectoryEntryTableModel model =
  249. (VFSDirectoryEntryTableModel)getModel();
  250. int row = getSelectedRow();
  251. ActionContext ac = VFSBrowser.getActionContext();
  252. ActionContext jac = jEdit.getActionContext();
  253. EditAction browserUp = ac.getAction("vfs.browser.up");
  254. VFSBrowser browser = browserView.getBrowser();
  255. switch(evt.getKeyCode())
  256. {
  257. case KeyEvent.VK_LEFT:
  258. evt.consume();
  259. if ((evt.getModifiers() & InputEvent.ALT_MASK)>0)
  260. {
  261. browser.previousDirectory();
  262. }
  263. else
  264. {
  265. if(row != -1)
  266. {
  267. if(model.files[row].expanded)
  268. {
  269. toggleExpanded(row);
  270. return;
  271. }
  272. for(int i = row - 1; i >= 0; i--)
  273. {
  274. if(model.files[i].expanded &&
  275. model.files[i].level < model.files[row].level)
  276. {
  277. setSelectedRow(i);
  278. return;
  279. }
  280. }
  281. }
  282. String dir = browserView.getBrowser()
  283. .getDirectory();
  284. dir = MiscUtilities.getParentOfPath(dir);
  285. browserView.getBrowser().setDirectory(dir);
  286. }
  287. break;
  288. case KeyEvent.VK_TAB:
  289. evt.consume();
  290. if ((evt.getModifiers() & InputEvent.SHIFT_MASK) > 0)
  291. {
  292. browserView.getParentDirectoryList().requestFocus();
  293. }
  294. else
  295. {
  296. browser.focusOnDefaultComponent();
  297. }
  298. break;
  299. case KeyEvent.VK_BACK_SPACE:
  300. evt.consume();
  301. ac.invokeAction(evt, browserUp);
  302. break;
  303. case KeyEvent.VK_UP:
  304. if ((evt.getModifiers() & InputEvent.ALT_MASK) >0)
  305. {
  306. evt.consume();
  307. ac.invokeAction(evt, browserUp);
  308. }
  309. break;
  310. case KeyEvent.VK_DELETE:
  311. evt.consume();
  312. EditAction deleteAct = ac.getAction("vfs.browser.delete");
  313. ac.invokeAction(evt, deleteAct);
  314. break;
  315. case KeyEvent.VK_N:
  316. if ((evt.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) == InputEvent.CTRL_DOWN_MASK)
  317. {
  318. evt.consume();
  319. EditAction ea = ac.getAction("vfs.browser.new-file");
  320. ac.invokeAction(evt, ea);
  321. }
  322. break;
  323. case KeyEvent.VK_INSERT:
  324. evt.consume();
  325. EditAction newDir = ac.getAction("vfs.browser.new-directory");
  326. ac.invokeAction(evt, newDir);
  327. break;
  328. case KeyEvent.VK_ESCAPE:
  329. EditAction cda = ac.getAction("vfs.browser.closedialog");
  330. ac.invokeAction(evt, cda);
  331. break;
  332. case KeyEvent.VK_F2:
  333. EditAction ren = ac.getAction("vfs.browser.rename");
  334. evt.consume();
  335. ac.invokeAction(evt, ren);
  336. break;
  337. case KeyEvent.VK_F5:
  338. evt.consume();
  339. EditAction reload= ac.getAction("vfs.browser.reload");
  340. ac.invokeAction(evt, reload);
  341. break;
  342. case KeyEvent.VK_F6:
  343. case KeyEvent.VK_RIGHT:
  344. evt.consume();
  345. if ((evt.getModifiers() & InputEvent.ALT_MASK)>0)
  346. {
  347. browser.nextDirectory();
  348. }
  349. else if(row != -1)
  350. {
  351. if(!model.files[row].expanded)
  352. toggleExpanded(row);
  353. }
  354. break;
  355. case KeyEvent.VK_ENTER:
  356. evt.consume();
  357. browserView.getBrowser().filesActivated(
  358. evt.isShiftDown()
  359. ? VFSBrowser.M_OPEN_NEW_VIEW
  360. : VFSBrowser.M_OPEN,false);
  361. break;
  362. }
  363. }
  364. else if(evt.getID() == KeyEvent.KEY_TYPED)
  365. {
  366. if(evt.isControlDown() || evt.isAltDown()
  367. || evt.isMetaDown())
  368. {
  369. evt.consume();
  370. return;
  371. }
  372. // hack...
  373. if(evt.isShiftDown() && evt.getKeyChar() == '\n')
  374. {
  375. evt.consume();
  376. return;
  377. }
  378. VFSBrowser browser = browserView.getBrowser();
  379. switch(evt.getKeyChar())
  380. {
  381. case '~':
  382. evt.consume();
  383. if(browser.getMode() == VFSBrowser.BROWSER)
  384. browser.setDirectory(System.getProperty(
  385. "user.home"));
  386. break;
  387. case '/':
  388. evt.consume();
  389. if(browser.getMode() == VFSBrowser.BROWSER)
  390. browser.rootDirectory();
  391. break;
  392. case '-':
  393. evt.consume();
  394. if(browser.getMode() == VFSBrowser.BROWSER)
  395. {
  396. browser.setDirectory(
  397. browser.getView().getBuffer()
  398. .getDirectory());
  399. }
  400. break;
  401. default:
  402. evt.consume();
  403. typeSelectBuffer.append(evt.getKeyChar());
  404. doTypeSelect(typeSelectBuffer.toString(),
  405. browser.getMode() == VFSBrowser
  406. .CHOOSE_DIRECTORY_DIALOG);
  407. timer.stop();
  408. timer.setInitialDelay(750);
  409. timer.setRepeats(false);
  410. timer.start();
  411. return;
  412. }
  413. }
  414. if(!evt.isConsumed())
  415. super.processKeyEvent(evt);
  416. } //}}}
  417. //{{{ setSelectedRow() method
  418. public void setSelectedRow(int row)
  419. {
  420. getSelectionModel().setSelectionInterval(row,row);
  421. scrollRectToVisible(getCellRect(row,0,true));
  422. } //}}}
  423. //{{{ Private members
  424. private final BrowserView browserView;
  425. private final JTableHeader header;
  426. private final FileCellRenderer renderer;
  427. private final StringBuffer typeSelectBuffer = new StringBuffer();
  428. private final Timer timer = new Timer(0,new ClearTypeSelect());
  429. private boolean resizingColumns;
  430. //{{{ doTypeSelect() method
  431. private boolean doTypeSelect(String str, int start, int end,
  432. boolean dirsOnly)
  433. {
  434. VFSFile[] files = ((VFSDirectoryEntryTableModel)
  435. getModel()).getFiles();
  436. int index = VFSFile.findCompletion(files,start,end,str,dirsOnly);
  437. if(index != -1)
  438. {
  439. setSelectedRow(index);
  440. return true;
  441. }
  442. else
  443. return false;
  444. } //}}}
  445. //{{{ resizeColumns() method
  446. private void resizeColumns()
  447. {
  448. VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel)getModel();
  449. FontRenderContext fontRenderContext = new FontRenderContext(
  450. null,false,false);
  451. int[] widths = new int[model.getColumnCount()];
  452. for(int i = 0; i < widths.length; i++)
  453. {
  454. String columnName = model.getColumnName(i);
  455. if(columnName != null)
  456. {
  457. widths[i] = (int)renderer.plainFont
  458. .getStringBounds(columnName,
  459. fontRenderContext).getWidth();
  460. }
  461. }
  462. for(int i = 1; i < widths.length; i++)
  463. {
  464. //String extAttr = model.getExtendedAttribute(i);
  465. widths[i] = Math.max(widths[i],model.getColumnWidth(i));
  466. }
  467. for(int i = 0; i < model.files.length; i++)
  468. {
  469. Entry entry = model.files[i];
  470. Font font = entry.dirEntry.getType()
  471. == VFSFile.FILE
  472. ? renderer.plainFont : renderer.boldFont;
  473. widths[0] = Math.max(widths[0],renderer.getEntryWidth(
  474. entry,font,fontRenderContext));
  475. }
  476. widths[0] += 10;
  477. TableColumnModel columns = getColumnModel();
  478. try
  479. {
  480. resizingColumns = true;
  481. for(int i = 0; i < widths.length; i++)
  482. {
  483. columns.getColumn(i).setPreferredWidth(widths[i]);
  484. columns.getColumn(i).setWidth(widths[i]);
  485. }
  486. }
  487. finally
  488. {
  489. resizingColumns = false;
  490. }
  491. doLayout();
  492. } //}}}
  493. //{{{ saveWidths() method
  494. private void saveWidths()
  495. {
  496. if(resizingColumns)
  497. return;
  498. VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel)getModel();
  499. TableColumnModel columns = getColumnModel();
  500. for(int i = 1; i < model.getColumnCount(); i++)
  501. model.setColumnWidth(i,columns.getColumn(i).getWidth());
  502. } //}}}
  503. //}}}
  504. //{{{ ClearTypeSelect class
  505. class ClearTypeSelect implements ActionListener
  506. {
  507. public void actionPerformed(ActionEvent evt)
  508. {
  509. typeSelectBuffer.setLength(0);
  510. }
  511. } //}}}
  512. //{{{ ColumnHandler class
  513. class ColumnHandler implements TableColumnModelListener
  514. {
  515. public void columnAdded(TableColumnModelEvent e) {}
  516. public void columnRemoved(TableColumnModelEvent e) {}
  517. public void columnMoved(TableColumnModelEvent e) {}
  518. public void columnSelectionChanged(ListSelectionEvent e) {}
  519. public void columnMarginChanged(ChangeEvent e)
  520. {
  521. saveWidths();
  522. }
  523. } //}}}
  524. //{{{ class MainMouseHandler
  525. class MainMouseHandler extends MouseInputAdapter
  526. {
  527. @Override
  528. public void mouseClicked(MouseEvent e)
  529. {
  530. super.mouseClicked(e);
  531. int ind = getSelectionModel().getMinSelectionIndex();
  532. if (ind == -1)
  533. return;
  534. Entry node = (Entry) getModel().getValueAt(ind, 0);
  535. boolean isDir = node.dirEntry.getType() == VFSFile.DIRECTORY;
  536. EditBus.send(new VFSPathSelected(jEdit.getActiveView(),
  537. node.dirEntry.getPath(), isDir));
  538. }
  539. } //}}}
  540. //{{{ MouseHandler class
  541. class MouseHandler extends MouseInputAdapter
  542. {
  543. @Override
  544. public void mousePressed(MouseEvent evt)
  545. {
  546. // double click on columns header
  547. if (evt.getSource() == header && evt.getClickCount() == 2)
  548. {
  549. VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel) header.getTable().getModel();
  550. TableColumnModel columnModel = header.getColumnModel();
  551. int viewColumn = columnModel.getColumnIndexAtX(evt.getX());
  552. int column = columnModel.getColumn(viewColumn).getModelIndex();
  553. saveWidths();
  554. if(model.sortByColumn(column))
  555. {
  556. resizeColumns();
  557. Log.log(Log.DEBUG,this,"VFSDirectoryEntryTable sorted by "
  558. + model.getColumnName(column)
  559. + (model.getAscending() ? " ascending" : " descending") );
  560. }
  561. }
  562. }
  563. } //}}}
  564. //{{{ HeaderRenderer
  565. static class HeaderRenderer extends DefaultTableCellRenderer
  566. {
  567. private final DefaultTableCellRenderer tcr;
  568. HeaderRenderer(DefaultTableCellRenderer tcr)
  569. {
  570. this.tcr = tcr;
  571. }
  572. @Override
  573. public Component getTableCellRendererComponent(JTable table, Object value,
  574. boolean isSelected, boolean hasFocus, int row, int column)
  575. {
  576. JLabel l = (JLabel)tcr.getTableCellRendererComponent(table,value,isSelected,hasFocus,row,column);
  577. VFSDirectoryEntryTableModel model = (VFSDirectoryEntryTableModel)table.getModel();
  578. Icon icon = column == model.getSortColumn()
  579. ? model.getAscending() ? ASC_ICON : DESC_ICON
  580. : null;
  581. l.setIcon(icon);
  582. // l.setHorizontalTextPosition(l.LEADING);
  583. return l;
  584. }
  585. } //}}}
  586. //{{{ SortOrder Icons
  587. static final Icon ASC_ICON = GUIUtilities.loadIcon("arrow-asc.png");
  588. static final Icon DESC_ICON = GUIUtilities.loadIcon("arrow-desc.png");
  589. //}}}
  590. }