/*
For general Scribus (>=1.3.2) copyright and licensing information please refer
to the COPYING file provided with the program. Following this notice may exist
a copyright and/or license notice that predates the release of Scribus 1.3.2
for which a new license (GPL+exception) is in place.
*/
/***************************************************************************
 *   Copyright (C) 2005 by Riku Leino                                      *
 *   tsoots@gmail.com                                                      *
 *                                                                         *
 *   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 of the License, 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.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "undomanager.h"
#include "undomanager.moc"
#include "undogui.h"
#include "scconfig.h"
#include "prefsmanager.h"
#include "prefscontext.h"
#include "prefsfile.h"
#include <qvaluelist.h>

#include "scpaths.h"

extern QPixmap loadIcon(QString nam);

UndoManager* UndoManager::_instance          = 0;
bool         UndoManager::_undoEnabled       = true;
int          UndoManager::undoEnabledCounter = 0;

UndoManager* UndoManager::instance()
{
	if (_instance == 0)
		_instance = new UndoManager();

	return _instance;
}

void UndoManager::setUndoEnabled(bool isEnabled)
{
	if (isEnabled && undoEnabledCounter == 0)
		return; // nothing to do undo is already enabled.
	else if (isEnabled && undoEnabledCounter > 0)
		--undoEnabledCounter;
	else if (!isEnabled)
		++undoEnabledCounter;

	_undoEnabled = undoEnabledCounter == 0;
	if (_undoEnabled)
		connectGuis();
	else if (undoEnabledCounter == 1)
		disconnectGuis(); // disconnect only once when setUndoEnabled(false) has been called
		                  // no need to call again if next setUndoEnabled() call will also be false.
}

bool UndoManager::undoEnabled()
{
	return _undoEnabled;
}

UndoManager::UndoManager()
{
	currentUndoObjectId = -1;
	if (!UndoManager::IGuides)
		initIcons();
	prefs = PrefsManager::instance()->prefsFile->getContext("undo");
	historyLength = prefs->getInt("historyLength", 20);
	if (historyLength < 0)
		historyLength = 10;
	transaction = 0;
	transactionTarget = 0;
	languageChange();
}

void UndoManager::beginTransaction(const QString &targetName,
                                   QPixmap *targetPixmap,
                                   const QString &name,
                                   const QString &description,
                                   QPixmap *actionPixmap)
{
	if (!_undoEnabled)
		return;
	if (transaction) // begin a transaction inside transaction
		transactions.push_back(
			std::pair<TransactionObject*, TransactionState*>(transactionTarget, transaction));
	transaction = new TransactionState();
	transactionTarget = new TransactionObject();
	transactionTarget->setUName(targetName); // Name which will be in action history
	if (targetPixmap)
		transactionTarget->setUPixmap(targetPixmap);
	if (name.length() > 0)          // if left to 0 length action will be fetched from the
		transaction->setName(name); // last added UndoState in this transaction
	if (description.length() > 0)
		transaction->setDescription(description); // tool tip for action history
	if (actionPixmap)
		transaction->setPixmap(actionPixmap); // for action history
}

void UndoManager::cancelTransaction()
{
	delete transaction;
	transaction = 0;
	delete transactionTarget;
	transactionTarget = 0;
	if (!transactions.empty())
	{
		// fetch the next transaction from the vector
		transactionTarget = transactions[transactions.size() - 1].first;
		transaction = transactions[transactions.size() - 1].second;
// 		delete transactions[transactions.size() - 1];
		transactions.erase(transactions.end() - 1);
	}
}

void UndoManager::commit(const QString &targetName,
                         QPixmap *targetPixmap,
                         const QString &name,
                         const QString &description,
                         QPixmap *actionPixmap)
{
	if (!transaction || !transactionTarget || !_undoEnabled)
	{
		cancelTransaction();
		return;
	}
	if (targetName.length() > 0)
		transactionTarget->setUName(targetName);
	if (targetPixmap)
		transactionTarget->setUPixmap(targetPixmap);
	if (name.length() > 0)
		transaction->setName(name);
	if (description.length() > 0)
		transaction->setDescription(description);
	if (actionPixmap)
		transaction->setPixmap(actionPixmap);

	UndoObject *tmpu = transactionTarget;
	TransactionState *tmps = transaction;

	if (!transactions.empty())
	{
		// fetch the next transaction to be an active transaction
		transactionTarget = transactions[transactions.size() - 1].first;
		transaction = transactions[transactions.size() - 1].second;
// 		delete transactions[transactions.size() - 1]
		transactions.erase(transactions.end() - 1);
	}
	else
	{
		transaction = 0;
		transactionTarget = 0;
	}

	if (tmps->sizet() > 0) // are there any actions inside the commited transaction
	{
		if (tmps->getName().isEmpty())
			tmps->useActionName();
		action(tmpu, tmps);
	} // if not just delete objects
	else
	{
		delete tmpu;
		tmpu = 0;
		delete tmps;
		tmps = 0;
	}
}

bool UndoManager::isTransactionMode()
{
	return transaction ? true : false;
}

void UndoManager::registerGui(UndoGui* gui)
{
	if (gui == 0)
		return;

	setUndoEnabled(false);
	setState(gui);
	undoGuis.push_back(gui);
	setUndoEnabled(true);
}

void UndoManager::setState(UndoGui* gui, int uid)
{
	gui->clear();

	ActionList::iterator itstart = stacks[currentDoc].second.begin() - 1;
	ActionList::iterator itend   = stacks[currentDoc].second.end() - 1;

	if ( stacks[currentDoc].second.size() == 0 )
		return;

	if (uid > -1)
	{ // find the range from where actions are added when in obj. spec. mode
		ActionList::iterator it2;
		for (it2 = stacks[currentDoc].first - 1; it2 != stacks[currentDoc].second.begin() - 1; --it2)
		{
			UndoState*  tmp  = (*it2).second;
			TransactionState *ts = dynamic_cast<TransactionState*>(tmp);
			if (ts && ts->contains(uid))
			{
				itstart = it2 - 1;
				break;
			}
		}
		for (it2 = stacks[currentDoc].first; it2 != stacks[currentDoc].second.end(); ++it2)
		{
			UndoState*  tmp  = (*it2).second;
			TransactionState *ts = dynamic_cast<TransactionState*>(tmp);
			if (ts && ts->contains(uid))
			{
				itend = it2 - 1;
				break;
			}
		}
	}

	for (; itend > itstart; --itend)
	{
		ActionPair pair = *itend;
		UndoObject* target = pair.first;
		UndoState*  state  = pair.second;

		if (uid == -1 || target->getUId() == static_cast<uint>(uid))
		{
			if (itend > stacks[currentDoc].first - 1)
				gui->insertUndoItem(target, state);
			else
				gui->insertRedoItem(target, state);
		}
	}
}

void UndoManager::connectGuis()
{
	for (uint i = 0; i < undoGuis.size(); ++i)
	{
		UndoGui *gui = undoGuis[i];

		connect(gui, SIGNAL(undo(int)), this, SLOT(doUndo(int)));
		connect(gui, SIGNAL(redo(int)), this, SLOT(doRedo(int)));
		connect(this, SIGNAL(newAction(UndoObject*, UndoState*)),
                gui, SLOT(insertUndoItem(UndoObject*, UndoState*)));
		connect(this, SIGNAL(popBack()), gui, SLOT(popBack()));
		connect(this, SIGNAL(undoSignal(int)), gui, SLOT(updateUndo(int)));
		connect(this, SIGNAL(redoSignal(int)), gui, SLOT(updateRedo(int)));
		connect(this, SIGNAL(clearRedo()), gui, SLOT(clearRedo()));
		gui->setEnabled(true);
		gui->updateUndoActions();
	}
}

void UndoManager::disconnectGuis()
{
	for (uint i = 0; i < undoGuis.size(); ++i)
	{
		UndoGui *gui = undoGuis[i];

		disconnect(gui, SIGNAL(undo(int)), this, SLOT(doUndo(int)));
		disconnect(gui, SIGNAL(redo(int)), this, SLOT(doRedo(int)));
		disconnect(this, SIGNAL(newAction(UndoObject*, UndoState*)),
                   gui, SLOT(insertUndoItem(UndoObject*, UndoState*)));
		disconnect(this, SIGNAL(popBack()), gui, SLOT(popBack()));
		disconnect(this, SIGNAL(undoSignal(int)), gui, SLOT(updateUndo(int)));
		disconnect(this, SIGNAL(redoSignal(int)), gui, SLOT(updateRedo(int)));
		disconnect(this, SIGNAL(clearRedo()), gui, SLOT(clearRedo()));
		gui->setEnabled(false);
	}
}

void UndoManager::removeGui(UndoGui* gui)
{
	std::vector<UndoGui*>::iterator it;
	for (it = undoGuis.begin(); it != undoGuis.end(); ++it)
		if (*it == gui)
			undoGuis.erase(it);
}

void UndoManager::switchStack(const QString& stackName)
{
	if (stackName == currentDoc)
		return; //already current stack
	currentDoc = stackName;
	if (!stacks.contains(currentDoc))
	{
		ActionList alist;
		ActionList::iterator it = alist.begin();
		stacks[currentDoc] = StackPair(it, alist);
	}

	for (uint i = 0; i < undoGuis.size(); ++i)
		setState(undoGuis[i]);
}

void UndoManager::renameStack(const QString& newName)
{
	if (currentDoc == newName)
		return;

	if (stacks[currentDoc].second.empty()) {
		currentDoc = newName;
		return;
	}
	
	ActionPair pair = *(stacks[currentDoc].first);
	StackPair tmp(stacks[currentDoc].first, stacks[currentDoc].second);
	stacks.erase(currentDoc);
	stacks[newName] = tmp;
	currentDoc = newName;
	ActionList::iterator it;
	for (it = stacks[currentDoc].second.begin(); it != stacks[currentDoc].second.end(); ++it)
	{
		if ((*it) == pair)
			break;
	}
	stacks[currentDoc].first = it;
}

void UndoManager::removeStack(const QString& stackName)
{
	if (stacks.contains(stackName))
	{
		for (uint i = 0; i < stacks[stackName].second.size(); ++i)
		{
			delete stacks[stackName].second[i].second;
			stacks[stackName].second[i].second = 0;
		}
		stacks.erase(stackName);
		if (currentDoc == stackName)
		{
			for (uint i = 0; i < undoGuis.size(); ++i)
				undoGuis[i]->clear();
			currentDoc = "__no_name__";
		}
	}
}

void UndoManager::action(UndoObject* target, UndoState* state, QPixmap *targetPixmap)
{
	QPixmap *oldIcon = 0;
	if (targetPixmap)
	{
		oldIcon = target->getUPixmap();
		target->setUPixmap(targetPixmap);
	}

	if (!_undoEnabled) // if so flush down the state
	{
		TransactionState *ts = dynamic_cast<TransactionState*>(state);
		if (ts) // flush the TransactionObject too
			delete target;
		delete state;
		return;
	}

	if ((!transaction) &&
        (currentUndoObjectId == -1 || currentUndoObjectId == static_cast<long>(target->getUId())))
		emit newAction(target, state); // send action to the guis
	else
		emit clearRedo();

	if (stacks[currentDoc].second.size() > 1 && !transaction) // delete redo states
	{
		ActionList::iterator it;
		for (it = stacks[currentDoc].second.begin(); it != stacks[currentDoc].first; ++it)
		{
			TransactionState *ts = dynamic_cast<TransactionState*>((*it).second);
			if (ts)
			{
				delete (*it).first; // delete TransactionObject
				(*it).first = 0;
			}
			delete (*it).second;
			(*it).second = 0;
		}
		stacks[currentDoc].second.erase(stacks[currentDoc].second.begin(), stacks[currentDoc].first);
	}

	if (transaction)
		transaction->pushBack(target, state);
	else
	{
		checkStackLength(); // check that history length
		stacks[currentDoc].second.insert(stacks[currentDoc].second.begin(), ActionPair(target, state));
		stacks[currentDoc].first = stacks[currentDoc].second.begin();
	}
	if (targetPixmap)
		target->setUPixmap(oldIcon);
}

void UndoManager::action(UndoObject* target, UndoState* state,
                         const QString &targetName, QPixmap *targetPixmap)
{
	QString oldName = target->getUName();
	if (!targetName.isEmpty())
		target->setUName(targetName);
	action(target, state, targetPixmap);
	target->setUName(oldName);
}

void UndoManager::undo(int steps)
{
	if (_undoEnabled)
		doUndo(steps);
}

void UndoManager::redo(int steps)
{
	if (_undoEnabled)
		doRedo(steps);
}

void UndoManager::doUndo(int steps)
{
	if (steps > 0 && _undoEnabled && stacks.size() > 0)
	{
		setUndoEnabled(false);
		UndoState* tmpUndoState = 0;
		currentAction = stacks[currentDoc].first;
		for (int i = 0; i < steps; ++i)
		{
			ActionPair &aPair = getNextUndoPair();
			UndoObject* tmpUndoObject = aPair.first;
			tmpUndoState = aPair.second;
			TransactionState *ts = dynamic_cast<TransactionState*>(tmpUndoState);

			if (tmpUndoObject && tmpUndoState && !ts)
				tmpUndoObject->restore(tmpUndoState, true);
			else if (tmpUndoState && ts)
				doTransactionUndo(ts);
			ts = 0;
		}
		reorderUndoStack(steps);
		setUndoEnabled(true);
		if (tmpUndoState)
		{
			emit undoSignal(steps);
			emit undoRedoDone();
		}
	}
}

ActionPair& UndoManager::getNextUndoPair()
{
	if (currentUndoObjectId == GLOBAL_UNDO_MODE)
		++currentAction;
	else
		for (; currentAction != stacks[currentDoc].second.end(); ++currentAction)
			if ((*currentAction).first->getUId() == static_cast<uint>(currentUndoObjectId))
			{
				++currentAction;
				break;
			}
	return *(currentAction - 1);
}

void UndoManager::reorderUndoStack(int steps)
{
	if (currentUndoObjectId == GLOBAL_UNDO_MODE)
		stacks[currentDoc].first += steps;
	else
	{
		int pos = -1;
		ActionList::iterator it;
		for (it = stacks[currentDoc].second.begin(); it != stacks[currentDoc].first + 1; ++it)
		{
			++pos;
		}

		// TODO Move the items around here
		for (int i = 0; i < steps; ++i)
		{
			ActionList::iterator it = stacks[currentDoc].second.begin() + pos;
			while ((*it).first->getUId() != static_cast<uint>(currentUndoObjectId)) ++it;
			ActionPair pair = *it;
			stacks[currentDoc].second.erase(it);
			stacks[currentDoc].second.insert(stacks[currentDoc].second.begin() + pos, pair);
			++pos;
		}

		stacks[currentDoc].first = stacks[currentDoc].second.begin() += pos;
	}
}

void UndoManager::doTransactionUndo(TransactionState *tstate)
{
	for (int i = tstate->sizet() - 1; i > -1; --i)
	{
		ActionPair *pair = tstate->at(i);
		TransactionState *ts = dynamic_cast<TransactionState*>(pair->second);
		if (pair && pair->first && pair->second && !ts)
			pair->first->restore(pair->second, true);
		else if (pair && pair->second && ts)
			doTransactionUndo(ts);
	}
}

void UndoManager::doRedo(int steps)
{
	if (steps > 0 && _undoEnabled && stacks.size() > 0)
	{
		setUndoEnabled(false);
		UndoState* tmpUndoState = 0;
		currentAction = stacks[currentDoc].first;
		for (int i = 0; i < steps; ++i) // TODO compare to stack size too
		{
			ActionPair &aPair = getNextRedoPair();
			UndoObject* tmpUndoObject = aPair.first;
			tmpUndoState = aPair.second;
			TransactionState *ts = dynamic_cast<TransactionState*>(tmpUndoState);

			if (tmpUndoObject && tmpUndoState && !ts)
				tmpUndoObject->restore(tmpUndoState, false);
			else if (tmpUndoState && ts)
				doTransactionRedo(ts);
			ts = 0;
		}

		reorderRedoStack(steps);
		setUndoEnabled(true);

		if (tmpUndoState)
		{
			emit redoSignal(steps);
			emit undoRedoDone();
		}
	}
}

ActionPair& UndoManager::getNextRedoPair()
{
	if (currentUndoObjectId == GLOBAL_UNDO_MODE)
		--currentAction;
	else
		for (--currentAction; currentAction != stacks[currentDoc].second.begin() - 1; --currentAction)
			if ((*currentAction).first->getUId() == static_cast<uint>(currentUndoObjectId))
				break;

	return *currentAction;
}

void UndoManager::reorderRedoStack(int steps)
{
	if (currentUndoObjectId == GLOBAL_UNDO_MODE)
		stacks[currentDoc].first -= steps;
	else
	{
		int pos = -1;
		ActionList::iterator it;
		for (it = stacks[currentDoc].second.begin(); it != stacks[currentDoc].first + 1; ++it)
			++pos;
		for (int i = 0; i < steps; ++i)
		{
			ActionList::iterator it = stacks[currentDoc].second.begin() + pos - 1;
			while ((*it).first->getUId() != static_cast<uint>(currentUndoObjectId)) --it;
			ActionPair pair = *it;
			stacks[currentDoc].second.erase(it);
			--pos;
			stacks[currentDoc].second.insert(stacks[currentDoc].second.begin() + pos, pair);
		}
		stacks[currentDoc].first = stacks[currentDoc].second.begin() += pos;
	}
}

void UndoManager::doTransactionRedo(TransactionState *tstate)
{
	for (uint i = 0; i < tstate->sizet(); ++i)
	{
		ActionPair *pair = tstate->at(i);
		TransactionState *ts = dynamic_cast<TransactionState*>(pair->second);
		if (pair && pair->first && pair->second && !ts)
			pair->first->restore(pair->second, false);
		else if (pair && pair->second && ts)
			doTransactionRedo(ts);
	}
}

bool UndoManager::hasUndoActions(int )
{
	// TODO Needs to fixed for object specific mode
	return stacks[currentDoc].first < stacks[currentDoc].second.end();
}

bool UndoManager::hasRedoActions(int )
{
	// TODO Needs to be fixed for object specific mode
	return stacks[currentDoc].first > stacks[currentDoc].second.begin();
}

void UndoManager::showObject(int uid)
{
	if (currentUndoObjectId == uid)
		return;
	setUndoEnabled(false);
	currentUndoObjectId = uid;
	for (uint i = 0; i < undoGuis.size(); ++i)
	{
		if (uid == -2)
			undoGuis[i]->clear();
		else
			setState(undoGuis[i], currentUndoObjectId);
	}
	setUndoEnabled(true);
}

UndoObject* UndoManager::replaceObject(ulong uid, UndoObject *newUndoObject)
{
	UndoObject *tmp = 0;
	for (uint i = 0; i < stacks[currentDoc].second.size(); ++i)
	{
		ActionPair &apair = stacks[currentDoc].second[i];
		TransactionState *ts = dynamic_cast<TransactionState*>(apair.second);
		if (ts)
			tmp = ts->replace(uid, newUndoObject);
		else if (apair.first && apair.first->getUId() == uid)
		{
			tmp = apair.first;
			apair.first = newUndoObject;
		}
	}
	if (transaction) // replace also in the currently open transaction
		tmp = transaction->replace(uid, newUndoObject);
	return tmp;
}

void UndoManager::setHistoryLength(int steps)
{
	if (steps >= 0)
	{
		historyLength = steps;
		prefs->set("historyLength", historyLength);
		checkStackLength();
	}
}

int UndoManager::getHistoryLength()
{
	if (stacks.size() > 0 && stacks[currentDoc].first != stacks[currentDoc].second.begin())
		return -1;
	return historyLength;
}

bool UndoManager::isGlobalMode()
{
	return currentUndoObjectId == -1;
}

void UndoManager::checkStackLength()
{
	if ((stacks[currentDoc].second.size() >= static_cast<uint>(historyLength)) && (historyLength != 0))
	{
		while (stacks[currentDoc].second.size() >= static_cast<uint>(historyLength))
		{
			ActionList::iterator it = stacks[currentDoc].second.end() - 1;
			if (stacks[currentDoc].first == it)
				stacks[currentDoc].first = it - 1;
			TransactionState *ts = dynamic_cast<TransactionState*>((*it).second);
			if (ts)
				delete (*it).first; // delete TransactionObject
			delete (*it).second;
			stacks[currentDoc].second.erase(it);
			emit popBack();
		}
	}
}

void UndoManager::deleteInstance()
{
	if (_instance)
		delete _instance;
	_instance = 0;
}

UndoManager::~UndoManager()
{
	StackMap::iterator it;
	for (it = stacks.begin(); it != stacks.end(); ++it)
	{
		for (uint i = 0; i < (*it).second.size(); ++i)
		{
			delete ((*it).second)[i].second;
			((*it).second)[i].second = 0;
		}
	}
}


void UndoManager::languageChange()
{
	UndoManager::AddVGuide          = tr("Add vertical guide");
	UndoManager::AddHGuide          = tr("Add horizontal guide");
	UndoManager::DelVGuide          = tr("Remove vertical guide");
	UndoManager::DelHGuide          = tr("Remove horizontal guide");
	UndoManager::MoveVGuide         = tr("Move vertical guide");
	UndoManager::MoveHGuide         = tr("Move horizontal guide");
	UndoManager::LockGuides         = tr("Lock guides");
	UndoManager::UnlockGuides       = tr("Unlock guides");
	UndoManager::Move               = tr("Move");
	UndoManager::Resize             = tr("Resize");
	UndoManager::Rotate             = tr("Rotate");
	UndoManager::MoveFromTo         = tr("X1: %1, Y1: %2, %3\nX2: %4, Y2: %5, %6");
	UndoManager::ResizeFromTo       = tr("W1: %1, H1: %2\nW2: %3, H2: %4");
	UndoManager::ImageOffset        = tr("Change Image Offset");
	UndoManager::ImageScale         = tr("Change Image Scale");
	UndoManager::ImageOffsetFromTo  = tr("X1: %1, Y1: %2\nX2: %4, Y2: %5");
	UndoManager::ImageScaleFromTo   = tr("X: %1, Y: %2\nX: %4, Y: %5");
	UndoManager::Selection          = tr("Selection");
	UndoManager::Group              = tr("Group");
	UndoManager::SelectionGroup     = tr("Selection/Group");
	UndoManager::Create             = tr("Create");
	UndoManager::CreateTo           = tr("X: %1, Y: %2\nW: %3, H: %4");
	UndoManager::AlignDistribute    = tr("Align/Distribute");
	UndoManager::ItemsInvolved      = tr("Items involved");
	UndoManager::Cancel             = tr("Cancel");
	UndoManager::SetFill            = tr("Set fill color");
	UndoManager::ColorFromTo        = tr("Color1: %1, Color2: %2");
	UndoManager::SetShade           = tr("Set fill color shade");
	UndoManager::SetLineColor       = tr("Set line color");
	UndoManager::SetLineShade       = tr("Set line color shade");
	UndoManager::FlipH              = tr("Flip horizontally");
	UndoManager::FlipV              = tr("Flip vertically");
	UndoManager::Lock               = tr("Lock");
	UndoManager::UnLock             = tr("Unlock");
	UndoManager::SizeLock           = tr("Lock size");
	UndoManager::SizeUnLock         = tr("Unlock size");
	UndoManager::EnablePrint        = tr("Enable Item Printing");
	UndoManager::DisablePrint       = tr("Disable Item Printing");
	UndoManager::Ungroup            = tr("Ungroup");
	UndoManager::Delete             = tr("Delete");
	UndoManager::Rename             = tr("Rename");
	UndoManager::FromTo             = tr("From %1\nto %2");
	UndoManager::ApplyMasterPage    = tr("Apply Master Page");
	UndoManager::Paste              = tr("Paste");
	UndoManager::Cut                = tr("Cut");
	UndoManager::Transparency       = tr("Set fill color transparency");
	UndoManager::LineTransparency   = tr("Set line color transparency");
	UndoManager::LineStyle          = tr("Set line style");
	UndoManager::LineEnd            = tr("Set the style of line end");
	UndoManager::LineJoin           = tr("Set the style of line join");
	UndoManager::LineWidth          = tr("Set line width");
	UndoManager::NoStyle            = tr("No style");
	UndoManager::CustomLineStyle    = tr("Set custom line style");
	UndoManager::NoLineStyle        = tr("Do not use custom line style");
	UndoManager::StartArrow         = tr("Set start arrow");
	UndoManager::EndArrow           = tr("Set end arrow");
	UndoManager::CreateTable        = tr("Create table");
	UndoManager::RowsCols           = tr("Rows: %1, Cols: %2");
	UndoManager::SetFont            = tr("Set font");
	UndoManager::SetFontSize        = tr("Set font size");
	UndoManager::SetFontWidth       = tr("Set font width");
	UndoManager::SetFontHeight       = tr("Set font height");
	UndoManager::SetFontFill        = tr("Set font fill color");
	UndoManager::SetFontStroke      = tr("Set font stroke color");
	UndoManager::SetFontFillShade   = tr("Set font fill color shade");
	UndoManager::SetFontStrokeShade = tr("Set font stroke color shade");
	UndoManager::SetKerning         = tr("Set kerning");
	UndoManager::SetLineSpacing     = tr("Set line spacing");
	UndoManager::SetStyle           = tr("Set paragraph style");
	UndoManager::SetLanguage        = tr("Set language");
	UndoManager::AlignText          = tr("Align text");
	UndoManager::SetFontEffect      = tr("Set font effect");
	UndoManager::ImageFrame         = tr("Image frame");
	UndoManager::TextFrame          = tr("Text frame");
	UndoManager::Polygon            = tr("Polygon");
	UndoManager::BezierCurve        = tr("Bezier curve");
	UndoManager::Polyline           = tr("Polyline");
	UndoManager::PathText           = tr("Text on a Path");
	UndoManager::ConvertTo          = tr("Convert to");
	UndoManager::ImportSVG          = tr("Import SVG image");
	UndoManager::ImportEPS          = tr("Import EPS image");
	UndoManager::ImportOOoDraw      = tr("Import OpenOffice.org Draw image");
	UndoManager::ScratchSpace       = tr("Scratch space");
	UndoManager::TextFlow           = tr("Text flows around the frame");
	UndoManager::BoundingBox        = tr("Text flows around bounding box");
	UndoManager::ContourLine        = tr("Text flows around contour line");
	UndoManager::NoTextFlow         = tr("No text flow");
	UndoManager::NoBoundingBox      = tr("No bounding box");
	UndoManager::NoContourLine      = tr("No contour line");
	UndoManager::PageNmbr           = tr("Page %1");
	UndoManager::ImageScaling       = tr("Set image scaling");
	UndoManager::FrameSize          = tr("Frame size");
	UndoManager::FreeScaling        = tr("Free scaling");
	UndoManager::KeepRatio          = tr("Keep aspect ratio");
	UndoManager::BreakRatio         = tr("Break aspect ratio");
	UndoManager::EditContourLine    = tr("Edit contour line");
	UndoManager::EditShape          = tr("Edit shape");
	UndoManager::ResetContourLine   = tr("Reset contour line");
	UndoManager::AddPage            = tr("Add page");
	UndoManager::AddPages           = tr("Add pages");
	UndoManager::DeletePage         = tr("Delete page");
	UndoManager::DeletePages        = tr("Delete pages");
	UndoManager::AddLayer           = tr("Add layer");
	UndoManager::DeleteLayer        = tr("Delete layer");
	UndoManager::RenameLayer        = tr("Rename layer");
	UndoManager::RaiseLayer         = tr("Raise layer");
	UndoManager::LowerLayer         = tr("Lower layer");
	UndoManager::SendToLayer        = tr("Send to layer");
	UndoManager::PrintLayer         = tr("Enable printing of layer");
	UndoManager::DoNotPrintLayer    = tr("Disable printing of layer");
	UndoManager::SetLayerName       = tr("Change name of the layer");
	UndoManager::GetImage           = tr("Get image");
	UndoManager::MultipleDuplicate  = tr("Multiple duplicate");
	UndoManager::EditContour        = tr("Edit contour line");
	UndoManager::ResetControlPoint  = tr("Reset control point");
	UndoManager::ResetControlPoints = tr("Reset control points");
	UndoManager::ImageEffects       = tr("Modify image effects");
}

/*** UndoManager::TransactionState *****************************************************/

UndoManager::TransactionState::TransactionState() : UndoState("")
{
	_size = 0;
}

ActionPair* UndoManager::TransactionState::at(int index)
{
	if (index >= 0 && static_cast<uint>(index) < sizet())
		return states[index];
	else
		return 0;
}

bool UndoManager::TransactionState::contains(int uid)
{
	bool ret = false;
	for (uint i = 0; i < states.size(); ++i)
	{
		if (states[i]->first->getUId() == static_cast<uint>(uid))
		{
			ret = true;
			break;
		}
	}
	return ret;
}

void UndoManager::TransactionState::pushBack(UndoObject *target, UndoState *state)
{
	if (target && state)
	{
		states.push_back(new ActionPair(target, state));
		++_size;
	}
}

uint UndoManager::TransactionState::sizet()
{
	return _size;
}

void UndoManager::TransactionState::useActionName()
{
	if (_size > 0)
	{
		setName(states[_size - 1]->second->getName());
	}
}

UndoObject* UndoManager::TransactionState::replace(ulong uid, UndoObject *newUndoObject)
{
	UndoObject *tmp = 0;
	for (uint i = 0; i < states.size(); ++i)
	{
		TransactionState *ts = dynamic_cast<TransactionState*>(states[i]->second);
		if (ts) // are we having a transaction inside a transaction
			ts->replace(uid, newUndoObject);
		else if (states[i]->first && states[i]->first->getUId() == uid)
		{
			tmp = states[i]->first;
			states[i]->first = newUndoObject;
		}
	}
	return tmp;
}

UndoManager::TransactionState::~TransactionState()
{
	for (uint i = 0; i < states.size(); ++i)
	{
		if (states[i])
		{
			if (states[i]->second)
			{
				delete states[i]->second;
				states[i]->second = 0;
			}
			delete states[i];
			states[i] = 0;
		}
	}
}

/*************************************************************************************/

void UndoManager::initIcons()
{
	QString iconDir = ScPaths::instance().iconDir();

/*** Icons for UndoObjects *******************************************/
	UndoManager::IImageFrame      = new QPixmap(iconDir + "Bild.xpm");
	UndoManager::ITextFrame       = new QPixmap(iconDir + "Text.xpm");
	UndoManager::ILine            = new QPixmap(iconDir + "Stift.xpm");
	UndoManager::IPolygon         = new QPixmap(iconDir + "spline.png");
	UndoManager::IPolyline        = new QPixmap(iconDir + "beziertool.png");
// 	UndoManager::IPathText        = new QPixmap(iconDir + "?";
	UndoManager::IGroup           = new QPixmap(iconDir + "u_group.png");
/*** Icons for actions ***********************************************/
	UndoManager::IMove            = new QPixmap(iconDir + "u_move.png");
	UndoManager::IResize          = new QPixmap(iconDir + "u_resize.png");
	UndoManager::IRotate          = new QPixmap(iconDir + "u_rotate.png");
	UndoManager::IAlignDistribute = new QPixmap(iconDir + "u_align.png");
	UndoManager::IGuides          = new QPixmap(iconDir + "u_margins.png");
	UndoManager::ILockGuides      = new QPixmap(iconDir + "u_margins_locked.png");
	UndoManager::IFill            = new QPixmap(iconDir + "u_fill.png");
	UndoManager::IShade           = new QPixmap(iconDir + "u_shade.png");
	UndoManager::IFlipH           = new QPixmap(iconDir + "u_fliph.png");
	UndoManager::IFlipV           = new QPixmap(iconDir + "u_flipv.png");
	UndoManager::ILock            = new QPixmap(iconDir + "u_lock.png");
	UndoManager::IUnLock          = new QPixmap(iconDir + "u_unlock.png");
	UndoManager::IEnablePrint     = new QPixmap(iconDir + "u_enableprint.png");
	UndoManager::IDisablePrint    = new QPixmap(iconDir + "u_disableprint.png");
	UndoManager::IDelete          = new QPixmap(iconDir + "u_delete.png");
	UndoManager::ICreate          = new QPixmap(iconDir + "u_create.png");
	UndoManager::IPaste           = new QPixmap(iconDir + "editpaste.png");
	UndoManager::ICut             = new QPixmap(iconDir + "u_cut.png");
	UndoManager::ITransparency    = new QPixmap(iconDir + "u_transp.png");
	UndoManager::ILineStyle       = new QPixmap(iconDir + "u_line.png");
	UndoManager::IArrow           = new QPixmap(iconDir + "u_arrow.png");
	UndoManager::ITable           = new QPixmap(iconDir + "frame_table.png");
	UndoManager::IFont            = new QPixmap(iconDir + "u_font.png");
	UndoManager::IImportOOoDraw   = new QPixmap(iconDir + "ooo_draw.png");
	UndoManager::ISVG             = new QPixmap(iconDir + "u_svg.png");
	UndoManager::IEPS             = new QPixmap(iconDir + "u_eps.png");
	UndoManager::IImageScaling    = new QPixmap(iconDir + "u_scale_image.png");
	UndoManager::IBorder          = new QPixmap(iconDir + "u_shape.png");
	UndoManager::IDocument        = new QPixmap(iconDir + "DateiNeu.xpm");
// 	UndoManager::ILayer           = new QPixmap(iconDir + "u_layer.png");
// 	UndoManager::ILayerAction     = new QPixmap(iconDir + "u_layer_action.png");
// 	UndoManager::IUp              = new QPixmap(iconDir + "u_up.png");
// 	UndoManager::IDown            = new QPixmap(iconDir + "u_down.png");
// 	UndoManager::IGetImage        = new QPixmap(iconDir + "u_get_image.png");
	UndoManager::IMultipleDuplicate = new QPixmap(iconDir + "u_multiple.png");
}
QString UndoManager::AddVGuide          = "";
QString UndoManager::AddHGuide          = "";
QString UndoManager::DelVGuide          = "";
QString UndoManager::DelHGuide          = "";
QString UndoManager::MoveVGuide         = "";
QString UndoManager::MoveHGuide         = "";
QString UndoManager::LockGuides         = "";
QString UndoManager::UnlockGuides       = "";
QString UndoManager::Move               = "";
QString UndoManager::Resize             = "";
QString UndoManager::Rotate             = "";
QString UndoManager::MoveFromTo         = "";
QString UndoManager::ImageOffset        = "";
QString UndoManager::ImageScale         = "";
QString UndoManager::ImageOffsetFromTo  = "";
QString UndoManager::ImageScaleFromTo   = "";
QString UndoManager::ResizeFromTo       = "";
QString UndoManager::Selection          = "";
QString UndoManager::Group              = "";
QString UndoManager::SelectionGroup     = "";
QString UndoManager::Create             = "";
QString UndoManager::CreateTo           = "";
QString UndoManager::AlignDistribute    = "";
QString UndoManager::ItemsInvolved      = "";
QString UndoManager::Cancel             = "";
QString UndoManager::SetFill            = "";
QString UndoManager::ColorFromTo        = "";
QString UndoManager::SetShade           = "";
QString UndoManager::SetLineColor       = "";
QString UndoManager::SetLineShade       = "";
QString UndoManager::FlipH              = "";
QString UndoManager::FlipV              = "";
QString UndoManager::Lock               = "";
QString UndoManager::UnLock             = "";
QString UndoManager::SizeLock           = "";
QString UndoManager::SizeUnLock         = "";
QString UndoManager::EnablePrint        = "";
QString UndoManager::DisablePrint       = "";
QString UndoManager::Ungroup            = "";
QString UndoManager::Delete             = "";
QString UndoManager::Rename             = "";
QString UndoManager::FromTo             = "";
QString UndoManager::ApplyMasterPage    = "";
QString UndoManager::Paste              = "";
QString UndoManager::Cut                = "";
QString UndoManager::Transparency       = "";
QString UndoManager::LineTransparency   = "";
QString UndoManager::LineStyle          = "";
QString UndoManager::LineEnd            = "";
QString UndoManager::LineJoin           = "";
QString UndoManager::LineWidth          = "";
QString UndoManager::NoStyle            = "";
QString UndoManager::CustomLineStyle    = "";
QString UndoManager::NoLineStyle        = "";
QString UndoManager::StartArrow         = "";
QString UndoManager::EndArrow           = "";
QString UndoManager::CreateTable        = "";
QString UndoManager::RowsCols           = "";
QString UndoManager::SetFont            = "";
QString UndoManager::SetFontSize        = "";
QString UndoManager::SetFontWidth       = "";
QString UndoManager::SetFontHeight      = "";
QString UndoManager::SetFontFill        = "";
QString UndoManager::SetFontStroke      = "";
QString UndoManager::SetFontFillShade   = "";
QString UndoManager::SetFontStrokeShade = "";
QString UndoManager::SetKerning         = "";
QString UndoManager::SetLineSpacing     = "";
QString UndoManager::SetStyle           = "";
QString UndoManager::SetLanguage        = "";
QString UndoManager::AlignText          = "";
QString UndoManager::SetFontEffect      = "";
QString UndoManager::ImageFrame         = "";
QString UndoManager::TextFrame          = "";
QString UndoManager::Polygon            = "";
QString UndoManager::BezierCurve        = "";
QString UndoManager::Polyline           = "";
QString UndoManager::PathText           = "";
QString UndoManager::ConvertTo          = "";
QString UndoManager::ImportSVG          = "";
QString UndoManager::ImportEPS          = "";
QString UndoManager::ImportOOoDraw      = "";
QString UndoManager::ScratchSpace       = "";
QString UndoManager::TextFlow           = "";
QString UndoManager::BoundingBox        = "";
QString UndoManager::ContourLine        = "";
QString UndoManager::NoTextFlow         = "";
QString UndoManager::NoBoundingBox      = "";
QString UndoManager::NoContourLine      = "";
QString UndoManager::PageNmbr           = "";
QString UndoManager::ImageScaling       = "";
QString UndoManager::FrameSize          = "";
QString UndoManager::FreeScaling        = "";
QString UndoManager::KeepRatio          = "";
QString UndoManager::BreakRatio         = "";
QString UndoManager::EditContourLine    = "";
QString UndoManager::EditShape          = "";
QString UndoManager::ResetContourLine   = "";
QString UndoManager::AddPage            = "";
QString UndoManager::AddPages           = "";
QString UndoManager::DeletePage         = "";
QString UndoManager::DeletePages        = "";
QString UndoManager::AddLayer           = "";
QString UndoManager::DeleteLayer        = "";
QString UndoManager::RenameLayer        = "";
QString UndoManager::RaiseLayer         = "";
QString UndoManager::LowerLayer         = "";
QString UndoManager::SendToLayer        = "";
QString UndoManager::PrintLayer         = "";
QString UndoManager::DoNotPrintLayer    = "";
QString UndoManager::SetLayerName       = "";
QString UndoManager::GetImage           = "";
QString UndoManager::MultipleDuplicate  = "";
QString UndoManager::EditContour        = "";
QString UndoManager::ResetControlPoint  = "";
QString UndoManager::ResetControlPoints = "";
QString UndoManager::ImageEffects       = "";

/*** Icons for UndoObjects *******************************************/
QPixmap *UndoManager::IImageFrame      = 0;
QPixmap *UndoManager::ITextFrame       = 0;
QPixmap *UndoManager::ILine            = 0;
QPixmap *UndoManager::IPolygon         = 0;
QPixmap *UndoManager::IPolyline        = 0;
QPixmap *UndoManager::IPathText        = 0;
QPixmap *UndoManager::IGroup           = 0;
QPixmap *UndoManager::ITable           = 0;
/*** Icons for actions ***********************************************/
QPixmap *UndoManager::IMove            = 0;
QPixmap *UndoManager::IResize          = 0;
QPixmap *UndoManager::IRotate          = 0;
QPixmap *UndoManager::IGuides          = 0;
QPixmap *UndoManager::ILockGuides      = 0;
QPixmap *UndoManager::IAlignDistribute = 0;
QPixmap *UndoManager::IFill            = 0;
QPixmap *UndoManager::IShade           = 0;
QPixmap *UndoManager::IFlipH           = 0;
QPixmap *UndoManager::IFlipV           = 0;
QPixmap *UndoManager::ILock            = 0;
QPixmap *UndoManager::IUnLock          = 0;
QPixmap *UndoManager::IEnablePrint     = 0;
QPixmap *UndoManager::IDisablePrint    = 0;
QPixmap *UndoManager::IDelete          = 0;
QPixmap *UndoManager::ICreate          = 0;
QPixmap *UndoManager::IPaste           = 0;
QPixmap *UndoManager::ICut             = 0;
QPixmap *UndoManager::ITransparency    = 0;
QPixmap *UndoManager::ILineStyle       = 0;
QPixmap *UndoManager::IArrow           = 0;
QPixmap *UndoManager::IFont            = 0;
QPixmap *UndoManager::ISVG             = 0;
QPixmap *UndoManager::IEPS             = 0;
QPixmap *UndoManager::IImportOOoDraw   = 0;
QPixmap *UndoManager::IImageScaling    = 0;
QPixmap *UndoManager::IBorder          = 0;
QPixmap *UndoManager::IDocument        = 0;
QPixmap *UndoManager::ILayer           = 0;
QPixmap *UndoManager::ILayerAction     = 0;
QPixmap *UndoManager::IUp              = 0;
QPixmap *UndoManager::IDown            = 0;
QPixmap *UndoManager::IPrint           = 0;
QPixmap *UndoManager::IGetImage        = 0;
QPixmap *UndoManager::IMultipleDuplicate = 0;

