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.action_binding.InterpreterManagement;
56 import org.scilab.modules.gui.events.callback.CallBack;
57 import org.scilab.modules.gui.pushbutton.PushButton;
58 import org.scilab.modules.ui_data.filebrowser.actions.ChangeCWDAction;
59 import org.scilab.modules.ui_data.filebrowser.actions.EditFileWithDefaultAppAction;
60 import org.scilab.modules.ui_data.filebrowser.actions.ExecuteFileInConsoleAction;
61 import org.scilab.modules.ui_data.filebrowser.actions.ExecuteFileInXcosAction;
62 import org.scilab.modules.ui_data.filebrowser.actions.LoadFileAsGraphAction;
63 import org.scilab.modules.ui_data.filebrowser.actions.LoadFileInScilabAction;
64 import org.scilab.modules.ui_data.filebrowser.actions.OpenFileInSciNotesAction;
65 import org.scilab.modules.ui_data.filebrowser.actions.OpenFileWithDefaultAppAction;
66 import org.scilab.modules.ui_data.filebrowser.actions.ValidateAction;
67 import org.scilab.modules.ui_data.utils.UiDataMessages;
70 * The tree table model abstract implementation
71 * @author Calixte DENIZET
73 public class SwingScilabTreeTable extends JTable {
75 private static final Insets INSETS = new Insets(0, 2, 0, 0);
76 private static final DateFormat DATEFORMAT = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
78 private static final Border BORDER = new AbstractBorder() {
79 public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
80 g.setColor(Color.LIGHT_GRAY);
81 g.drawLine(x, y, x, y + height);
84 public Insets getBorderInsets(Component c) {
88 public Insets getBorderInsets(Component c, Insets insets) {
93 private Method isLocationInExpandControl;
95 protected ScilabTreeTableCellRenderer tree;
96 protected ScilabFileSelectorComboBox combobox;
97 protected ScilabFileBrowserHistory history;
100 * Default Constructor
101 * @param treeTableModel the tree table model
102 * @param combobox the combox used to set the path
104 public SwingScilabTreeTable(ScilabTreeTableModel treeTableModel, ScilabFileSelectorComboBox combobox) {
106 this.combobox = combobox;
107 combobox.setTreeTable(this);
108 history = new ScilabFileBrowserHistory(this);
109 tree = new ScilabTreeTableCellRenderer(this, treeTableModel);
110 super.setModel(new ScilabTreeTableModelAdapter(treeTableModel, tree));
112 // Install the tree editor renderer and editor.
113 setDefaultRenderer(ScilabTreeTableModel.class, tree);
114 setDefaultRenderer(Date.class, new DefaultTableCellRenderer() {
116 setHorizontalTextPosition(DefaultTableCellRenderer.LEFT);
119 public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focus, int row, int col) {
120 JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, selected, focus, row, col);
121 label.setText(DATEFORMAT.format((Date) value));
123 label.setBorder(BORDER);
128 setDefaultRenderer(ScilabFileBrowserModel.FileSize.class, new DefaultTableCellRenderer() {
130 setHorizontalTextPosition(DefaultTableCellRenderer.LEFT);
133 public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focus, int row, int col) {
134 Component c = super.getTableCellRendererComponent(table, value, selected, focus, row, col);
136 JLabel jl = (JLabel) c;
137 jl.setBorder(BORDER);
142 setDefaultRenderer(String.class, new DefaultTableCellRenderer() {
144 setHorizontalTextPosition(DefaultTableCellRenderer.LEFT);
147 public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focus, int row, int col) {
148 Component c = super.getTableCellRendererComponent(table, value, selected, focus, row, col);
150 JLabel jl = (JLabel) c;
151 jl.setBorder(BORDER);
158 setFillsViewportHeight(true);
159 setIntercellSpacing(new Dimension(0, 0));
160 setRowSorter(new FileBrowserRowSorter(tree, this));
161 setAutoResizeMode(AUTO_RESIZE_NEXT_COLUMN);
164 isLocationInExpandControl = BasicTreeUI.class.getDeclaredMethod("isLocationInExpandControl", new Class[]{TreePath.class, int.class, int.class});
165 isLocationInExpandControl.setAccessible(true);
166 } catch (NoSuchMethodException e) { }
168 addMouseListener(new MouseAdapter() {
169 public void mousePressed(MouseEvent e) {
170 Point p = e.getPoint();
171 int col = columnAtPoint(p);
172 if (getColumnClass(col) == ScilabTreeTableModel.class && SwingUtilities.isLeftMouseButton(e)) {
174 if (isLocationInExpandControl != null) {
176 int row = rowAtPoint(p);
177 TreePath path = tree.getPathForRow(row);
178 boolean isOnExpander = ((Boolean) isLocationInExpandControl.invoke(tree.getUI(), path, e.getX(), e.getY())).booleanValue();
179 Rectangle r = tree.getRowBounds(row);
180 if (!isOnExpander && !r.contains(p)) {
181 me = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), r.x, r.y, e.getClickCount(), e.isPopupTrigger());
183 } catch (Exception ex) { }
185 tree.dispatchEvent(me);
190 addKeyListener(new KeyAdapter() {
191 public void keyTyped(KeyEvent e) {
192 char c = e.getKeyChar();
193 if (Character.isLetter(c)) {
195 if (Character.isUpperCase(c)) {
198 c = Character.toLowerCase(c);
199 int[] rows = getSelectedRows();
200 int count = getRowCount();
202 if (rows != null && rows.length != 0) {
203 start = modulo(rows[0] + step, count);
205 for (int i = start; i != start - step; i = modulo(i + step, count)) {
206 char first = ((FileNode) tree.getPathForRow(i).getLastPathComponent()).toString().charAt(0);
207 first = Character.toLowerCase(first);
209 scrollRectToVisible(tree.getRowBounds(i));
210 setRowSelectionInterval(i, i);
220 setComponentPopupMenu(createPopup());
224 * @return the Next button used in history
226 public PushButton getNextButton() {
227 return history.getNextButton();
231 * @return the Previous button used in history
233 public PushButton getPreviousButton() {
234 return history.getPreviousButton();
238 * @return the combobox used to set the path
240 public ScilabFileSelectorComboBox getComboBox() {
245 * Get the selected rows as file path
248 public String[] getSelectedPaths() {
249 int[] rows = getSelectedRows();
250 String[] paths = new String[rows.length];
251 for (int i = 0; i < rows.length; i++) {
252 TreePath path = tree.getPathForRow(rows[i]);
253 FileNode fn = (FileNode) path.getLastPathComponent();
254 paths[i] = fn.getFile().getAbsolutePath();
261 * Get the selected rows as file
264 public File[] getSelectedFiles() {
265 int[] rows = getSelectedRows();
266 File[] files = new File[rows.length];
267 for (int i = 0; i < rows.length; i++) {
268 TreePath path = tree.getPathForRow(rows[i]);
269 FileNode fn = (FileNode) path.getLastPathComponent();
270 files[i] = fn.getFile();
279 public int getRowHeight(int row) {
280 return getRowHeight();
286 public boolean isOpaque() {
291 * Set the base directory
292 * @param baseDir the base directory
294 public void setBaseDir(String baseDir) {
295 setBaseDir(baseDir, true);
299 * Set the base directory
300 * @param baseDir the base directory
301 * @param addInHistory if true the dir is add in the history
303 public void setBaseDir(String baseDir, boolean addInHistory) {
304 ScilabFileBrowserModel model = (ScilabFileBrowserModel) tree.getModel();
305 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 InterpreterManagement.requestScilabExec("chdir('" + baseDir + "')");
314 model.setBaseDir(baseDir, this);
319 * Set the file filter to use in table
320 * @param pat the pattern to use
322 public void setFilter(Pattern pat) {
323 ScilabFileBrowserModel model = (ScilabFileBrowserModel) tree.getModel();
324 TreePath rootPath = new TreePath(model.getRoot());
325 Enumeration<TreePath> en = tree.getExpandedDescendants(rootPath);
327 model.setFilter(pat);
330 while (en.hasMoreElements()) {
331 tree.expandPath(en.nextElement());
339 public void reload(ScilabFileBrowserModel model) {
340 tree.setModel(model);
341 tree.setRowHeight(getRowHeight());
342 tree.setLargeModel(true);
343 TreePath path = new TreePath(model.getRoot());
344 tree.collapsePath(path);
345 ((JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this)).getVerticalScrollBar().setValue(0);
346 tree.expandPath(path);
350 /* Workaround for BasicTableUI anomaly. Make sure the UI never tries to
351 * paint the editor. The UI currently uses different techniques to
352 * paint the renderers and editors and overriding setBounds() below
353 * is not the right thing to do for an editor. Returning -1 for the
354 * editing row in this case, ensures the editor is never painted.
356 public int getEditingRow() {
357 if (getColumnClass(editingColumn) == ScilabTreeTableModel.class) {
367 private void initActions() {
368 final ActionMap actions = getActionMap();
369 actions.put("scinotes", new OpenFileInSciNotesAction(this));
370 actions.put("xcos", new ExecuteFileInXcosAction(this));
371 actions.put("console", new ExecuteFileInConsoleAction(this));
372 actions.put("load", new LoadFileInScilabAction(this));
373 actions.put("graph", new LoadFileAsGraphAction(this));
374 actions.put("cwd", new ChangeCWDAction(this));
375 if (EditFileWithDefaultAppAction.isSupported()) {
376 actions.put("edit", new EditFileWithDefaultAppAction(this));
378 if (OpenFileWithDefaultAppAction.isSupported()) {
379 actions.put("open", new OpenFileWithDefaultAppAction(this));
381 actions.put("validate", new ValidateAction(this));
382 actions.put("validateorexpand", new CallBack(null) {
383 public void callBack() {
384 int[] rows = getSelectedRows();
385 if (rows != null && rows.length != 0) {
386 TreePath path = tree.getPathForRow(rows[0]);
387 FileNode fn = (FileNode) path.getLastPathComponent();
389 ((CallBack) actions.get("validate")).callBack();
391 if (tree.isExpanded(path)) {
392 tree.collapsePath(path);
394 tree.expandPath(path);
396 setRowSelectionInterval(rows[0], rows[0]);
402 combobox.setAction((CallBack) actions.get("cwd"));
403 InputMap map = getInputMap();
404 map.put(KeyStroke.getKeyStroke("ENTER"), "validateorexpand");
408 * Create the popup menu
410 private JPopupMenu createPopup() {
411 ActionMap actions = getActionMap();
412 JPopupMenu popup = new JPopupMenu();
413 JMenuItem item = new JMenuItem(UiDataMessages.OPENINSCINOTES);
414 item.addActionListener(actions.get("scinotes"));
417 item = new JMenuItem(UiDataMessages.EXECINCONSOLE);
418 item.addActionListener(actions.get("console"));
421 item = new JMenuItem(UiDataMessages.OPENINXCOS);
422 item.addActionListener(actions.get("xcos"));
425 item = new JMenuItem(UiDataMessages.LOADINSCILAB);
426 item.addActionListener(actions.get("load"));
429 if (actions.get("edit") != null || actions.get("open") != null) {
430 popup.addSeparator();
433 if (actions.get("edit") != null) {
434 item = new JMenuItem(UiDataMessages.EDITWITHDEFAULT);
435 item.addActionListener(actions.get("edit"));
439 if (actions.get("open") != null) {
440 item = new JMenuItem(UiDataMessages.OPENWITHDEFAULT);
441 item.addActionListener(actions.get("open"));
451 * A modulo for negative numbers
453 * @param p an other int
456 private static final int modulo(int n, int p) {