You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
5.7 KiB
254 lines
5.7 KiB
/* ============================================================
|
|
*
|
|
* This file is a part of digiKam project
|
|
* http://www.digikam.org
|
|
*
|
|
* Date : 2005-02-06
|
|
* Description : an image editor actions undo/redo manager
|
|
*
|
|
* Copyright (C) 2005-2006 by Renchi Raju <renchi@pooh.tam.uiuc.edu>
|
|
* Copyright (C) 2005-2006 Joern Ahrens <joern.ahrens@kdemail.net>
|
|
*
|
|
* This program 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 2, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program 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.
|
|
*
|
|
* ============================================================ */
|
|
|
|
// C++ includes.
|
|
|
|
#include <typeinfo>
|
|
#include <climits>
|
|
|
|
// Local includes.
|
|
|
|
#include "ddebug.h"
|
|
#include "dimginterface.h"
|
|
#include "undoaction.h"
|
|
#include "undocache.h"
|
|
#include "undomanager.h"
|
|
|
|
namespace Digikam
|
|
{
|
|
|
|
class UndoManagerPriv
|
|
{
|
|
|
|
public:
|
|
|
|
UndoManagerPriv()
|
|
{
|
|
dimgiface = 0;
|
|
undoCache = 0;
|
|
origin = 0;
|
|
}
|
|
|
|
TQValueList<UndoAction*> undoActions;
|
|
TQValueList<UndoAction*> redoActions;
|
|
int origin;
|
|
|
|
UndoCache *undoCache;
|
|
|
|
DImgInterface *dimgiface;
|
|
};
|
|
|
|
UndoManager::UndoManager(DImgInterface* iface)
|
|
{
|
|
d = new UndoManagerPriv;
|
|
d->dimgiface = iface;
|
|
d->undoCache = new UndoCache;
|
|
}
|
|
|
|
UndoManager::~UndoManager()
|
|
{
|
|
clear(true);
|
|
delete d->undoCache;
|
|
delete d;
|
|
}
|
|
|
|
void UndoManager::addAction(UndoAction* action)
|
|
{
|
|
if (!action)
|
|
return;
|
|
|
|
// All redo actions are invalid now
|
|
clearRedoActions();
|
|
|
|
d->undoActions.push_back(action);
|
|
|
|
if (typeid(*action) == typeid(UndoActionIrreversible))
|
|
{
|
|
int w = d->dimgiface->origWidth();
|
|
int h = d->dimgiface->origHeight();
|
|
int bytesDepth = d->dimgiface->bytesDepth();
|
|
uchar* data = d->dimgiface->getImage();
|
|
|
|
d->undoCache->putData(d->undoActions.size(), w, h, bytesDepth, data);
|
|
}
|
|
|
|
// if origin is at one of the redo action that are now invalid,
|
|
// it is no longer reachable
|
|
if (d->origin < 0)
|
|
d->origin = INT_MAX;
|
|
else
|
|
d->origin++;
|
|
}
|
|
|
|
void UndoManager::undo()
|
|
{
|
|
if (d->undoActions.isEmpty())
|
|
return;
|
|
|
|
UndoAction* action = d->undoActions.back();
|
|
|
|
if (typeid(*action) == typeid(UndoActionIrreversible))
|
|
{
|
|
// Save the current state for the redo operation
|
|
|
|
int w = d->dimgiface->origWidth();
|
|
int h = d->dimgiface->origHeight();
|
|
int bytesDepth = d->dimgiface->bytesDepth();
|
|
uchar* data = d->dimgiface->getImage();
|
|
|
|
d->undoCache->putData(d->undoActions.size() + 1, w, h, bytesDepth, data);
|
|
|
|
// And now, undo the action
|
|
|
|
int newW, newH, newBytesDepth;
|
|
uchar *newData = d->undoCache->getData(d->undoActions.size(), newW, newH, newBytesDepth, false);
|
|
if (newData)
|
|
{
|
|
d->dimgiface->putImage(newData, newW, newH, newBytesDepth == 8 ? true : false);
|
|
delete [] newData;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
action->rollBack();
|
|
}
|
|
|
|
d->undoActions.pop_back();
|
|
d->redoActions.push_back(action);
|
|
d->origin--;
|
|
}
|
|
|
|
void UndoManager::redo()
|
|
{
|
|
if(d->redoActions.isEmpty())
|
|
return;
|
|
|
|
UndoAction *action = d->redoActions.back();
|
|
|
|
if(typeid(*action) == typeid(UndoActionIrreversible))
|
|
{
|
|
int w, h, bytesDepth;
|
|
uchar *data = d->undoCache->getData(d->undoActions.size() + 2, w, h, bytesDepth, false);
|
|
if (data)
|
|
{
|
|
d->dimgiface->putImage(data, w, h, bytesDepth == 8 ? true : false);
|
|
delete[] data;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
action->execute();
|
|
}
|
|
|
|
d->redoActions.pop_back();
|
|
d->undoActions.push_back(action);
|
|
d->origin++;
|
|
}
|
|
|
|
void UndoManager::clear(bool clearCache)
|
|
{
|
|
clearUndoActions();
|
|
clearRedoActions();
|
|
setOrigin();
|
|
|
|
if(clearCache)
|
|
d->undoCache->clear();
|
|
}
|
|
|
|
void UndoManager::clearUndoActions()
|
|
{
|
|
UndoAction *action;
|
|
TQValueList<UndoAction*>::iterator it;
|
|
|
|
for(it = d->undoActions.begin(); it != d->undoActions.end(); ++it)
|
|
{
|
|
action = *it;
|
|
delete action;
|
|
}
|
|
d->undoActions.clear();
|
|
}
|
|
|
|
void UndoManager::clearRedoActions()
|
|
{
|
|
if(!anyMoreRedo())
|
|
return;
|
|
|
|
UndoAction *action;
|
|
TQValueList<UndoAction*>::iterator it;
|
|
|
|
// get the level of the first redo action
|
|
int level = d->undoActions.size() + 1;
|
|
for(it = d->redoActions.begin(); it != d->redoActions.end(); ++it)
|
|
{
|
|
action = *it;
|
|
d->undoCache->erase(level);
|
|
delete action;
|
|
level++;
|
|
}
|
|
d->undoCache->erase(level);
|
|
d->redoActions.clear();
|
|
}
|
|
|
|
bool UndoManager::anyMoreUndo()
|
|
{
|
|
return !d->undoActions.isEmpty();
|
|
}
|
|
|
|
bool UndoManager::anyMoreRedo()
|
|
{
|
|
return !d->redoActions.isEmpty();
|
|
}
|
|
|
|
void UndoManager::getUndoHistory(TQStringList &titles)
|
|
{
|
|
TQValueList<UndoAction*>::iterator it;
|
|
|
|
for(it = d->undoActions.begin(); it != d->undoActions.end(); ++it)
|
|
{
|
|
titles.push_front((*it)->getTitle());
|
|
}
|
|
}
|
|
|
|
void UndoManager::getRedoHistory(TQStringList &titles)
|
|
{
|
|
TQValueList<UndoAction*>::iterator it;
|
|
|
|
for(it = d->redoActions.begin(); it != d->redoActions.end(); ++it)
|
|
{
|
|
titles.push_front((*it)->getTitle());
|
|
}
|
|
}
|
|
|
|
bool UndoManager::isAtOrigin()
|
|
{
|
|
return d->origin == 0;
|
|
}
|
|
|
|
void UndoManager::setOrigin()
|
|
{
|
|
d->origin = 0;
|
|
}
|
|
|
|
} // namespace Digikam
|