2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2011 - DIGITEO - Calixte DENIZET
5 * This file must be used under the terms of the CeCILL.
6 * This source file is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at
9 * http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt
13 package org.scilab.modules.ui_data.filebrowser;
15 import java.awt.Color;
16 import java.awt.Component;
17 import java.awt.Dimension;
18 import java.awt.Graphics;
19 import java.awt.Insets;
20 import java.awt.Point;
21 import java.awt.Rectangle;
22 import java.awt.event.ActionEvent;
23 import java.awt.event.ActionListener;
24 import java.awt.event.KeyAdapter;
25 import java.awt.event.KeyEvent;
26 import java.awt.event.MouseAdapter;
27 import java.awt.event.MouseEvent;
29 import java.lang.reflect.Method;
30 import java.text.DateFormat;
31 import java.util.Date;
32 import java.util.Enumeration;
33 import java.util.regex.Pattern;
35 import javax.swing.AbstractCellEditor;
36 import javax.swing.ActionMap;
37 import javax.swing.InputMap;
38 import javax.swing.JLabel;
39 import javax.swing.JMenuItem;
40 import javax.swing.JPopupMenu;
41 import javax.swing.JScrollPane;
42 import javax.swing.JTable;
43 import javax.swing.JTree;
44 import javax.swing.KeyStroke;
45 import javax.swing.SwingUtilities;
46 import javax.swing.border.AbstractBorder;
47 import javax.swing.border.Border;
48 import javax.swing.plaf.basic.BasicTreeUI;
49 import javax.swing.table.DefaultTableCellRenderer;
50 import javax.swing.table.TableCellEditor;
51 import javax.swing.table.TableCellRenderer;
52 import javax.swing.table.TableColumn;
53 import javax.swing.tree.TreePath;
55 import org.scilab.modules.gui.events.callback.CallBack;
56 import org.scilab.modules.gui.pushbutton.PushButton;
57 import org.scilab.modules.ui_data.filebrowser.actions.ChangeCWDAction;
58 import org.scilab.modules.ui_data.filebrowser.actions.EditFileWithDefaultAppAction;
59 import org.scilab.modules.ui_data.filebrowser.actions.ExecuteFileInConsoleAction;
60 import org.scilab.modules.ui_data.filebrowser.actions.ExecuteFileInXcosAction;
61 import org.scilab.modules.ui_data.filebrowser.actions.LoadFileAsGraphAction;
62 import org.scilab.modules.ui_data.filebrowser.actions.LoadFileInScilabAction;
63 import org.scilab.modules.ui_data.filebrowser.actions.OpenFileInSciNotesAction;
64 import org.scilab.modules.ui_data.filebrowser.actions.OpenFileWithDefaultAppAction;
65 import org.scilab.modules.ui_data.filebrowser.actions.ValidateAction;
66 import org.scilab.modules.ui_data.utils.UiDataMessages;
69 * The tree table model abstract implementation
70 * @author Calixte DENIZET
72 public class SwingScilabTreeTable extends JTable {
74 private static final Insets INSETS = new Insets(0, 2, 0, 0);
75 private static final DateFormat DATEFORMAT = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
77 private static final Border BORDER = new AbstractBorder() {
78 public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
79 g.setColor(Color.LIGHT_GRAY);
80 g.drawLine(x, y, x, y + height);
83 public Insets getBorderInsets(Component c) {
87 public Insets getBorderInsets(Component c, Insets insets) {
92 private Method isLocationInExpandControl;
94 protected ScilabTreeTableCellRenderer tree;
95 protected ScilabFileSelectorComboBox combobox;
96 protected ScilabFileBrowserHistory history;
100 * @param treeTableModel the tree table model
101 * @param combobox the combox used to set the path
103 public SwingScilabTreeTable(ScilabTreeTableModel treeTableModel, ScilabFileSelectorComboBox combobox) {
105 this.combobox = combobox;
106 combobox.setTreeTable(this);
107 history = new ScilabFileBrowserHistory(this);
108 tree = new ScilabTreeTableCellRenderer(this, treeTableModel);
109 super.setModel(new ScilabTreeTableModelAdapter(treeTableModel, tree));
111 // Install the tree editor renderer and editor.
112 setDefaultRenderer(ScilabTreeTableModel.class, tree);
113 setDefaultRenderer(Date.class, new DefaultTableCellRenderer() {
115 setHorizontalTextPosition(DefaultTableCellRenderer.LEFT);
118 public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focus, int row, int col) {
119 JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, selected, focus, row, col);
120 label.setText(DATEFORMAT.format((Date) value));
122 label.setBorder(BORDER);
127 setDefaultRenderer(ScilabFileBrowserModel.FileSize.class, new DefaultTableCellRenderer() {
129 setHorizontalTextPosition(DefaultTableCellRenderer.LEFT);
132 public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focus, int row, int col) {
133 Component c = super.getTableCellRendererComponent(table, value, selected, focus, row, col);
135 JLabel jl = (JLabel) c;
136 jl.setBorder(BORDER);
141 setDefaultRenderer(String.class, new DefaultTableCellRenderer() {
143 setHorizontalTextPosition(DefaultTableCellRenderer.LEFT);
146 public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focus, int row, int col) {
147 Component c = super.getTableCellRendererComponent(table, value, selected, focus, row, col);
149 JLabel jl = (JLabel) c;
150 jl.setBorder(BORDER);
157 setFillsViewportHeight(true);
158 setIntercellSpacing(new Dimension(0, 0));
159 setRowSorter(new FileBrowserRowSorter(tree, this));
160 setAutoResizeMode(AUTO_RESIZE_NEXT_COLUMN);
163 isLocationInExpandControl = BasicTreeUI.class.getDeclaredMethod("isLocationInExpandControl", new Class[]{TreePath.class, int.class, int.class});
164 isLocationInExpandControl.setAccessible(true);
165 } catch (NoSuchMethodException e) { }
167 addMouseListener(new MouseAdapter() {
168 public void mousePressed(MouseEvent e) {
169 Point p = e.getPoint();
170 int col = columnAtPoint(p);
171 if (getColumnClass(col) == ScilabTreeTableModel.class && SwingUtilities.isLeftMouseButton(e)) {
173 if (isLocationInExpandControl != null) {
175 int row = rowAtPoint(p);
176 TreePath path = tree.getPathForRow(row);
177 boolean isOnExpander = ((Boolean) isLocationInExpandControl.invoke(tree.getUI(), path, e.getX(), e.getY())).booleanValue();
178 Rectangle r = tree.getRowBounds(row);
179 if (!isOnExpander && !r.contains(p)) {
180 me = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), r.x, r.y, e.getClickCount(), e.isPopupTrigger());
182 } catch (Exception ex) { }
184 tree.dispatchEvent(me);
189 addKeyListener(new KeyAdapter() {
190 public void keyTyped(KeyEvent e) {
191 char c = e.getKeyChar();
192 if (Character.isLetter(c)) {
194 if (Character.isUpperCase(c)) {
197 c = Character.toLowerCase(c);
198 int[] rows = getSelectedRows();
199 int count = getRowCount();
201 if (rows != null && rows.length != 0) {
202 start = modulo(rows[0] + step, count);
204 for (int i = start; i != start - step; i = modulo(i + step, count)) {
205 char first = ((FileNode) tree.getPathForRow(i).getLastPathComponent()).toString().charAt(0);
206 first = Character.toLowerCase(first);
208 scrollRectToVisible(tree.getRowBounds(i));
209 setRowSelectionInterval(i, i);
219 setComponentPopupMenu(createPopup());
223 * @return the Next button used in history
225 public PushButton getNextButton() {
226 return history.getNextButton();
230 * @return the Previous button used in history
232 public PushButton getPreviousButton() {
233 return history.getPreviousButton();
237 * @return the combobox used to set the path
239 public ScilabFileSelectorComboBox getComboBox() {
244 * Get the selected rows as file path
247 public String[] getSelectedPaths() {
248 int[] rows = getSelectedRows();
249 String[] paths = new String[rows.length];
250 for (int i = 0; i < rows.length; i++) {
251 TreePath path = tree.getPathForRow(rows[i]);
252 FileNode fn = (FileNode) path.getLastPathComponent();
253 paths[i] = fn.getFile().getAbsolutePath();
260 * Get the selected rows as file
263 public File[] getSelectedFiles() {
264 int[] rows = getSelectedRows();
265 File[] files = new File[rows.length];
266 for (int i = 0; i < rows.length; i++) {
267 TreePath path = tree.getPathForRow(rows[i]);
268 FileNode fn = (FileNode) path.getLastPathComponent();
269 files[i] = fn.getFile();
278 public int getRowHeight(int row) {
279 return getRowHeight();
285 public boolean isOpaque() {
290 * Set the base directory
291 * @param baseDir the base directory
293 public void setBaseDir(String baseDir) {
294 setBaseDir(baseDir, true);
298 * Set the base directory
299 * @param baseDir the base directory
300 * @param addInHistory if true the dir is add in the history
302 public void setBaseDir(String baseDir, boolean addInHistory) {
303 ScilabFileBrowserModel model = (ScilabFileBrowserModel) tree.getModel();
304 combobox.setBaseDir(baseDir);
306 File f = new File(baseDir);
307 if (!baseDir.equals(model.getBaseDir()) && f.exists() && f.isDirectory() && f.canRead()) {
310 history.addPathInHistory(baseDir);
312 model.setBaseDir(baseDir, this);
318 * Set the file filter to use in table
319 * @param pat the pattern to use
321 public void setFilter(Pattern pat) {
322 ScilabFileBrowserModel model = (ScilabFileBrowserModel) tree.getModel();
323 TreePath rootPath = new TreePath(model.getRoot());
324 Enumeration<TreePath> en = tree.getExpandedDescendants(rootPath);
326 model.setFilter(pat);
329 while (en.hasMoreElements()) {
330 tree.expandPath(en.nextElement());
338 public void reload(ScilabFileBrowserModel model) {
339 tree.setModel(model);
340 tree.setRowHeight(getRowHeight());
341 tree.setLargeModel(true);
342 TreePath path = new TreePath(model.getRoot());
343 tree.collapsePath(path);
344 ((JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this)).getVerticalScrollBar().setValue(0);
345 tree.expandPath(path);
346 if (getRowCount() >= 1) {
347 repaint(tree.getRowBounds(1));
352 /* Workaround for BasicTableUI anomaly. Make sure the UI never tries to
353 * paint the editor. The UI currently uses different techniques to
354 * paint the renderers and editors and overriding setBounds() below
355 * is not the right thing to do for an editor. Returning -1 for the
356 * editing row in this case, ensures the editor is never painted.
358 public int getEditingRow() {
359 if (getColumnClass(editingColumn) == ScilabTreeTableModel.class) {
369 private void initActions() {
370 final ActionMap actions = getActionMap();
371 actions.put("scinotes", new OpenFileInSciNotesAction(this));
372 actions.put("xcos", new ExecuteFileInXcosAction(this));
373 actions.put("console", new ExecuteFileInConsoleAction(this));
374 actions.put("load", new LoadFileInScilabAction(this));
375 actions.put("graph", new LoadFileAsGraphAction(this));
376 actions.put("cwd", new ChangeCWDAction(this));
377 if (EditFileWithDefaultAppAction.isSupported()) {
378 actions.put("edit", new EditFileWithDefaultAppAction(this));
380 if (OpenFileWithDefaultAppAction.isSupported()) {
381 actions.put("open", new OpenFileWithDefaultAppAction(this));
383 actions.put("validate", new ValidateAction(this));
384 actions.put("validateorexpand", new CallBack(null) {
385 public void callBack() {
386 int[] rows = getSelectedRows();
387 if (rows != null && rows.length != 0) {
388 TreePath path = tree.getPathForRow(rows[0]);
389 FileNode fn = (FileNode) path.getLastPathComponent();
391 ((CallBack) actions.get("validate")).callBack();
393 if (tree.isExpanded(path)) {
394 tree.collapsePath(path);
396 tree.expandPath(path);
398 setRowSelectionInterval(rows[0], rows[0]);
404 combobox.setAction((CallBack) actions.get("cwd"));
405 InputMap map = getInputMap();
406 map.put(KeyStroke.getKeyStroke("ENTER"), "validateorexpand");
410 * Create the popup menu
412 private JPopupMenu createPopup() {
413 ActionMap actions = getActionMap();
414 JPopupMenu popup = new JPopupMenu();
415 JMenuItem item = new JMenuItem(UiDataMessages.OPENINSCINOTES);
416 item.addActionListener(actions.get("scinotes"));
419 item = new JMenuItem(UiDataMessages.EXECINCONSOLE);
420 item.addActionListener(actions.get("console"));
423 item = new JMenuItem(UiDataMessages.OPENINXCOS);
424 item.addActionListener(actions.get("xcos"));
427 item = new JMenuItem(UiDataMessages.LOADINSCILAB);
428 item.addActionListener(actions.get("load"));
431 if (actions.get("edit") != null || actions.get("open") != null) {
432 popup.addSeparator();
435 if (actions.get("edit") != null) {
436 item = new JMenuItem(UiDataMessages.EDITWITHDEFAULT);
437 item.addActionListener(actions.get("edit"));
441 if (actions.get("open") != null) {
442 item = new JMenuItem(UiDataMessages.OPENWITHDEFAULT);
443 item.addActionListener(actions.get("open"));
453 * A modulo for negative numbers
455 * @param p an other int
458 private static final int modulo(int n, int p) {