/*
* 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
}
}