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.
dolphin/src/undomanager.cpp

403 lines
13 KiB

/***************************************************************************
* Copyright (C) 2006 by Peter Penz *
* peter.penz@gmx.at *
* *
* 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., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
***************************************************************************/
#include "undomanager.h"
#include <tdelocale.h>
#include <tdeio/netaccess.h>
#include <tqtimer.h>
#include <assert.h>
#include "dolphin.h"
#include "dolphinstatusbar.h"
#include "progressindicator.h"
DolphinCommand::DolphinCommand() :
m_type(Copy),
m_macroIndex(-1)
{
// Implementation note: DolphinCommands are stored in a TQValueList, whereas
// TQValueList requires a default constructor of the added class.
// Instead of expressing this implementation detail to the interface by adding a
// Type::Undefined just Type::Copy is used to assure that all members have
// a defined state.
}
DolphinCommand::DolphinCommand(Type type,
const KURL::List& source,
const KURL& dest) :
m_type(type),
m_macroIndex(-1),
m_source(source),
m_dest(dest)
{
}
DolphinCommand::~DolphinCommand()
{
}
DolphinCommand& DolphinCommand::operator = (const DolphinCommand& command)
{
m_type = command.m_type;
m_source = command.m_source;
m_dest = command.m_dest;
return *this;
}
UndoManager& UndoManager::instance()
{
static UndoManager* instance = 0;
if (instance == 0) {
instance = new UndoManager();
}
return *instance;
}
void UndoManager::addCommand(const DolphinCommand& command)
{
++m_historyIndex;
if (m_recordMacro) {
DolphinCommand macroCommand = command;
macroCommand.m_macroIndex = m_macroCounter;
m_history.insert(m_history.at(m_historyIndex), macroCommand);
}
else {
m_history.insert(m_history.at(m_historyIndex), command);
}
emit undoAvailable(true);
emit undoTextChanged(i18n("Undo: %1").arg(commandText(command)));
// prevent an endless growing of the Undo history
if (m_historyIndex > 10000) {
m_history.erase(m_history.begin());
--m_historyIndex;
}
}
void UndoManager::beginMacro()
{
assert(!m_recordMacro);
m_recordMacro = true;
++m_macroCounter;
}
void UndoManager::endMacro()
{
assert(m_recordMacro);
m_recordMacro = false;
}
void UndoManager::undo()
{
if (m_recordMacro) {
endMacro();
}
if (m_historyIndex < 0) {
return;
}
int progressCount = 0;
int macroCount = 1;
calcStepsCount(macroCount, progressCount);
m_progressIndicator = new ProgressIndicator(i18n("Executing undo operation..."),
i18n("Executed undo operation."),
progressCount);
for (int i = 0; i < macroCount; ++i) {
const DolphinCommand command = m_history[m_historyIndex];
--m_historyIndex;
if (m_historyIndex < 0) {
emit undoAvailable(false);
emit undoTextChanged(i18n("Undo"));
}
else {
emit undoTextChanged(i18n("Undo: %1").arg(commandText(m_history[m_historyIndex])));
}
if (m_historyIndex < static_cast<int>(m_history.count()) - 1) {
emit redoAvailable(true);
emit redoTextChanged(i18n("Redo: %1").arg(commandText(command)));
}
else {
emit redoAvailable(false);
emit redoTextChanged(i18n("Redo"));
}
KURL::List sourceURLs = command.source();
KURL::List::Iterator it = sourceURLs.begin();
const KURL::List::Iterator end = sourceURLs.end();
const TQString destURL(command.destination().prettyURL(+1));
TDEIO::Job* job = 0;
switch (command.type()) {
case DolphinCommand::Link:
case DolphinCommand::Copy: {
KURL::List list;
while (it != end) {
const KURL deleteURL(destURL + (*it).filename());
list.append(deleteURL);
++it;
}
job = TDEIO::del(list, false, false);
break;
}
case DolphinCommand::Move: {
KURL::List list;
const KURL newDestURL((*it).directory());
while (it != end) {
const KURL newSourceURL(destURL + (*it).filename());
list.append(newSourceURL);
++it;
}
job = TDEIO::move(list, newDestURL, false);
break;
}
case DolphinCommand::Rename: {
assert(sourceURLs.count() == 1);
TDEIO::NetAccess::move(command.destination(), (*it));
break;
}
case DolphinCommand::Trash: {
while (it != end) {
// TODO: use TDEIO::special for accessing the trash protocol. See
// also Dolphin::slotJobResult() for further details.
const TQString originalFileName((*it).filename().section('-', 1));
KURL newDestURL(destURL + originalFileName);
TDEIO::NetAccess::move(*it, newDestURL);
++it;
m_progressIndicator->execOperation();
}
break;
}
case DolphinCommand::CreateFolder:
case DolphinCommand::CreateFile: {
TDEIO::NetAccess::del(command.destination(), &Dolphin::mainWin());
break;
}
}
if (job != 0) {
// Execute the jobs in a synchronous manner and forward the progress
// information to the Dolphin statusbar.
connect(job, TQ_SIGNAL(percent(TDEIO::Job*, unsigned long)),
this, TQ_SLOT(slotPercent(TDEIO::Job*, unsigned long)));
TDEIO::NetAccess::synchronousRun(job, &Dolphin::mainWin());
}
m_progressIndicator->execOperation();
}
delete m_progressIndicator;
m_progressIndicator = 0;
}
void UndoManager::redo()
{
if (m_recordMacro) {
endMacro();
}
const int maxHistoryIndex = m_history.count() - 1;
if (m_historyIndex >= maxHistoryIndex) {
return;
}
++m_historyIndex;
int progressCount = 0;
int macroCount = 1;
calcStepsCount(macroCount, progressCount);
m_progressIndicator = new ProgressIndicator(i18n("Executing redo operation..."),
i18n("Executed redo operation."),
progressCount);
for (int i = 0; i < macroCount; ++i) {
const DolphinCommand command = m_history[m_historyIndex];
if (m_historyIndex >= maxHistoryIndex) {
emit redoAvailable(false);
emit redoTextChanged(i18n("Redo"));
}
else {
emit redoTextChanged(i18n("Redo: %1").arg(commandText(m_history[m_historyIndex + 1])));
}
emit undoAvailable(true);
emit undoTextChanged(i18n("Undo: %1").arg(commandText(command)));
Dolphin& dolphin = Dolphin::mainWin();
KURL::List sourceURLs = command.source();
KURL::List::Iterator it = sourceURLs.begin();
const KURL::List::Iterator end = sourceURLs.end();
TDEIO::Job* job = 0;
switch (command.type()) {
case DolphinCommand::Link: {
job = TDEIO::link(sourceURLs, command.destination(), false);
break;
}
case DolphinCommand::Copy: {
job = TDEIO::copy(sourceURLs, command.destination(), false);
break;
}
case DolphinCommand::Rename:
case DolphinCommand::Move: {
job = TDEIO::move(sourceURLs, command.destination(), false);
break;
}
case DolphinCommand::Trash: {
const TQString destURL(command.destination().prettyURL());
while (it != end) {
// TODO: use TDEIO::special for accessing the trash protocol. See
// also Dolphin::slotJobResult() for further details.
const TQString originalFileName((*it).filename().section('-', 1));
KURL originalSourceURL(destURL + "/" + originalFileName);
TDEIO::Job* moveToTrashJob = TDEIO::trash(originalSourceURL);
TDEIO::NetAccess::synchronousRun(moveToTrashJob, &dolphin);
++it;
m_progressIndicator->execOperation();
}
break;
}
case DolphinCommand::CreateFolder: {
TDEIO::NetAccess::mkdir(command.destination(), &dolphin);
break;
}
case DolphinCommand::CreateFile: {
m_progressIndicator->execOperation();
KURL::List::Iterator it = sourceURLs.begin();
assert(sourceURLs.count() == 1);
TDEIO::CopyJob* copyJob = TDEIO::copyAs(*it, command.destination(), false);
copyJob->setDefaultPermissions(true);
job = copyJob;
break;
}
}
if (job != 0) {
// Execute the jobs in a synchronous manner and forward the progress
// information to the Dolphin statusbar.
connect(job, TQ_SIGNAL(percent(TDEIO::Job*, unsigned long)),
this, TQ_SLOT(slotPercent(TDEIO::Job*, unsigned long)));
TDEIO::NetAccess::synchronousRun(job, &dolphin);
}
++m_historyIndex;
m_progressIndicator->execOperation();
}
--m_historyIndex;
delete m_progressIndicator;
m_progressIndicator = 0;
}
UndoManager::UndoManager() :
m_recordMacro(false),
m_historyIndex(-1),
m_macroCounter(0),
m_progressIndicator(0)
{
}
UndoManager::~UndoManager()
{
delete m_progressIndicator;
m_progressIndicator = 0;
}
TQString UndoManager::commandText(const DolphinCommand& command) const
{
TQString text;
switch (command.type()) {
case DolphinCommand::Copy: text = i18n("Copy"); break;
case DolphinCommand::Move: text = i18n("Move"); break;
case DolphinCommand::Link: text = i18n("Link"); break;
case DolphinCommand::Rename: text = i18n("Rename"); break;
case DolphinCommand::Trash: text = i18n("Move to Trash"); break;
case DolphinCommand::CreateFolder: text = i18n("Create New Folder"); break;
case DolphinCommand::CreateFile: text = i18n("Create New File"); break;
default: break;
}
return text;
}
void UndoManager::slotPercent(TDEIO::Job* /* job */, unsigned long /* percent */)
{
// It is not allowed to update the progress indicator in the context
// of this slot, hence do an asynchronous triggering.
TQTimer::singleShot(0, this, TQ_SLOT(updateProgress()));
}
void UndoManager::updateProgress()
{
m_progressIndicator->execOperation();
}
void UndoManager::calcStepsCount(int& macroCount, int& progressCount)
{
progressCount = 0;
macroCount = 0;
const int macroIndex = m_history[m_historyIndex].m_macroIndex;
if (macroIndex < 0) {
// default use case: no macro has been recorded
macroCount = 1;
progressCount = m_history[m_historyIndex].source().count();
return;
}
// iterate backward for undo...
int i = m_historyIndex;
while ((i >= 0) && (m_history[i].m_macroIndex == macroIndex)) {
++macroCount;
progressCount += m_history[i].source().count();
--i;
}
// iterate forward for redo...
const int max = m_history.count() - 1;
i = m_historyIndex + 1;
while ((i <= max) && (m_history[i].m_macroIndex == macroIndex)) {
++macroCount;
progressCount += m_history[i].source().count();
++i;
}
}
#include "undomanager.moc"