2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2010 - DIGITEO - Vincent COUVERT
4 * Copyright (C) 2010 - DIGITEO - Allan CORNET
5 * Copyright (C) 2011 - Calixte DENIZET
7 * This file must be used under the terms of the CeCILL.
8 * This source file is licensed as described in the file COPYING, which
9 * you should have received as part of this distribution. The terms
10 * are also available at
11 * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
15 package org.scilab.modules.history_browser;
17 import java.awt.BorderLayout;
18 import java.awt.Color;
19 import java.awt.Component;
20 import java.awt.Graphics;
21 import java.awt.event.ActionEvent;
22 import java.awt.event.ActionListener;
23 import java.util.ArrayList;
24 import java.util.Collections;
26 import javax.swing.BoundedRangeModel;
27 import javax.swing.JPanel;
28 import javax.swing.JScrollBar;
29 import javax.swing.JScrollPane;
30 import javax.swing.JTree;
31 import javax.swing.SwingUtilities;
32 import javax.swing.Timer;
33 import javax.swing.tree.DefaultMutableTreeNode;
34 import javax.swing.tree.DefaultTreeCellRenderer;
35 import javax.swing.tree.DefaultTreeModel;
36 import javax.swing.tree.ExpandVetoException;
37 import javax.swing.tree.TreeModel;
38 import javax.swing.tree.TreeNode;
39 import javax.swing.tree.TreePath;
41 import org.scilab.modules.gui.bridge.tab.SwingScilabTab;
42 import org.scilab.modules.gui.bridge.window.SwingScilabWindow;
43 import org.scilab.modules.gui.menu.Menu;
44 import org.scilab.modules.gui.menu.ScilabMenu;
45 import org.scilab.modules.gui.menubar.MenuBar;
46 import org.scilab.modules.gui.menubar.ScilabMenuBar;
47 import org.scilab.modules.gui.messagebox.MessageBox;
48 import org.scilab.modules.gui.messagebox.ScilabMessageBox;
49 import org.scilab.modules.gui.tab.SimpleTab;
50 import org.scilab.modules.gui.tabfactory.ScilabTabFactory;
51 import org.scilab.modules.gui.textbox.ScilabTextBox;
52 import org.scilab.modules.gui.textbox.TextBox;
53 import org.scilab.modules.gui.toolbar.ScilabToolBar;
54 import org.scilab.modules.gui.toolbar.ToolBar;
55 import org.scilab.modules.gui.utils.WindowsConfigurationManager;
56 import org.scilab.modules.gui.window.ScilabWindow;
57 import org.scilab.modules.history_browser.actions.ClearAction;
58 import org.scilab.modules.history_browser.actions.CloseAction;
59 import org.scilab.modules.history_browser.actions.CopyAction;
60 import org.scilab.modules.history_browser.actions.CutAction;
61 import org.scilab.modules.history_browser.actions.DeleteAction;
62 import org.scilab.modules.history_browser.actions.EditInScinotesAction;
63 import org.scilab.modules.history_browser.actions.EvaluateAction;
64 import org.scilab.modules.history_browser.actions.HelpAction;
65 import org.scilab.modules.history_manager.HistoryManagement;
66 import org.scilab.modules.localization.Messages;
69 * Main Scilab Command History GUI
70 * @author Vincent COUVERT
71 * @author Calixte DENIZET
73 @SuppressWarnings(value = { "serial" })
74 public final class CommandHistory extends SwingScilabTab implements SimpleTab {
76 public static final String COMMANDHISTORYUUID = "856207f6-0a60-47a0-b9f4-232feedd4bf4";
78 private static final int DEFAULT_WIDTH = 450;
79 private static final int DEFAULT_HEIGHT = 550;
80 private static final String NEWLINE = "\n";
81 private static final String SESSION_BEGINNING = "// -- ";
82 private static final String SESSION_ENDING = " -- //";
84 private static HistoryTree scilabHistoryTree;
85 private static DefaultMutableTreeNode scilabHistoryRootNode;
86 private static DefaultMutableTreeNode currentSessionNode;
87 private static DefaultTreeModel scilabHistoryTreeModel;
88 private static SwingScilabTab browserTab;
89 private static JScrollPane scrollPane;
91 private static boolean modelLoaded;
92 private static boolean initialized;
94 private static java.util.List<String> linesToAppend;
95 private static javax.swing.Timer linesToAppendTimer;
98 ScilabTabFactory.getInstance().addTabFactory(CommandHistoryTabFactory.getInstance());
100 linesToAppend = Collections.synchronizedList(new ArrayList<String>());
101 linesToAppendTimer = new Timer(0, new ActionListener() {
102 public void actionPerformed(ActionEvent e) {
106 linesToAppendTimer.setRepeats(false);
112 private CommandHistory() {
113 super(CommandHistoryMessages.TITLE, COMMANDHISTORYUUID);
114 setAssociatedXMLIDForHelp("historybrowser");
116 addMenuBar(createMenuBar());
117 addToolBar(createToolBar());
118 addInfoBar(ScilabTextBox.createTextBox());
120 scilabHistoryTree.addMouseListener(new CommandHistoryMouseListener());
122 DeleteAction.registerKeyAction();
123 EvaluateAction.registerKeyAction();
124 CopyAction.registerKeyAction();
125 CutAction.registerKeyAction();
126 CloseAction.registerKeyAction();
128 scrollPane = new JScrollPane(scilabHistoryTree);
129 JPanel contentPane = new JPanel(new BorderLayout());
130 contentPane.add(scrollPane);
131 setContentPane(contentPane);
135 * Initialize the History Browser at Scilab launch
136 * Called directly from Scilab
138 public static void initialize() {
140 scilabHistoryRootNode = new DefaultMutableTreeNode(Messages.gettext("History loading in progress..."));
141 scilabHistoryTreeModel = new DefaultTreeModel(scilabHistoryRootNode);
142 scilabHistoryTree = new HistoryTree(scilabHistoryTreeModel);
143 scilabHistoryTree.setShowsRootHandles(true);
144 scilabHistoryTree.setDragEnabled(true);
145 scilabHistoryTree.setEnabled(true);
146 scilabHistoryTree.setRootVisible(false);
147 scilabHistoryTree.setScrollsOnExpand(true);
148 scilabHistoryTree.setVisible(false);
150 // Under Windows the directory icon is used: bad....
151 DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) scilabHistoryTree.getCellRenderer();
152 renderer.setLeafIcon(null);
153 renderer.setClosedIcon(null);
154 renderer.setOpenIcon(null);
161 * Create a new Command History tab
162 * @return the corresponding tab
164 public static SwingScilabTab createCommandHistoryTab() {
165 browserTab = new CommandHistory();
166 WindowsConfigurationManager.restorationFinished(browserTab);
174 public void addInfoBar(TextBox infoBarToAdd) {
175 setInfoBar(infoBarToAdd);
181 public void addMenuBar(MenuBar menuBarToAdd) {
182 setMenuBar(menuBarToAdd);
188 public void addToolBar(ToolBar toolBarToAdd) {
189 setToolBar(toolBarToAdd);
195 public SwingScilabWindow getParentWindow() {
196 return SwingScilabWindow.allScilabWindows.get(getParentWindowId());
202 public SimpleTab getAsSimpleTab() {
207 * Update the browser once an history file has been loaded
209 public static void loadFromFile() {
210 final String historyLines[] = HistoryManagement.getAllLinesOfScilabHistory();
212 SwingUtilities.invokeLater(new Runnable() {
216 int nbEntries = historyLines.length;
217 for (int entryIndex = 0; entryIndex < nbEntries; entryIndex++) {
218 /* Do not expand at each insertion for performances reasons */
219 appendLineAndExpand(historyLines[entryIndex], false);
224 /* Expand all sessions tree */
228 public static void expandAll() {
229 if (isHistoryVisible()) {
230 // put the expansion in an invokeLater to avoid some kind of freeze with huge history
231 SwingUtilities.invokeLater(new Runnable() {
233 scilabHistoryTree.setVisible(true);
235 scilabHistoryTreeModel.nodeStructureChanged((TreeNode) scilabHistoryTreeModel.getRoot());
239 final Object root = scilabHistoryTreeModel.getRoot();
240 final TreePath pathRoot = new TreePath(root);
241 final int N = scilabHistoryTreeModel.getChildCount(root);
242 scilabHistoryTree.mustFire = false;
243 for (int i = 0; i < N; i++) {
244 Object o = scilabHistoryTreeModel.getChild(root, i);
245 if (!scilabHistoryTreeModel.isLeaf(o)) {
246 scilabHistoryTree.expandPath(pathRoot.pathByAddingChild(o));
249 scilabHistoryTree.mustFire = true;
250 scilabHistoryTree.fireTreeExpanded(pathRoot);
259 * Add a new line to the History Browser
260 * @param lineToAppend the line to append
262 public static void appendLine(String lineToAppend) {
263 synchronized (linesToAppend) {
264 linesToAppend.add(lineToAppend);
265 linesToAppendTimer.start();
269 public static void appendLinesOnEDT() {
270 synchronized (linesToAppend) {
271 for (String lineToAppend : linesToAppend) {
272 appendLineAndExpand(lineToAppend, true);
274 linesToAppend.clear();
279 * check if line is a begin session
280 * @param line to check
281 * @retour true or false
283 private static boolean isBeginSessionLine(String lineToAppend) {
284 if (lineToAppend.startsWith(SESSION_BEGINNING) && lineToAppend.endsWith(SESSION_ENDING)) {
291 * Add a new line to the History Browser
292 * @param lineToAppend the line to append
293 * @param expand do we need to expand all session nodes?
295 public static void appendLineAndExpand(String lineToAppend, boolean expand) {
296 if (isBeginSessionLine(lineToAppend)) {
297 // Create a new session node
298 currentSessionNode = new DefaultMutableTreeNode(new SessionString(lineToAppend));
299 scilabHistoryTreeModel.insertNodeInto(currentSessionNode, scilabHistoryRootNode, scilabHistoryRootNode.getChildCount());
300 if (expand && isHistoryVisible()) {
301 scilabHistoryTree.expandRow(scilabHistoryTree.getRowCount() - 1);
302 scilabHistoryTree.scrollPathToVisible(new TreePath(currentSessionNode.getPath()));
305 boolean mustScroll = false;
306 if (expand && isHistoryVisible()) {
307 JScrollBar vb = scrollPane.getVerticalScrollBar();
309 BoundedRangeModel model = vb.getModel();
310 // mustScroll is true if the knob is at the bottom of the scollbar.
311 mustScroll = model.getValue() == model.getMaximum() - model.getExtent();
314 DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(lineToAppend);
315 scilabHistoryTreeModel.insertNodeInto(childNode, currentSessionNode, currentSessionNode.getChildCount());
316 if (expand && isHistoryVisible()) {
317 scilabHistoryTree.expandRow(scilabHistoryTree.getRowCount() - 1);
319 scilabHistoryTree.scrollPathToVisible(new TreePath(childNode.getPath()));
326 * Reset the History Browser (after a clear)
328 public static void reset() {
329 scilabHistoryRootNode.removeAllChildren();
330 scilabHistoryTreeModel.reload();
331 currentSessionNode = null;
335 * Remove an entry from History
336 * @param lineNumber the number of the line
338 public static void deleteLine(int lineNumber) {
339 int numberOfSessions = scilabHistoryRootNode.getChildCount();
340 int sessionIndex = 0;
341 int numberOfLines = 0;
343 while (sessionIndex < numberOfSessions) {
344 if (numberOfLines == lineNumber) {
345 // Do we try to delete the last session node (which it is the current session used)
346 if (sessionIndex == (numberOfSessions - 1)) {
348 MessageBox errorMsg = ScilabMessageBox.createMessageBox();
349 errorMsg.setTitle(CommandHistoryMessages.ERROR);
350 errorMsg.setMessage(CommandHistoryMessages.CANNOT_DELETE_CURRENT_SESSION_NODE);
351 errorMsg.setIcon("error");
352 errorMsg.displayAndWait();
356 // Otherwise it is OK, delete session node
357 TreeNode sessionNode = scilabHistoryRootNode.getChildAt(sessionIndex);
358 scilabHistoryRootNode.remove(sessionIndex);
359 scilabHistoryTreeModel.nodesWereRemoved(scilabHistoryRootNode, new int[] {sessionIndex},
360 new Object[] {sessionNode});
364 DefaultMutableTreeNode sessionNode = (DefaultMutableTreeNode) scilabHistoryRootNode.getChildAt(sessionIndex);
366 // Did we reach the session containing the line to remove ?
367 if (numberOfLines + sessionNode.getChildCount() >= lineNumber) {
368 // Yes, delete the child line
369 int childIndex = lineNumber - numberOfLines - 1;
370 TreeNode childNode = sessionNode.getChildAt(childIndex);
371 sessionNode.remove(childIndex);
372 scilabHistoryTreeModel.nodesWereRemoved(sessionNode, new int[] {childIndex},
373 new Object[] {childNode} );
376 /* No, jump to next session */
377 numberOfLines += sessionNode.getChildCount() + 1;
386 public static void closeHistory() {
391 * @return the browserTab
393 public static SwingScilabTab getBrowserTab() {
398 * Manage History Browser visibility
400 public static void setVisible() {
401 if (browserTab == null) {
402 boolean success = WindowsConfigurationManager.restoreUUID(COMMANDHISTORYUUID);
404 CommandHistoryTabFactory.getInstance().getTab(COMMANDHISTORYUUID);
405 SwingScilabWindow window = (SwingScilabWindow) ScilabWindow.createWindow().getAsSimpleWindow();
406 window.addTab(browserTab);
407 window.setLocation(0, 0);
408 window.setSize(500, 500);
409 window.setVisible(true);
412 browserTab.setVisible(true);
417 * Launch the history browser
419 public static void launchHistoryBrowser() {
424 * Get History Browser visibility
425 * @return visibility status
427 private static boolean isHistoryVisible() {
428 return browserTab != null && browserTab.isVisible();
432 * Create History Browser MenuBar
433 * @return the menu bar
435 private static MenuBar createMenuBar() {
436 MenuBar menuBar = ScilabMenuBar.createMenuBar();
438 Menu fileMenu = ScilabMenu.createMenu();
439 fileMenu.setText(CommandHistoryMessages.FILE);
440 fileMenu.setMnemonic('F');
442 fileMenu.add(CloseAction.createMenuItem());
444 menuBar.add(fileMenu);
446 Menu editMenu = ScilabMenu.createMenu();
447 editMenu.setText(CommandHistoryMessages.EDIT);
448 editMenu.setMnemonic('E');
450 editMenu.add(CopyAction.createMenuItem());
451 editMenu.add(CutAction.createMenuItem());
452 editMenu.add(EvaluateAction.createMenuItem());
453 editMenu.add(EditInScinotesAction.createMenuItem());
454 editMenu.addSeparator();
455 editMenu.add(DeleteAction.createMenuItem());
456 editMenu.add(ClearAction.createMenuItem());
458 menuBar.add(editMenu);
460 Menu helpMenu = ScilabMenu.createMenu();
461 helpMenu.setText(CommandHistoryMessages.HELP);
462 helpMenu.setMnemonic('?');
464 helpMenu.add(HelpAction.createMenuItem());
466 menuBar.add(helpMenu);
472 * Create History Browser ToolBar
473 * @return the tool bar
475 private static ToolBar createToolBar() {
476 ToolBar toolBar = ScilabToolBar.createToolBar();
478 toolBar.add(CopyAction.createPushButton());
479 toolBar.add(CutAction.createPushButton());
480 toolBar.add(DeleteAction.createPushButton());
482 toolBar.addSeparator();
484 toolBar.add(HelpAction.createPushButton());
493 public static JTree getTree() {
494 return scilabHistoryTree;
498 * Get the JTree Model
499 * @return the tree model
501 public static DefaultTreeModel getTreeModel() {
502 return scilabHistoryTreeModel;
506 * Get the selected commands and store them into an "executable" string
509 public static String getSelectedCommands() {
510 TreePath[] selectedPaths = CommandHistory.getTree().getSelectionPaths();
512 if (selectedPaths == null) {
516 String selectedEntries = new String();
518 for (int i = 0; i < selectedPaths.length; i++) {
519 Object obj = ((DefaultMutableTreeNode) selectedPaths[i].getLastPathComponent()).getUserObject();
520 selectedEntries += obj.toString();
522 if (i < selectedPaths.length - 1) {
523 selectedEntries += NEWLINE;
527 return selectedEntries;
530 private static void scrollAtBottom() {
531 SwingUtilities.invokeLater(new Runnable() {
533 scrollPane.getHorizontalScrollBar().setValue(0);
534 scrollPane.getVerticalScrollBar().setValue(scrollPane.getVerticalScrollBar().getMaximum());
541 * Inner class to render the session nodes in green
543 static class SessionString {
547 SessionString(String s) {
551 public String toString() {
556 @SuppressWarnings(value = { "serial" })
557 static class HistoryTree extends JTree {
559 private boolean first = true;
560 private Color defaultColor;
561 private Color sessionColor = new Color(1, 168, 1);
562 boolean mustFire = true;
564 HistoryTree(TreeModel model) {
569 setCellRenderer(new DefaultTreeCellRenderer() {
571 defaultColor = getTextNonSelectionColor();
574 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) {
575 super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
576 if (((DefaultMutableTreeNode) value).getUserObject() instanceof SessionString) {
577 setTextNonSelectionColor(sessionColor);
579 setTextNonSelectionColor(defaultColor);
587 public void fireTreeExpanded(TreePath path) {
589 super.fireTreeExpanded(path);
593 public void fireTreeWillExpand(TreePath path) throws ExpandVetoException {
595 super.fireTreeWillExpand(path);
599 public void paint(final Graphics g) {
601 g.setFont(getFont());
602 int height = g.getFontMetrics().getHeight();
603 setRowHeight(height);
606 scrollPane.getVerticalScrollBar().setUnitIncrement(height);
611 } catch (Exception e) {
612 SwingUtilities.invokeLater(new Runnable() {