Files
GoldenCheetah/src/RideFileCommand.cpp
Mark Liversedge 0f4d00dbda Code Cleanup: RideFileCommand
.. fix small memory leak on command stack.
2013-02-11 14:36:05 +00:00

410 lines
10 KiB
C++

/*
* Copyright (c) 2010 Mark Liversedge (liversedge@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., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "RideFile.h"
#include "RideFileCommand.h"
#include "RideEditor.h"
#include <math.h>
#include <float.h>
// comparing doubles is nasty
static bool doubles_equal(double a, double b)
{
double errorB = b * DBL_EPSILON;
return (a >= b - errorB) && (a <= b + errorB);
}
//----------------------------------------------------------------------
// The public interface to the commands
//----------------------------------------------------------------------
RideFileCommand::RideFileCommand(RideFile *ride) : ride(ride), stackptr(0), inLUW(false), luw(NULL)
{
connect(ride, SIGNAL(saved()), this, SLOT(clearHistory()));
connect(ride, SIGNAL(reverted()), this, SLOT(clearHistory()));
}
RideFileCommand::~RideFileCommand()
{
clearHistory();
}
void
RideFileCommand::setPointValue(int index, RideFile::SeriesType series, double value)
{
SetPointValueCommand *cmd = new SetPointValueCommand(ride, index, series,
ride->getPointValue(index, series), value);
doCommand(cmd);
}
void
RideFileCommand::deletePoint(int index)
{
RideFilePoint current = *ride->dataPoints()[index];
DeletePointCommand *cmd = new DeletePointCommand(ride, index, current);
doCommand(cmd);
}
void
RideFileCommand::deletePoints(int index, int count)
{
QVector<RideFilePoint> current;
for(int i=0; i< count; i++) {
RideFilePoint point = *ride->dataPoints()[index+i];
current.append(point);
}
DeletePointsCommand *cmd = new DeletePointsCommand(ride, index, count, current);
doCommand(cmd);
}
void
RideFileCommand::insertPoint(int index, RideFilePoint *points)
{
InsertPointCommand *cmd = new InsertPointCommand(ride, index, points);
doCommand(cmd);
}
void
RideFileCommand::appendPoints(QVector <RideFilePoint> newRows)
{
AppendPointsCommand *cmd = new AppendPointsCommand(ride, ride->dataPoints().count(), newRows);
doCommand(cmd);
}
void
RideFileCommand::setDataPresent(RideFile::SeriesType type, bool newvalue)
{
bool oldvalue = ride->isDataPresent(type);
SetDataPresentCommand *cmd = new SetDataPresentCommand(ride, type, newvalue, oldvalue);
doCommand(cmd);
}
QString
RideFileCommand::changeLog()
{
QString log;
for (int i=0; i<stackptr; i++) {
if (stack[i]->type != RideCommand::NoOp)
log += stack[i]->description + '\n';
}
return log;
}
//----------------------------------------------------------------------
// Manage the Command Stack
//----------------------------------------------------------------------
void
RideFileCommand::clearHistory()
{
foreach (RideCommand *cmd, stack) delete cmd;
stack.clear();
stackptr = 0;
}
void
RideFileCommand::startLUW(QString name)
{
luw = new LUWCommand(this, name, ride);
inLUW = true;
beginCommand(false, luw);
}
void
RideFileCommand::endLUW()
{
if (inLUW == false) return; // huh?
inLUW = false;
// add to the stack if it isn't empty
if (luw->worklist.count()) doCommand(luw, true);
}
void
RideFileCommand::doCommand(RideCommand *cmd, bool noexec)
{
// we must add to the LUW, but also must
// execute immediately since state data
// is collected by each command as it is
// created.
if (inLUW) {
luw->addCommand(cmd);
beginCommand(false, cmd);
cmd->doCommand(); // luw must be executed as added!!!
cmd->docount++;
endCommand(false, cmd);
return;
}
// place onto stack
if (stack.count()) {
// wipe away commands we can no longer redo
for (int i=stackptr; i<stack.count(); i++) delete stack.at(i);
stack.remove(stackptr, stack.count() - stackptr);
}
stack.append(cmd);
stackptr++;
if (noexec == false) {
beginCommand(false, cmd); // signal
cmd->doCommand(); // execute
}
cmd->docount++;
endCommand(false, cmd); // signal - even if LUW
// we changed it!
ride->emitModified();
}
void
RideFileCommand::redoCommand()
{
if (stackptr < stack.count()) {
beginCommand(false, stack[stackptr]); // signal
stack[stackptr]->doCommand();
stack[stackptr]->docount++;
stackptr++; // increment before end to keep in sync in case
// it is queried 'after' the command is executed
// i.e. within a slot connected to this signal
endCommand(false, stack[stackptr-1]); // signal
}
}
void
RideFileCommand::undoCommand()
{
if (stackptr > 0) {
stackptr--;
beginCommand(true, stack[stackptr]); // signal
stack[stackptr]->undoCommand();
endCommand(true, stack[stackptr]); // signal
}
}
int
RideFileCommand::undoCount()
{
return stackptr;
}
int
RideFileCommand::redoCount()
{
return stack.count() - stackptr;
}
void
RideFileCommand::emitBeginCommand(bool undo, RideCommand *cmd)
{
emit beginCommand(undo, cmd);
}
void
RideFileCommand::emitEndCommand(bool undo, RideCommand *cmd)
{
emit endCommand(undo, cmd);
}
//----------------------------------------------------------------------
// Commands...
//----------------------------------------------------------------------
// Logical Unit of work
LUWCommand::LUWCommand(RideFileCommand *commander, QString name, RideFile *ride) :
RideCommand(ride), commander(commander)
{
type = RideCommand::LUW;
description = name;
}
LUWCommand::~LUWCommand()
{
foreach(RideCommand *cmd, worklist) delete cmd;
}
bool
LUWCommand::doCommand()
{
foreach(RideCommand *cmd, worklist) {
commander->emitBeginCommand(false, cmd);
cmd->doCommand();
commander->emitEndCommand(false, cmd);
}
return true;
}
bool
LUWCommand::undoCommand()
{
for (int i=worklist.count(); i > 0; i--) {
RideCommand *cmd = worklist[i-1];
commander->emitBeginCommand(true, cmd);
cmd->undoCommand();
commander->emitEndCommand(true, cmd);
}
return true;
}
// Set point value
SetPointValueCommand::SetPointValueCommand(RideFile *ride, int row,
RideFile::SeriesType series, double oldvalue, double newvalue) :
RideCommand(ride), // base class looks after these
row(row), series(series), oldvalue(oldvalue), newvalue(newvalue)
{
type = RideCommand::SetPointValue;
description = tr("Set Value");
}
bool
SetPointValueCommand::doCommand()
{
// check it has changed first!
if (!doubles_equal(oldvalue, newvalue)) {
ride->setPointValue(row,series,newvalue);
}
return true;
}
bool
SetPointValueCommand::undoCommand()
{
if (!doubles_equal(oldvalue, newvalue)) {
ride->setPointValue(row,series,oldvalue);
}
return true;
}
// Remove a point
DeletePointCommand::DeletePointCommand(RideFile *ride, int row, RideFilePoint point) :
RideCommand(ride), // base class looks after these
row(row), point(point)
{
type = RideCommand::DeletePoint;
description = tr("Remove Point");
}
bool
DeletePointCommand::doCommand()
{
ride->deletePoint(row);
return true;
}
bool
DeletePointCommand::undoCommand()
{
ride->insertPoint(row, new RideFilePoint(point));
return true;
}
// Remove points
DeletePointsCommand::DeletePointsCommand(RideFile *ride, int row, int count,
QVector<RideFilePoint> current) :
RideCommand(ride), // base class looks after these
row(row), count(count), points(current)
{
type = RideCommand::DeletePoints;
description = tr("Remove Points");
}
bool
DeletePointsCommand::doCommand()
{
ride->deletePoints(row, count);
return true;
}
bool
DeletePointsCommand::undoCommand()
{
for (int i=(count-1); i>=0; i--) ride->insertPoint(row, new RideFilePoint(points[i]));
return true;
}
// Insert a point
InsertPointCommand::InsertPointCommand(RideFile *ride, int row, RideFilePoint *point) :
RideCommand(ride), // base class looks after these
row(row), point(*point)
{
type = RideCommand::InsertPoint;
description = tr("Insert Point");
}
bool
InsertPointCommand::doCommand()
{
ride->insertPoint(row, new RideFilePoint(point));
return true;
}
bool
InsertPointCommand::undoCommand()
{
ride->deletePoint(row);
return true;
}
// Append points
AppendPointsCommand::AppendPointsCommand(RideFile *ride, int row, QVector<RideFilePoint> points) :
RideCommand(ride), // base class looks after these
row(row), count (points.count()), points(points)
{
type = RideCommand::AppendPoints;
description = tr("Append Points");
}
bool
AppendPointsCommand::doCommand()
{
QVector<RideFilePoint *> newPoints;
foreach (RideFilePoint point, points) {
RideFilePoint *p = new RideFilePoint(point);
newPoints.append(p);
}
ride->appendPoints(newPoints);
return true;
}
bool
AppendPointsCommand::undoCommand()
{
for (int i=0; i<count; i++) ride->deletePoint(row);
return true;
}
SetDataPresentCommand::SetDataPresentCommand(RideFile *ride,
RideFile::SeriesType series, bool newvalue, bool oldvalue) :
RideCommand(ride), // base class looks after these
series(series), oldvalue(oldvalue), newvalue(newvalue)
{
type = RideCommand::SetDataPresent;
description = tr("Set Data Present");
}
bool
SetDataPresentCommand::doCommand()
{
ride->setDataPresent(series, newvalue);
return true;
}
bool
SetDataPresentCommand::undoCommand()
{
ride->setDataPresent(series, oldvalue);
return true;
}