2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2011 - DIGITEO - Calixte DENIZET
5 * Copyright (C) 2012 - 2016 - Scilab Enterprises
7 * This file is hereby licensed under the terms of the GNU GPL v2.0,
8 * pursuant to article 5.3.4 of the CeCILL v.2.1.
9 * This file was originally licensed under the terms of the CeCILL v2.1,
10 * and continues to be available under such terms.
11 * For more information, see the COPYING file which you should have received
12 * along with this program.
16 package org.scilab.modules.ui_data.filebrowser;
18 import java.awt.Color;
19 import java.awt.Component;
20 import java.awt.Dimension;
21 import java.awt.Graphics;
22 import java.awt.Insets;
23 import java.awt.Point;
24 import java.awt.Rectangle;
25 import java.awt.event.KeyAdapter;
26 import java.awt.event.KeyEvent;
27 import java.awt.event.MouseAdapter;
28 import java.awt.event.MouseEvent;
30 import java.lang.reflect.Method;
31 import java.text.DateFormat;
32 import java.util.Date;
33 import java.util.regex.Pattern;
35 import javax.swing.ActionMap;
36 import javax.swing.ImageIcon;
37 import javax.swing.InputMap;
38 import javax.swing.JButton;
39 import javax.swing.JLabel;
40 import javax.swing.JMenuItem;
41 import javax.swing.JPopupMenu;
42 import javax.swing.JScrollPane;
43 import javax.swing.JTable;
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.SwingWorker;
51 import javax.swing.tree.TreePath;
53 import org.scilab.modules.commons.gui.FindIconHelper;
54 import org.scilab.modules.gui.events.callback.CommonCallBack;
55 import org.scilab.modules.ui_data.filebrowser.actions.ChangeCWDAction;
56 import org.scilab.modules.ui_data.filebrowser.actions.EditFileWithDefaultAppAction;
57 import org.scilab.modules.ui_data.filebrowser.actions.ExecuteFileInConsoleAction;
58 import org.scilab.modules.ui_data.filebrowser.actions.ExecuteFileInXcosAction;
59 import org.scilab.modules.ui_data.filebrowser.actions.ExecuteMatFileAction;
60 import org.scilab.modules.ui_data.filebrowser.actions.LoadFileAsGraphAction;
61 import org.scilab.modules.ui_data.filebrowser.actions.LoadFileInScilabAction;
62 import org.scilab.modules.ui_data.filebrowser.actions.OpenFileInSciNotesAction;
63 import org.scilab.modules.ui_data.filebrowser.actions.OpenFileWithDefaultAppAction;
64 import org.scilab.modules.ui_data.filebrowser.actions.ValidateAction;
65 import org.scilab.modules.ui_data.utils.UiDataMessages;
68 * The tree table model abstract implementation
69 * @author Calixte DENIZET
71 @SuppressWarnings(value = { "serial" })
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 SwingWorker dirRefresher;
93 private ScilabFileBrowserModel model;
94 private boolean resetScrollBar = true;
96 private Method isLocationInExpandControl;
98 protected ScilabTreeTableCellRenderer tree;
99 protected ScilabFileSelectorComboBox combobox;
100 protected ScilabFileBrowserHistory history;
103 * Default Constructor
104 * @param treeTableModel the tree table model
105 * @param combobox the combox used to set the path
107 public SwingScilabTreeTable(ScilabTreeTableModel treeTableModel, ScilabFileSelectorComboBox combobox) {
109 this.combobox = combobox;
110 combobox.setTreeTable(this);
111 history = new ScilabFileBrowserHistory(this);
112 tree = new ScilabTreeTableCellRenderer(this, treeTableModel);
113 super.setModel(new ScilabTreeTableModelAdapter(treeTableModel, tree));
115 // Install the tree editor renderer and editor.
116 setDefaultRenderer(ScilabTreeTableModel.class, tree);
117 setDefaultRenderer(Date.class, new DefaultTableCellRenderer() {
119 setHorizontalTextPosition(DefaultTableCellRenderer.LEFT);
122 public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focus, int row, int col) {
123 JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, selected, focus, row, col);
124 label.setText(DATEFORMAT.format((Date) value));
126 label.setBorder(BORDER);
131 setDefaultRenderer(ScilabFileBrowserModel.FileSize.class, new DefaultTableCellRenderer() {
133 setHorizontalTextPosition(DefaultTableCellRenderer.LEFT);
136 public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focus, int row, int col) {
137 Component c = super.getTableCellRendererComponent(table, value, selected, focus, row, col);
139 JLabel jl = (JLabel) c;
140 jl.setBorder(BORDER);
145 setDefaultRenderer(String.class, new DefaultTableCellRenderer() {
147 setHorizontalTextPosition(DefaultTableCellRenderer.LEFT);
150 public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focus, int row, int col) {
151 Component c = super.getTableCellRendererComponent(table, value, selected, focus, row, col);
153 JLabel jl = (JLabel) c;
154 jl.setBorder(BORDER);
161 setFillsViewportHeight(true);
162 setIntercellSpacing(new Dimension(0, 0));
163 setRowSorter(new FileBrowserRowSorter(tree, this));
164 setAutoResizeMode(AUTO_RESIZE_NEXT_COLUMN);
167 isLocationInExpandControl = BasicTreeUI.class.getDeclaredMethod("isLocationInExpandControl", new Class[] {TreePath.class, int.class, int.class});
168 isLocationInExpandControl.setAccessible(true);
169 } catch (NoSuchMethodException e) { }
171 addMouseListener(new MouseAdapter() {
172 public void mousePressed(MouseEvent e) {
173 Point p = e.getPoint();
174 int col = columnAtPoint(p);
175 if (getColumnClass(col) == ScilabTreeTableModel.class && SwingUtilities.isLeftMouseButton(e)) {
177 if (isLocationInExpandControl != null) {
179 int row = rowAtPoint(p);
180 TreePath path = tree.getPathForRow(row);
181 boolean isOnExpander = ((Boolean) isLocationInExpandControl.invoke(tree.getUI(), path, e.getX(), e.getY())).booleanValue();
182 Rectangle r = tree.getRowBounds(row);
183 if (!isOnExpander && !r.contains(p)) {
184 me = new MouseEvent((Component) e.getSource(), e.getID(), e.getWhen(), e.getModifiers(), r.x, r.y, e.getClickCount(), e.isPopupTrigger());
186 } catch (Exception ex) { }
188 tree.dispatchEvent(me);
193 addKeyListener(new KeyAdapter() {
194 public void keyTyped(KeyEvent e) {
195 char c = e.getKeyChar();
196 if (Character.isLetter(c)) {
198 if (Character.isUpperCase(c)) {
201 c = Character.toLowerCase(c);
202 int[] rows = getSelectedRows();
203 int count = getRowCount();
205 if (rows != null && rows.length != 0) {
206 start = modulo(rows[0] + step, count);
208 for (int i = start; i != start - step; i = modulo(i + step, count)) {
209 char first = ((FileNode) tree.getPathForRow(i).getLastPathComponent()).toString().charAt(0);
210 first = Character.toLowerCase(first);
212 scrollRectToVisible(tree.getRowBounds(i));
213 setRowSelectionInterval(i, i);
223 setComponentPopupMenu(createPopup());
227 * @return the Next button used in history
229 public JButton getNextButton() {
230 return history.getNextButton();
234 * @return the Previous button used in history
236 public JButton getPreviousButton() {
237 return history.getPreviousButton();
241 * @return the combobox used to set the path
243 public ScilabFileSelectorComboBox getComboBox() {
248 * Get the selected rows as file path
251 public String[] getSelectedPaths() {
252 int[] rows = getSelectedRows();
253 String[] paths = new String[rows.length];
254 for (int i = 0; i < rows.length; i++) {
255 TreePath path = tree.getPathForRow(rows[i]);
256 FileNode fn = (FileNode) path.getLastPathComponent();
257 paths[i] = fn.getFile().getAbsolutePath();
264 * Get the selected rows as file
267 public File[] getSelectedFiles() {
268 int[] rows = getSelectedRows();
269 File[] files = new File[rows.length];
270 for (int i = 0; i < rows.length; i++) {
271 TreePath path = tree.getPathForRow(rows[i]);
272 FileNode fn = (FileNode) path.getLastPathComponent();
273 files[i] = fn.getFile();
282 public int getRowHeight(int row) {
283 return getRowHeight();
289 public boolean isOpaque() {
294 * Set the base directory
295 * @param baseDir the base directory
297 public void setBaseDir(String baseDir) {
298 setBaseDir(baseDir, true);
302 * Set the base directory
303 * @param baseDir the base directory
304 * @param addInHistory if true the dir is add in the history
306 public synchronized void setBaseDir(String baseDir, boolean addInHistory) {
307 boolean cancelled = false;
308 ScilabFileBrowserModel model;
309 if (dirRefresher != null) {
310 dirRefresher.cancel(true);
316 model = (ScilabFileBrowserModel) tree.getModel();
318 combobox.setBaseDir(baseDir);
320 File f = new File(baseDir);
321 if (cancelled || (f.exists() && f.isDirectory() && f.canRead())) {
322 boolean sameDir = baseDir.equals(model.getBaseDir());
324 if (!sameDir && addInHistory) {
325 history.addPathInHistory(baseDir);
327 resetScrollBar = !sameDir;
328 model.setBaseDir(baseDir, this);
334 * Set the file filter to use in table
335 * @param pat the pattern to use
337 public void setFilter(Pattern pat) {
338 ScilabFileBrowserModel model = (ScilabFileBrowserModel) tree.getModel();
339 TreePath rootPath = new TreePath(model.getRoot());
341 model.setFilter(pat);
348 public void reload(ScilabFileBrowserModel model) {
349 tree.setModel(model);
350 tree.setRowHeight(getRowHeight());
351 tree.setLargeModel(true);
352 TreePath path = new TreePath(model.getRoot());
353 tree.collapsePath(path);
354 if (resetScrollBar) {
355 ((JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this)).getVerticalScrollBar().setValue(0);
357 resetScrollBar = true;
358 tree.expandPath(path);
359 if (getRowCount() >= 1) {
360 repaint(tree.getRowBounds(0));
365 /* Workaround for BasicTableUI anomaly. Make sure the UI never tries to
366 * paint the editor. The UI currently uses different techniques to
367 * paint the renderers and editors and overriding setBounds() below
368 * is not the right thing to do for an editor. Returning -1 for the
369 * editing row in this case, ensures the editor is never painted.
371 public int getEditingRow() {
372 if (getColumnClass(editingColumn) == ScilabTreeTableModel.class) {
382 private void initActions() {
383 final ActionMap actions = getActionMap();
384 actions.put("scinotes", new OpenFileInSciNotesAction(this));
385 actions.put("xcos", new ExecuteFileInXcosAction(this));
386 actions.put("mat", new ExecuteMatFileAction(this));
387 actions.put("console", new ExecuteFileInConsoleAction(this));
388 actions.put("load", new LoadFileInScilabAction(this));
389 actions.put("graph", new LoadFileAsGraphAction(this));
390 actions.put("cwd", new ChangeCWDAction(this));
391 if (EditFileWithDefaultAppAction.isSupported()) {
392 actions.put("edit", new EditFileWithDefaultAppAction(this));
394 if (OpenFileWithDefaultAppAction.isSupported()) {
395 actions.put("open", new OpenFileWithDefaultAppAction(this));
397 actions.put("validate", new ValidateAction(this));
398 actions.put("validateorexpand", new CommonCallBack(null) {
399 public void callBack() {
400 int[] rows = getSelectedRows();
401 if (rows != null && rows.length != 0) {
402 TreePath path = tree.getPathForRow(rows[0]);
403 FileNode fn = (FileNode) path.getLastPathComponent();
405 ((CommonCallBack) actions.get("validate")).callBack();
407 if (tree.isExpanded(path)) {
408 tree.collapsePath(path);
410 tree.expandPath(path);
412 setRowSelectionInterval(rows[0], rows[0]);
418 combobox.setAction((CommonCallBack) actions.get("cwd"));
419 InputMap map = getInputMap();
420 map.put(KeyStroke.getKeyStroke("ENTER"), "validateorexpand");
424 * Create the popup menu
426 private JPopupMenu createPopup() {
427 ActionMap actions = getActionMap();
428 JPopupMenu popup = new JPopupMenu();
429 JMenuItem item = new JMenuItem(UiDataMessages.OPENINSCINOTES);
430 item.addActionListener(actions.get("scinotes"));
431 item.setIcon(new ImageIcon(FindIconHelper.findIcon("accessories-text-editor")));
434 item = new JMenuItem(UiDataMessages.EXECINCONSOLE);
435 item.addActionListener(actions.get("console"));
436 item.setIcon(new ImageIcon(FindIconHelper.findIcon("media-playback-start")));
439 item = new JMenuItem(UiDataMessages.OPENINXCOS);
440 item.addActionListener(actions.get("xcos"));
441 item.setIcon(new ImageIcon(FindIconHelper.findIcon("utilities-system-monitor")));
444 item = new JMenuItem(UiDataMessages.LOADINSCILAB);
445 item.addActionListener(actions.get("load"));
446 item.setIcon(new ImageIcon(FindIconHelper.findIcon("scilab")));
449 if (actions.get("edit") != null || actions.get("open") != null) {
450 popup.addSeparator();
453 if (actions.get("edit") != null) {
454 item = new JMenuItem(UiDataMessages.EDITWITHDEFAULT);
455 item.addActionListener(actions.get("edit"));
459 if (actions.get("open") != null) {
460 item = new JMenuItem(UiDataMessages.OPENWITHDEFAULT);
461 item.addActionListener(actions.get("open"));
471 public synchronized void setDirRefresher(SwingWorker refresher, ScilabFileBrowserModel model) {
472 dirRefresher = refresher;
477 * A modulo for negative numbers
479 * @param p another int
482 private static final int modulo(int n, int p) {