/*
* Copyright (C) 2007 Quadduc <quadduc@gmail.com>
*
* This file is part of LateralGM.
* LateralGM is free software and comes with ABSOLUTELY NO WARRANTY.
* See LICENSE for details.
*/
package org.lateralgm.components.impl;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.DocumentEvent.EventType;
import javax.swing.text.AbstractDocument;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.CompoundEdit;
import javax.swing.undo.UndoManager;
import javax.swing.undo.UndoableEdit;
import org.lateralgm.main.LGM;
import org.lateralgm.messages.Messages;
public class DocumentUndoManager extends UndoManager implements CaretListener
{
private static final long serialVersionUID = 1L;
protected final AbstractAction undoAction;
protected final AbstractAction redoAction;
protected int caretUpdates = 0;
protected MarkerEdit modifiedMarker;
public DocumentUndoManager()
{
undoAction = new AbstractAction(Messages.getString("DocumentUndoManager.UNDO"),
LGM.getIconForKey("DocumentUndoManager.UNDO"))
{
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent ae)
{
endGroupEdit();
try
{
undo();
}
catch (CannotUndoException e)
{
//Nothing to undo
}
updateActions();
}
};
redoAction = new AbstractAction(Messages.getString("DocumentUndoManager.REDO"),
LGM.getIconForKey("DocumentUndoManager.REDO"))
{
private static final long serialVersionUID = 1L;
public void actionPerformed(ActionEvent ae)
{
try
{
redo();
}
catch (CannotRedoException e)
{
//Nothing to redo
}
updateActions();
}
};
resetModifiedMarker();
updateActions();
}
public void resetModifiedMarker()
{
if (modifiedMarker != null) modifiedMarker.die();
modifiedMarker = new MarkerEdit();
super.addEdit(modifiedMarker);
}
public boolean isModified()
{
/* In order to know whether we're at the marker, we temporarily make it significant, so
* that editToBeUndone() doesn't ignore it. */
modifiedMarker.significant = true;
boolean result = editToBeUndone() != modifiedMarker;
modifiedMarker.significant = false;
return result;
}
@Override
public synchronized boolean addEdit(UndoableEdit anEdit)
{
UndoableEdit le = lastEdit();
try
{
if (le instanceof GroupEdit)
{
GroupEdit ge = (GroupEdit) le;
if (ge.addEdit(anEdit)) return true;
}
endGroupEdit();
GroupEdit ge = new GroupEdit();
if (ge.addEdit(anEdit) && super.addEdit(ge)) return true;
return super.addEdit(anEdit);
}
finally
{
updateActions();
}
}
public void endGroupEdit()
{
UndoableEdit le = lastEdit();
if (le instanceof GroupEdit) ((GroupEdit) le).end();
}
private class GroupEdit extends CompoundEdit
{
private static final long serialVersionUID = 1L;
public GroupEdit()
{
super();
}
@Override
public boolean canUndo()
{
if (edits.size() == 0 || edits.lastElement() != null && edits.lastElement().canUndo()
|| super.canUndo()) return true;
return false;
}
@Override
public boolean addEdit(UndoableEdit anEdit)
{
UndoableEdit le = lastEdit();
if (!(anEdit instanceof AbstractDocument.DefaultDocumentEvent)) return false;
AbstractDocument.DefaultDocumentEvent dde1, dde2;
dde2 = (AbstractDocument.DefaultDocumentEvent) anEdit;
EventType et = dde2.getType();
if (le != null)
{
dde1 = (AbstractDocument.DefaultDocumentEvent) le;
if (dde1.getType().equals(et))
{
int o1 = dde1.getOffset();
int o2 = dde2.getOffset();
int l1 = dde1.getLength();
int l2 = dde2.getLength();
if (et.equals(EventType.INSERT) && o2 == o1 + l1)
{
return super.addEdit(anEdit);
}
else if (et.equals(EventType.REMOVE) && (o2 == o1 + l1 || o1 == o2 + l2))
{
return super.addEdit(anEdit);
}
}
}
else if (et.equals(EventType.INSERT) || et.equals(EventType.REMOVE))
return super.addEdit(anEdit);
return false;
}
}
public void updateActions()
{
undoAction.setEnabled(canUndo());
redoAction.setEnabled(canRedo());
}
public AbstractAction getRedoAction()
{
return redoAction;
}
public AbstractAction getUndoAction()
{
return undoAction;
}
public void caretUpdate(CaretEvent e)
{
if (caretUpdates > 0) endGroupEdit();
caretUpdates++;
}
public void undoableEditHappened(UndoableEditEvent e)
{
caretUpdates = 0;
addEdit(e.getEdit());
}
private class MarkerEdit extends AbstractUndoableEdit
{
private static final long serialVersionUID = 1L;
protected boolean significant = false;
public MarkerEdit()
{
super();
}
@Override
public boolean isSignificant()
{
return significant;
}
}
}