|
|
|
/***************************************************************************
|
|
|
|
* 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, TQT_SIGNAL(percent(TDEIO::Job*, unsigned long)),
|
|
|
|
this, TQT_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, TQT_SIGNAL(percent(TDEIO::Job*, unsigned long)),
|
|
|
|
this, TQT_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, TQT_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"
|
|
|
|
|
|
|
|
|