Files
sethLabels/glabels/UndoRedoModel.cpp
T
Jim Evins b797d13e40 Reconcile style accross all source files.
- All glabels code is in "glabels" top-level namespace.
- Other assorted cleanup.
2017-01-15 22:58:53 -05:00

264 lines
4.2 KiB
C++

/* UndoRedoModel.cpp
*
* Copyright (C) 2016 Jim Evins <evins@snaught.com>
*
* This file is part of gLabels-qt.
*
* gLabels-qt 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 3 of the License, or
* (at your option) any later version.
*
* gLabels-qt 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 gLabels-qt. If not, see <http://www.gnu.org/licenses/>.
*/
#include "UndoRedoModel.h"
#include "LabelModel.h"
namespace glabels
{
///
/// Constructor
///
UndoRedoModel::UndoRedoModel( LabelModel* model )
{
mModel = model;
mNewSelection = true;
connect( model, SIGNAL(selectionChanged()), this, SLOT(onSelectionChanged()) );
}
///
/// Destructor
///
UndoRedoModel::~UndoRedoModel()
{
// empty
}
///
/// Checkpoint
///
void UndoRedoModel::checkpoint( const QString& description )
{
//
// Do not perform consecutive checkpoints that are identical.
// E.g. moving an object by dragging, would produce a large number
// of incremental checkpoints -- what we really want is a single
// checkpoint so that we can undo the entire dragging effort with
// one "undo"
//
if ( mNewSelection || (description != mLastDescription) )
{
/* Sever old redo "thread" */
mRedoStack.clear();
/* Save state onto undo stack. */
State* stateNow = new State( mModel, description );
mUndoStack.push( stateNow );
/* Track consecutive checkpoints. */
mNewSelection = false;
mLastDescription = description;
emit changed();
}
}
///
/// Undo
///
void UndoRedoModel::undo()
{
State* oldState = mUndoStack.pop();
State* stateNow = new State( mModel, oldState->description );
mRedoStack.push( stateNow );
mModel->restore( oldState->model );
delete oldState;
mNewSelection = true;
emit changed();
}
///
/// Redo
///
void UndoRedoModel::redo()
{
State* oldState = mRedoStack.pop();
State* stateNow = new State( mModel, oldState->description );
mUndoStack.push( stateNow );
mModel->restore( oldState->model );
delete oldState;
mNewSelection = true;
emit changed();
}
///
/// Can we undo?
///
bool UndoRedoModel::canUndo() const
{
return !mUndoStack.isEmpty();
}
///
/// Can we redo?
///
bool UndoRedoModel::canRedo() const
{
return !mRedoStack.isEmpty();
}
///
/// Undo description
///
QString UndoRedoModel::undoDescription() const
{
if ( canUndo() )
{
return mUndoStack.topState()->description;
}
else
{
return "";
}
}
///
/// Redo description
///
QString UndoRedoModel::redoDescription() const
{
if ( canRedo() )
{
return mRedoStack.topState()->description;
}
else
{
return "";
}
}
///
/// Selection changed handler
///
void UndoRedoModel::onSelectionChanged()
{
mNewSelection = true;
}
///
/// State constructor
///
UndoRedoModel::State::State( LabelModel* model, const QString& description )
{
this->model = model->save();
this->description = description;
}
///
/// State destructor
///
UndoRedoModel::State::~State()
{
delete model;
}
///
/// Stack constructor
///
UndoRedoModel::Stack::Stack()
{
// empty
}
///
/// Stack destructor
///
UndoRedoModel::Stack::~Stack()
{
clear();
}
///
/// Push state onto stack
///
void UndoRedoModel::Stack::push( UndoRedoModel::State* state )
{
list.push_front( state );
}
///
/// Pop state from stack
///
UndoRedoModel::State* UndoRedoModel::Stack::pop()
{
return list.takeFirst();
}
///
/// Peek at state at top of stack
///
const UndoRedoModel::State* UndoRedoModel::Stack::topState() const
{
return list.first();
}
///
/// Is stack empty?
///
bool UndoRedoModel::Stack::isEmpty() const
{
return list.isEmpty();
}
///
/// Clear stack
///
void UndoRedoModel::Stack::clear()
{
while ( !isEmpty() )
{
delete pop();
}
}
}