/* * Copyright 2010-2015 Institut Pasteur. * * This file is part of Icy. * * Icy is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Icy is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.gui.inspector; import icy.action.SequenceOperationActions; import icy.gui.component.button.IcyButton; import icy.gui.main.ActiveSequenceListener; import icy.main.Icy; import icy.preferences.GeneralPreferences; import icy.sequence.Sequence; import icy.sequence.SequenceEvent; import icy.system.thread.ThreadUtil; import icy.undo.AbstractIcyUndoableEdit; import icy.undo.IcyUndoManager; import icy.undo.IcyUndoManagerListener; import java.awt.BorderLayout; import java.awt.Component; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.Icon; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.ScrollPaneConstants; import javax.swing.SpinnerNumberModel; import javax.swing.border.EmptyBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableColumn; import javax.swing.table.TableColumnModel; /** * @author Stephane */ public class UndoManagerPanel extends JPanel implements ActiveSequenceListener, ListSelectionListener, IcyUndoManagerListener, ChangeListener { /** * */ private static final long serialVersionUID = 1464754827529975860L; static final String[] columnNames = {"", "Action"}; protected IcyUndoManager undoManager; // GUI AbstractTableModel tableModel; ListSelectionModel tableSelectionModel; JTable table; // internals boolean isSelectionAdjusting; IcyButton undoButton; IcyButton redoButton; JSpinner historySizeField; IcyButton clearAllButLastButton; IcyButton clearAllButton; final Runnable refresher; public UndoManagerPanel() { super(); undoManager = null; isSelectionAdjusting = false; initialize(); historySizeField.setValue(Integer.valueOf(GeneralPreferences.getHistorySize())); historySizeField.addChangeListener(this); refresher = new Runnable() { @Override public void run() { refreshTableDataAndActions(); } }; refresher.run(); } private void initialize() { // build table tableModel = new AbstractTableModel() { /** * */ private static final long serialVersionUID = -8573364273165723214L; @Override public int getColumnCount() { return columnNames.length; } @Override public String getColumnName(int column) { return columnNames[column]; } @Override public int getRowCount() { if (undoManager != null) return undoManager.getEditsCount() + 1; return 1; } @Override public Object getValueAt(int row, int column) { if (row == 0) { if (column == 0) return null; if (undoManager != null) return "Initial state"; return "No opened sequence"; } if (undoManager != null) { final AbstractIcyUndoableEdit edit = undoManager.getEdit(row - 1); switch (column) { case 0: return edit.getIcon(); case 1: return edit.getPresentationName(); } } return ""; } @Override public boolean isCellEditable(int row, int column) { return false; } @Override public Class<?> getColumnClass(int columnIndex) { if (columnIndex == 0) return Icon.class; return String.class; } }; table = new JTable(tableModel); table.setToolTipText("Click on an action to undo or redo until that point"); final TableColumnModel colModel = table.getColumnModel(); TableColumn col; // columns setting col = colModel.getColumn(0); col.setPreferredWidth(20); col.setMinWidth(20); col.setMaxWidth(20); col = colModel.getColumn(1); col.setPreferredWidth(100); col.setMinWidth(60); table.setRowHeight(20); table.setColumnSelectionAllowed(false); table.setRowSelectionAllowed(true); table.setShowVerticalLines(true); table.setAutoCreateRowSorter(false); table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); tableSelectionModel = table.getSelectionModel(); tableSelectionModel.addListSelectionListener(this); tableSelectionModel.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); final JPanel middlePanel = new JPanel(); middlePanel.setLayout(new BoxLayout(middlePanel, BoxLayout.PAGE_AXIS)); middlePanel.add(table.getTableHeader()); final JScrollPane sc = new JScrollPane(table, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); sc.setToolTipText(""); middlePanel.add(sc); final JPanel bottomPanel = new JPanel(); bottomPanel.setBorder(new EmptyBorder(2, 0, 0, 0)); bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.LINE_AXIS)); undoButton = new IcyButton(SequenceOperationActions.undoAction); undoButton.setFlat(true); undoButton.setHideActionText(true); bottomPanel.add(undoButton); redoButton = new IcyButton(SequenceOperationActions.redoAction); redoButton.setFlat(true); redoButton.setHideActionText(true); bottomPanel.add(redoButton); Component horizontalGlue = Box.createHorizontalGlue(); bottomPanel.add(horizontalGlue); JLabel lblNewLabel = new JLabel("History size"); lblNewLabel.setToolTipText(""); bottomPanel.add(lblNewLabel); Component horizontalStrut = Box.createHorizontalStrut(8); bottomPanel.add(horizontalStrut); historySizeField = new JSpinner(); historySizeField.setModel(new SpinnerNumberModel(50, 1, 200, 1)); historySizeField.setToolTipText("Maximum size of the history (lower value will reduce memory usage)"); bottomPanel.add(historySizeField); Component horizontalStrut_2 = Box.createHorizontalStrut(8); bottomPanel.add(horizontalStrut_2); clearAllButLastButton = new IcyButton(SequenceOperationActions.undoClearAllButLastAction); clearAllButLastButton.setFlat(true); clearAllButLastButton.setHideActionText(true); bottomPanel.add(clearAllButLastButton); clearAllButton = new IcyButton(SequenceOperationActions.undoClearAction); clearAllButton.setFlat(true); clearAllButton.setHideActionText(true); bottomPanel.add(clearAllButton); setLayout(new BorderLayout()); add(middlePanel, BorderLayout.CENTER); add(bottomPanel, BorderLayout.SOUTH); } public void setUndoManager(IcyUndoManager value) { if (undoManager != value) { if (undoManager != null) undoManager.removeListener(this); undoManager = value; if (undoManager != null) undoManager.addListener(this); // refresh data and actions ThreadUtil.bgRunSingle(refresher); } } // /** // * Return index of specified Edit // */ // protected int getEditIndex(AbstractIcyUndoableEdit edit) // { // if (undoManager != null) // return undoManager.getSignificantIndex(edit); // // return -1; // } public AbstractIcyUndoableEdit getLastSelectedEdit() { if (undoManager != null) { final int index = tableSelectionModel.getMaxSelectionIndex(); if (index > 0) return undoManager.getSignificantEdit(index - 1); } return null; } protected void refreshTableDataAndActions() { ThreadUtil.invokeNow(new Runnable() { @Override public void run() { isSelectionAdjusting = true; try { tableModel.fireTableDataChanged(); if (undoManager != null) tableSelectionModel.setSelectionInterval(0, undoManager.getNextAddIndex()); else tableSelectionModel.setSelectionInterval(0, 0); } finally { isSelectionAdjusting = false; } if (undoManager != null) { undoButton.setEnabled(undoManager.canUndo()); redoButton.setEnabled(undoManager.canRedo()); clearAllButLastButton.setEnabled(undoManager.canUndo()); clearAllButton.setEnabled(undoManager.canUndo() || undoManager.canRedo()); } else { undoButton.setEnabled(false); redoButton.setEnabled(false); clearAllButLastButton.setEnabled(false); clearAllButton.setEnabled(false); } } }); } /** * called when selection has changed */ protected void selectionChanged() { // process undo / redo operation if (undoManager != null) { final AbstractIcyUndoableEdit selectedEdit = getLastSelectedEdit(); // first entry if (selectedEdit == null) undoManager.undoAll(); else undoManager.undoOrRedoTo(selectedEdit); } } @Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() || isSelectionAdjusting) return; if (tableSelectionModel.getMinSelectionIndex() != 0) tableSelectionModel.setSelectionInterval(0, tableSelectionModel.getMaxSelectionIndex()); else selectionChanged(); } @Override public void stateChanged(ChangeEvent e) { final int value = ((Integer) historySizeField.getValue()).intValue(); // change size of all current active undo manager for (Sequence sequence : Icy.getMainInterface().getSequences()) { final IcyUndoManager um = sequence.getUndoManager(); if (um != null) um.setLimit(value); } GeneralPreferences.setHistorySize(value); refreshTableDataAndActions(); } @Override public void undoManagerChanged(IcyUndoManager source) { ThreadUtil.bgRunSingle(refresher); } @Override public void sequenceActivated(Sequence sequence) { if (sequence == null) setUndoManager(null); else setUndoManager(sequence.getUndoManager()); } @Override public void sequenceDeactivated(Sequence sequence) { // nothing here } @Override public void activeSequenceChanged(SequenceEvent event) { // nothing here } }